diff --git a/benchmarks/bench_translations.py b/benchmarks/bench_translations.py
index 621ac275d9eb7fcf05763cf5c62d3436dda15e6b..ed97ed5456d00fcdc99daeb8e14841db8155b4dd 100644
--- a/benchmarks/bench_translations.py
+++ b/benchmarks/bench_translations.py
@@ -17,7 +17,6 @@ from sumpy.kernel import LaplaceKernel, HelmholtzKernel
 import logging
 logger = logging.getLogger(__name__)
 
-import six
 import pymbolic.mapper.flop_counter
 
 import sumpy.symbolic as sym
@@ -31,7 +30,7 @@ class Param:
         self.order = order
 
     def __repr__(self):
-        return "{}D_order_{}".format(self.dim, self.order)
+        return f"{self.dim}D_order_{self.order}"
 
 
 class TranslationBenchmarkSuite:
@@ -44,7 +43,7 @@ class TranslationBenchmarkSuite:
         Param(3, 10),
     ]
 
-    param_names = ['order']
+    param_names = ["order"]
 
     def setup(self, param):
         logging.basicConfig(level=logging.INFO)
@@ -71,7 +70,7 @@ class TranslationBenchmarkSuite:
         for i, expr in enumerate(result):
             sac.assign_unique("coeff%d" % i, expr)
         sac.run_global_cse()
-        insns = to_loopy_insns(six.iteritems(sac.assignments))
+        insns = to_loopy_insns(sac.assignments.items())
         counter = pymbolic.mapper.flop_counter.CSEAwareFlopCounter()
 
         return sum([counter.rec(insn.expression)+1 for insn in insns])
diff --git a/examples/curve-pot.py b/examples/curve-pot.py
index 72389fb4276f8acfadf6a765ea5692d4bd4d2dd4..65e273d8e31504a1eb116234f00845e071694d71 100644
--- a/examples/curve-pot.py
+++ b/examples/curve-pot.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import pyopencl as cl
 import numpy as np
 import numpy.linalg as la
diff --git a/examples/curve.py b/examples/curve.py
index 17327c3d5a365ef6bde5b7c8cebf1fe685a767d6..589ad16e7c83c7a4839340f4544e07b22c49df59 100644
--- a/examples/curve.py
+++ b/examples/curve.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import numpy as np
 
 import scipy as sp
diff --git a/examples/fourier.py b/examples/fourier.py
index e804222573dcb2655067ef42524185d6cbcf221b..24fd6aa747c92b0f7b1f8cb10af36d9cbf70f4bf 100644
--- a/examples/fourier.py
+++ b/examples/fourier.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import numpy as np
 
 
diff --git a/setup.py b/setup.py
index ceb4d7e2c26e86ff6127c7b157cd2e74946c60a7..5630d7b8ae7e865d56bbfcb0c53631faf8d6bf46 100644
--- a/setup.py
+++ b/setup.py
@@ -97,7 +97,6 @@ setup(name="sumpy",
           "loo.py>=2017.2",
           "boxtree>=2018.1",
           "pytest>=2.3",
-          "six",
           "pyrsistent>=0.16.0",
 
           # If this causes issues, see:
diff --git a/sumpy/__init__.py b/sumpy/__init__.py
index fd0fc5154e04882ef45a0d6f8e282362e6f39f27..6555fd2b9b3899ea36bc35ff811442b8b79ef7be 100644
--- a/sumpy/__init__.py
+++ b/sumpy/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
 
 __license__ = """
@@ -72,7 +70,7 @@ def set_caching_enabled(flag):
     CACHING_ENABLED = flag
 
 
-class CacheMode(object):
+class CacheMode:
     """A context manager for setting whether :mod:`sumpy` is allowed to use
     disk caches.
     """
diff --git a/sumpy/assignment_collection.py b/sumpy/assignment_collection.py
index dadf3fc954e890c2ecc53716feb4857187a14d2b..9078c7aad409589ecb65dc8e713845ddd012c763 100644
--- a/sumpy/assignment_collection.py
+++ b/sumpy/assignment_collection.py
@@ -1,8 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
-import six
-from six.moves import zip
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -41,7 +36,7 @@ Manipulating batches of assignments
 """
 
 
-class _SymbolGenerator(object):
+class _SymbolGenerator:
 
     def __init__(self, taken_symbols):
         self.taken_symbols = taken_symbols
@@ -83,7 +78,7 @@ class _SymbolGenerator(object):
 
 # {{{ collection of assignments
 
-class SymbolicAssignmentCollection(object):
+class SymbolicAssignmentCollection:
     """Represents a collection of assignments::
 
         a = 5*x
@@ -117,8 +112,8 @@ class SymbolicAssignmentCollection(object):
 
     def __str__(self):
         return "\n".join(
-            "%s <- %s" % (name, expr)
-            for name, expr in six.iteritems(self.assignments))
+            f"{name} <- {expr}"
+            for name, expr in self.assignments.items())
 
     def get_all_dependencies(self, var_name):
         """Including recursive dependencies."""
@@ -145,7 +140,7 @@ class SymbolicAssignmentCollection(object):
         return result
 
     def add_assignment(self, name, expr, root_name=None, wrt_set=None):
-        assert isinstance(name, six.string_types)
+        assert isinstance(name, str)
         assert name not in self.assignments
 
         if wrt_set is None:
diff --git a/sumpy/codegen.py b/sumpy/codegen.py
index 085f9bd459f08b6bcb1ae19dff227c80970ab8cf..b5d08088653509a8afc37b80da3959b4c14257b5 100644
--- a/sumpy/codegen.py
+++ b/sumpy/codegen.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -22,14 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from six.moves import range
 
 import numpy as np
 import pyopencl as cl
 import pyopencl.tools  # noqa
 import loopy as lp
 
-import six
 import re
 
 from pymbolic.mapper import IdentityMapper, WalkMapper, CSECachingMapperMixin
@@ -81,15 +77,15 @@ class SympyToPymbolicMapper(SympyToPymbolicMapperBase):
 
 def make_one_step_subst(assignments):
     assignments = dict(assignments)
-    unwanted_vars = set(six.iterkeys(assignments))
+    unwanted_vars = set(assignments.keys())
 
     # Ensure no re-assignments.
     assert len(unwanted_vars) == len(assignments)
 
     from loopy.symbolic import get_dependencies
-    unwanted_deps = dict(
-        (name, get_dependencies(value) & unwanted_vars)
-        for name, value in six.iteritems(assignments))
+    unwanted_deps = {
+        name: get_dependencies(value) & unwanted_vars
+        for name, value in assignments.items()}
 
     # {{{ compute substitution order
 
@@ -323,7 +319,7 @@ def bessel_mangler(kernel, identifier, arg_dtypes):
         return None
 
 
-class BesselGetter(object):
+class BesselGetter:
     def __init__(self, bessel_j_arg_to_top_order):
         self.bessel_j_arg_to_top_order = bessel_j_arg_to_top_order
         self.cse_cache = {}
diff --git a/sumpy/cse.py b/sumpy/cse.py
index 823ef54667ff6c1b8895eab3d59e9b3bd5316f7a..0da0eadc7f1c0afb4c29dafe1569f19b5c36000d 100644
--- a/sumpy/cse.py
+++ b/sumpy/cse.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division
-
 __copyright__ = """
 Copyright (C) 2017 Matt Wala
 Copyright (C) 2006-2016 SymPy Development Team
@@ -127,7 +125,7 @@ def postprocess_for_cse(expr, optimizations):
 
 # {{{ opt cse
 
-class FuncArgTracker(object):
+class FuncArgTracker:
     """
     A class which manages a mapping from functions to arguments and an inverse
     mapping from arguments to functions.
@@ -215,7 +213,7 @@ class FuncArgTracker(object):
             if func_i in larger_funcs_container:
                 count_map[func_i] += 1
 
-        return dict((k, v) for k, v in count_map.items() if v >= 2)
+        return {k: v for k, v in count_map.items() if v >= 2}
 
     def get_subset_candidates(self, argset, restrict_to_funcset=None):
         """
@@ -225,8 +223,8 @@ class FuncArgTracker(object):
         """
         iarg = iter(argset)
 
-        indices = set(
-            fi for fi in self.arg_to_funcset[next(iarg)])
+        indices = {
+            fi for fi in self.arg_to_funcset[next(iarg)]}
 
         if restrict_to_funcset is not None:
             indices &= restrict_to_funcset
@@ -252,7 +250,7 @@ class FuncArgTracker(object):
         self.func_to_argset[func_i].update(new_args)
 
 
-class Unevaluated(object):
+class Unevaluated:
 
     def __init__(self, func, args):
         self.func = func
@@ -325,7 +323,7 @@ def match_common_args(func_class, funcs, opt_subs):
                 com_func = Unevaluated(
                         func_class, arg_tracker.get_args_in_value_order(com_args))
                 com_func_number = arg_tracker.get_or_add_value_number(com_func)
-                arg_tracker.update_func_argset(i, diff_i | set([com_func_number]))
+                arg_tracker.update_func_argset(i, diff_i | {com_func_number})
                 changed.add(i)
             else:
                 # Treat the whole expression as a CSE.
@@ -340,13 +338,13 @@ def match_common_args(func_class, funcs, opt_subs):
                 com_func_number = arg_tracker.get_or_add_value_number(funcs[i])
 
             diff_j = arg_tracker.func_to_argset[j].difference(com_args)
-            arg_tracker.update_func_argset(j, diff_j | set([com_func_number]))
+            arg_tracker.update_func_argset(j, diff_j | {com_func_number})
             changed.add(j)
 
             for k in arg_tracker.get_subset_candidates(
                     com_args, common_arg_candidates):
                 diff_k = arg_tracker.func_to_argset[k].difference(com_args)
-                arg_tracker.update_func_argset(k, diff_k | set([com_func_number]))
+                arg_tracker.update_func_argset(k, diff_k | {com_func_number})
                 changed.add(k)
 
         if i in changed:
diff --git a/sumpy/e2e.py b/sumpy/e2e.py
index c0994444eee52fd94190f4a86d2d6f66aa469980..fdc937ee382eaa2c8fef0da90cb278b6a691401b 100644
--- a/sumpy/e2e.py
+++ b/sumpy/e2e.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
 
 __license__ = """
@@ -22,9 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-import six
-from six.moves import range
-
 import numpy as np
 import loopy as lp
 import sumpy.symbolic as sym
@@ -117,8 +112,8 @@ class E2EBase(KernelCacheWrapper):
 
         from sumpy.codegen import to_loopy_insns
         return to_loopy_insns(
-                six.iteritems(sac.assignments),
-                vector_names=set(["d"]),
+                sac.assignments.items(),
+                vector_names={"d"},
                 pymbolic_expr_maps=[self.tgt_expansion.get_code_transformer()],
                 retain_names=tgt_coeff_names,
                 complex_dtype=np.complex128  # FIXME
diff --git a/sumpy/e2p.py b/sumpy/e2p.py
index 408b736408d9247db72d56da75e27e0b7dd6c857..964dccb1fb37ef9d29203b4902f88bb87bd0523b 100644
--- a/sumpy/e2p.py
+++ b/sumpy/e2p.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
 
 __license__ = """
@@ -22,9 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-import six
-from six.moves import range
-
 import numpy as np
 import loopy as lp
 import sumpy.symbolic as sym
@@ -103,8 +98,8 @@ class E2PBase(KernelCacheWrapper):
 
         from sumpy.codegen import to_loopy_insns
         loopy_insns = to_loopy_insns(
-                six.iteritems(sac.assignments),
-                vector_names=set(["b"]),
+                sac.assignments.items(),
+                vector_names={"b"},
                 pymbolic_expr_maps=[self.expansion.get_code_transformer()],
                 retain_names=result_names,
                 complex_dtype=np.complex128  # FIXME
diff --git a/sumpy/expansion/__init__.py b/sumpy/expansion/__init__.py
index 719801afcace0923a716aa9136fe152092001bd9..f1d0e20e70fa19776e867aec2eb6fa53ea8f7656 100644
--- a/sumpy/expansion/__init__.py
+++ b/sumpy/expansion/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -45,7 +43,7 @@ logger = logging.getLogger(__name__)
 
 # {{{ base class
 
-class ExpansionBase(object):
+class ExpansionBase:
     """
     .. automethod:: with_kernel
     .. automethod:: __len__
@@ -159,7 +157,7 @@ class ExpansionBase(object):
 
 # {{{ expansion terms wrangler
 
-class ExpansionTermsWrangler(object):
+class ExpansionTermsWrangler:
 
     init_arg_names = ("order", "dim", "max_mi")
 
@@ -197,9 +195,9 @@ class ExpansionTermsWrangler(object):
             all(mi[i] <= self.max_mi[i] for i in range(self.dim))]
 
     def copy(self, **kwargs):
-        new_kwargs = dict(
-                (name, getattr(self, name))
-                for name in self.init_arg_names)
+        new_kwargs = {
+                name: getattr(self, name)
+                for name in self.init_arg_names}
 
         for name in self.init_arg_names:
             new_kwargs[name] = kwargs.pop(name, getattr(self, name))
@@ -226,7 +224,7 @@ class FullExpansionTermsWrangler(ExpansionTermsWrangler):
 
 # {{{ sparse matrix-vector multiplication
 
-class CSEMatVecOperator(object):
+class CSEMatVecOperator:
     """
     A class to facilitate a fast matrix vector multiplication with
     common subexpression eliminated. In compressed Taylor
@@ -312,7 +310,7 @@ class LinearPDEBasedExpansionTermsWrangler(ExpansionTermsWrangler):
         :param order: order of the expansion
         :param dim: number of dimensions
         """
-        super(LinearPDEBasedExpansionTermsWrangler, self).__init__(order, dim,
+        super().__init__(order, dim,
                 max_mi)
 
     def get_coefficient_identifiers(self):
@@ -357,8 +355,6 @@ class LinearPDEBasedExpansionTermsWrangler(ExpansionTermsWrangler):
 
     @memoize_method
     def get_stored_ids_and_unscaled_projection_matrix(self):
-        from six import iteritems
-
         from pytools import ProcessLogger
         plog = ProcessLogger(logger, "compute PDE for Taylor coefficients")
 
@@ -368,7 +364,7 @@ class LinearPDEBasedExpansionTermsWrangler(ExpansionTermsWrangler):
 
         diff_op = self.get_pde_as_diff_op()
         assert len(diff_op.eqs) == 1
-        pde_dict = dict((k.mi, v) for k, v in diff_op.eqs[0].items())
+        pde_dict = {k.mi: v for k, v in diff_op.eqs[0].items()}
         for ident in pde_dict.keys():
             if ident not in coeff_ident_enumerate_dict:
                 # Order of the expansion is less than the order of the PDE.
@@ -413,7 +409,7 @@ class LinearPDEBasedExpansionTermsWrangler(ExpansionTermsWrangler):
             # eg: u_xx + u_yy + u_zz is represented as
             # [((2, 0, 0), 1), ((0, 2, 0), 1), ((0, 0, 2), 1)]
             assignment = []
-            for other_mi, coeff in iteritems(pde_dict):
+            for other_mi, coeff in pde_dict.items():
                 j = coeff_ident_enumerate_dict[add_mi(other_mi, diff)]
                 if i == j:
                     # Skip the u_zz part here.
@@ -488,7 +484,7 @@ class LaplaceExpansionTermsWrangler(LinearPDEBasedExpansionTermsWrangler):
     init_arg_names = ("order", "dim", "max_mi")
 
     def __init__(self, order, dim, max_mi=None):
-        super(LaplaceExpansionTermsWrangler, self).__init__(order=order, dim=dim,
+        super().__init__(order=order, dim=dim,
             max_mi=max_mi)
 
     def get_pde_as_diff_op(self):
@@ -502,7 +498,7 @@ class HelmholtzExpansionTermsWrangler(LinearPDEBasedExpansionTermsWrangler):
 
     def __init__(self, order, dim, helmholtz_k_name, max_mi=None):
         self.helmholtz_k_name = helmholtz_k_name
-        super(HelmholtzExpansionTermsWrangler, self).__init__(order=order, dim=dim,
+        super().__init__(order=order, dim=dim,
             max_mi=max_mi)
 
     def get_pde_as_diff_op(self, **kwargs):
@@ -516,7 +512,7 @@ class BiharmonicExpansionTermsWrangler(LinearPDEBasedExpansionTermsWrangler):
     init_arg_names = ("order", "dim", "max_mi")
 
     def __init__(self, order, dim, max_mi=None):
-        super(BiharmonicExpansionTermsWrangler, self).__init__(order=order, dim=dim,
+        super().__init__(order=order, dim=dim,
             max_mi=max_mi)
 
     def get_pde_as_diff_op(self, **kwargs):
@@ -527,7 +523,7 @@ class BiharmonicExpansionTermsWrangler(LinearPDEBasedExpansionTermsWrangler):
 
 # {{{ volume taylor
 
-class VolumeTaylorExpansionBase(object):
+class VolumeTaylorExpansionBase:
 
     @classmethod
     def get_or_make_expansion_terms_wrangler(cls, *key):
@@ -561,8 +557,8 @@ class VolumeTaylorExpansionBase(object):
     @property
     @memoize_method
     def _storage_loc_dict(self):
-        return dict((i, idx) for idx, i in
-                    enumerate(self.get_coefficient_identifiers()))
+        return {i: idx for idx, i in
+                    enumerate(self.get_coefficient_identifiers())}
 
     def get_storage_index(self, i):
         return self._storage_loc_dict[i]
@@ -613,7 +609,7 @@ class BiharmonicConformingVolumeTaylorExpansion(VolumeTaylorExpansionBase):
 
 # {{{ expansion factory
 
-class ExpansionFactoryBase(object):
+class ExpansionFactoryBase:
     """An interface
     .. automethod:: get_local_expansion_class
     .. automethod:: get_multipole_expansion_class
diff --git a/sumpy/expansion/diff_op.py b/sumpy/expansion/diff_op.py
index 15cb3a4669dab6850696e93ee271eade376c5890..9cfbb496d636776347b51c0d865d9f22411d43e5 100644
--- a/sumpy/expansion/diff_op.py
+++ b/sumpy/expansion/diff_op.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2019 Isuru Fernando"
 
 __license__ = """
@@ -38,7 +36,7 @@ Differential operator interface
 DerivativeIdentifier = namedtuple("DerivativeIdentifier", ["mi", "vec_idx"])
 
 
-class LinearPDESystemOperator(object):
+class LinearPDESystemOperator:
     r"""
     Represents a constant-coefficient linear differential operator of a
     vector-valued function with `dim` variables. It is represented by a tuple of
diff --git a/sumpy/expansion/level_to_order.py b/sumpy/expansion/level_to_order.py
index 5e29b86138ec705ef57747929dead3403160b17a..303462dd1f0a64a07117b2a72ed21017017b6753 100644
--- a/sumpy/expansion/level_to_order.py
+++ b/sumpy/expansion/level_to_order.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2016 Matt Wala"
 
 __license__ = """
@@ -30,7 +28,7 @@ __doc__ = """
 import numpy as np
 
 
-class FMMLibExpansionOrderFinder(object):
+class FMMLibExpansionOrderFinder:
     r"""Return expansion orders that meet the tolerance for a given level
     using routines wrapped from :mod:`pyfmmlib`.
     """
@@ -80,7 +78,7 @@ class FMMLibExpansionOrderFinder(object):
         return nterms + self.extra_order
 
 
-class SimpleExpansionOrderFinder(object):
+class SimpleExpansionOrderFinder:
     r"""
     This models the Laplace truncation error as:
 
diff --git a/sumpy/expansion/local.py b/sumpy/expansion/local.py
index f5822332694113bbd722bf1dddecaab9a607bb04..83dc6b30ddb9c3237c30ede4059455992eb7b271 100644
--- a/sumpy/expansion/local.py
+++ b/sumpy/expansion/local.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -22,7 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from six.moves import range, zip
 import sumpy.symbolic as sym
 
 from sumpy.expansion import (
@@ -134,8 +131,8 @@ class VolumeTaylorLocalExpansionBase(LocalExpansionBase):
                 coeffs, rscale, sac=sac))
 
         bvec = [b*rscale**-1 for b in bvec]
-        mi_to_index = dict((mi, i) for i, mi in
-                        enumerate(self.get_full_coefficient_identifiers()))
+        mi_to_index = {mi: i for i, mi in
+                        enumerate(self.get_full_coefficient_identifiers())}
 
         # Sort multi-indices so that last dimension varies fastest
         sorted_target_mis = sorted(self.get_full_coefficient_identifiers())
@@ -314,7 +311,7 @@ class VolumeTaylorLocalExpansionBase(LocalExpansionBase):
             dvec_scaled = [d*src_rscale for d in dvec]
             expr = src_expansion.evaluate(src_coeff_exprs, dvec_scaled,
                         rscale=src_rscale, sac=sac)
-            replace_dict = dict((d, d/src_rscale) for d in dvec)
+            replace_dict = {d: d/src_rscale for d in dvec}
             taker = MiDerivativeTaker(expr, dvec)
             rscale_ratio = sym.UnevaluatedExpr(tgt_rscale/src_rscale)
             result = [
@@ -464,7 +461,7 @@ class H2DLocalExpansion(_FourierBesselLocalExpansion):
         assert (isinstance(kernel.get_base_kernel(), HelmholtzKernel)
                 and kernel.dim == 2)
 
-        super(H2DLocalExpansion, self).__init__(kernel, order, use_rscale)
+        super().__init__(kernel, order, use_rscale)
 
         from sumpy.expansion.multipole import H2DMultipoleExpansion
         self.mpole_expn_class = H2DMultipoleExpansion
@@ -479,7 +476,7 @@ class Y2DLocalExpansion(_FourierBesselLocalExpansion):
         assert (isinstance(kernel.get_base_kernel(), YukawaKernel)
                 and kernel.dim == 2)
 
-        super(Y2DLocalExpansion, self).__init__(kernel, order, use_rscale)
+        super().__init__(kernel, order, use_rscale)
 
         from sumpy.expansion.multipole import Y2DMultipoleExpansion
         self.mpole_expn_class = Y2DMultipoleExpansion
diff --git a/sumpy/expansion/multipole.py b/sumpy/expansion/multipole.py
index 7ad993733c9bfaee0d05d726b8d005e62409f85b..313b49bdffce8a3027e64904dee4426545c4b013 100644
--- a/sumpy/expansion/multipole.py
+++ b/sumpy/expansion/multipole.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -22,7 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from six.moves import range, zip
 import sumpy.symbolic as sym  # noqa
 
 from sumpy.symbolic import vector_xreplace
@@ -163,11 +160,11 @@ class VolumeTaylorMultipoleExpansionBase(MultipoleExpansionBase):
 
         from sumpy.tools import mi_factorial
 
-        src_mi_to_index = dict((mi, i) for i, mi in enumerate(
-            src_expansion.get_coefficient_identifiers()))
+        src_mi_to_index = {mi: i for i, mi in enumerate(
+            src_expansion.get_coefficient_identifiers())}
 
-        tgt_mi_to_index = dict((mi, i) for i, mi in enumerate(
-            self.get_full_coefficient_identifiers()))
+        tgt_mi_to_index = {mi: i for i, mi in enumerate(
+            self.get_full_coefficient_identifiers())}
 
         src_coeff_exprs = list(src_coeff_exprs)
         for i, mi in enumerate(src_expansion.get_coefficient_identifiers()):
@@ -361,7 +358,7 @@ class H2DMultipoleExpansion(_HankelBased2DMultipoleExpansion):
         assert (isinstance(kernel.get_base_kernel(), HelmholtzKernel)
                 and kernel.dim == 2)
 
-        super(H2DMultipoleExpansion, self).__init__(
+        super().__init__(
                 kernel, order, use_rscale=use_rscale)
 
     def get_bessel_arg_scaling(self):
@@ -374,7 +371,7 @@ class Y2DMultipoleExpansion(_HankelBased2DMultipoleExpansion):
         assert (isinstance(kernel.get_base_kernel(), YukawaKernel)
                 and kernel.dim == 2)
 
-        super(Y2DMultipoleExpansion, self).__init__(
+        super().__init__(
                 kernel, order, use_rscale=use_rscale)
 
     def get_bessel_arg_scaling(self):
diff --git a/sumpy/fmm.py b/sumpy/fmm.py
index 668734fdc8a433a65b3b80b722f825b2aa2f743b..39ed7f2093d44a32c24a5265cfb0ba844de2e98d 100644
--- a/sumpy/fmm.py
+++ b/sumpy/fmm.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
 
 __license__ = """
@@ -29,8 +27,6 @@ __doc__ = """Integrates :mod:`boxtree` with :mod:`sumpy`.
 """
 
 
-from six.moves import zip
-
 import pyopencl as cl
 import pyopencl.array  # noqa
 
@@ -49,7 +45,7 @@ def level_to_rscale(tree, level):
 
 # {{{ expansion wrangler code container
 
-class SumpyExpansionWranglerCodeContainer(object):
+class SumpyExpansionWranglerCodeContainer:
     """Objects of this type serve as a place to keep the code needed
     for :class:`SumpyExpansionWrangler`. Since :class:`SumpyExpansionWrangler`
     necessarily must have a :class:`pyopencl.CommandQueue`, but this queue
@@ -158,7 +154,7 @@ class UnableToCollectTimingData(UserWarning):
     pass
 
 
-class SumpyTimingFuture(object):
+class SumpyTimingFuture:
 
     def __init__(self, queue, events):
         self.queue = queue
@@ -199,7 +195,7 @@ class SumpyTimingFuture(object):
 
 # {{{ expansion wrangler
 
-class SumpyExpansionWrangler(object):
+class SumpyExpansionWrangler:
     """Implements the :class:`boxtree.fmm.ExpansionWranglerInterface`
     by using :mod:`sumpy` expansions/translations.
 
diff --git a/sumpy/kernel.py b/sumpy/kernel.py
index bb96f598fc5ddc60dc6079064b5987c64075d79c..9beaff6c5b2a520fbc5a2ceceb32f10a5a7ab713 100644
--- a/sumpy/kernel.py
+++ b/sumpy/kernel.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -22,7 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from six.moves import range, zip
 
 import loopy as lp
 import numpy as np
@@ -77,7 +74,7 @@ Transforming kernels
 """
 
 
-class KernelArgument(object):
+class KernelArgument:
     """
     .. attribute:: loopy_arg
 
@@ -112,7 +109,7 @@ class KernelArgument(object):
 
 # {{{ basic kernel interface
 
-class Kernel(object):
+class Kernel:
     """Basic kernel interface.
 
     .. attribute:: is_complex_valued
@@ -355,10 +352,10 @@ class ExpressionKernel(Kernel):
             raise ValueError("dist_vec length does not match expected dimension")
 
         from sumpy.symbolic import Symbol
-        expr = expr.xreplace(dict(
-            (Symbol("d%d" % i), dist_vec_i)
+        expr = expr.xreplace({
+            Symbol("d%d" % i): dist_vec_i
             for i, dist_vec_i in enumerate(scaled_dist_vec)
-            ))
+            })
 
         return expr
 
@@ -412,7 +409,7 @@ class LaplaceKernel(ExpressionKernel):
         else:
             raise NotImplementedError("unsupported dimensionality")
 
-        super(LaplaceKernel, self).__init__(
+        super().__init__(
                 dim,
                 expression=expr,
                 global_scaling_const=scaling,
@@ -464,7 +461,7 @@ class BiharmonicKernel(ExpressionKernel):
         else:
             raise RuntimeError("unsupported dimensionality")
 
-        super(BiharmonicKernel, self).__init__(
+        super().__init__(
                 dim,
                 expression=expr,
                 global_scaling_const=scaling,
@@ -504,7 +501,7 @@ class HelmholtzKernel(ExpressionKernel):
         else:
             raise RuntimeError("unsupported dimensionality")
 
-        super(HelmholtzKernel, self).__init__(
+        super().__init__(
                 dim,
                 expression=expr,
                 global_scaling_const=scaling,
@@ -586,7 +583,7 @@ class YukawaKernel(ExpressionKernel):
         else:
             raise RuntimeError("unsupported dimensionality")
 
-        super(YukawaKernel, self).__init__(
+        super().__init__(
                 dim,
                 expression=expr,
                 global_scaling_const=scaling,
@@ -664,7 +661,7 @@ class StokesletKernel(ExpressionKernel):
         self.icomp = icomp
         self.jcomp = jcomp
 
-        super(StokesletKernel, self).__init__(
+        super().__init__(
                 dim,
                 expression=expr,
                 global_scaling_const=scaling,
@@ -729,7 +726,7 @@ class StressletKernel(ExpressionKernel):
         self.jcomp = jcomp
         self.kcomp = kcomp
 
-        super(StressletKernel, self).__init__(
+        super().__init__(
                 dim,
                 expression=expr,
                 global_scaling_const=scaling,
@@ -882,11 +879,11 @@ class DirectionalDerivative(DerivativeBase):
         key_builder.rec(key_hash, self.dir_vec_name)
 
     def __str__(self):
-        return r"%s . \/_%s %s" % (
+        return r"{} . \/_{} {}".format(
                 self.dir_vec_name, self.directional_kind[0], self.inner_kernel)
 
     def __repr__(self):
-        return "%s(%r, %s)" % (
+        return "{}({!r}, {})".format(
                 type(self).__name__,
                 self.inner_kernel,
                 self.dir_vec_name)
@@ -979,12 +976,12 @@ class DirectionalSourceDerivative(DirectionalDerivative):
 
 # {{{ kernel mappers
 
-class KernelMapper(object):
+class KernelMapper:
     def rec(self, kernel):
         try:
             method = getattr(self, kernel.mapper_method)
         except AttributeError:
-            raise RuntimeError("%s cannot handle %s" % (
+            raise RuntimeError("{} cannot handle {}".format(
                 type(self), type(kernel)))
         else:
             return method(kernel)
diff --git a/sumpy/p2e.py b/sumpy/p2e.py
index f5472fc42e306c21eeec5091b26d548cdb66c71a..58c38321d0270382ad58111ccf67421c1abc4ba8 100644
--- a/sumpy/p2e.py
+++ b/sumpy/p2e.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
 
 __license__ = """
@@ -22,9 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-import six
-from six.moves import range
-
 import numpy as np
 import loopy as lp
 from loopy.version import MOST_RECENT_LANGUAGE_VERSION
@@ -94,8 +89,8 @@ class P2EBase(KernelCacheWrapper):
 
         from sumpy.codegen import to_loopy_insns
         return to_loopy_insns(
-                six.iteritems(sac.assignments),
-                vector_names=set(["a"]),
+                sac.assignments.items(),
+                vector_names={"a"},
                 pymbolic_expr_maps=[self.expansion.get_code_transformer()],
                 retain_names=coeff_names,
                 complex_dtype=np.complex128  # FIXME
diff --git a/sumpy/p2p.py b/sumpy/p2p.py
index c585d0eae7ec73de1231850b3b3be628ba9e3d4d..e1a8da6d43886c9585ff6a9d5b4f66a27df4e0b2 100644
--- a/sumpy/p2p.py
+++ b/sumpy/p2p.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = """
 Copyright (C) 2012 Andreas Kloeckner
 Copyright (C) 2018 Alexandru Fikl
@@ -25,9 +23,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-import six
-from six.moves import range
-
 import numpy as np
 import loopy as lp
 from loopy.version import MOST_RECENT_LANGUAGE_VERSION
@@ -100,8 +95,8 @@ class P2PBase(KernelComputation, KernelCacheWrapper):
         sac.run_global_cse()
 
         from sumpy.codegen import to_loopy_insns
-        loopy_insns = to_loopy_insns(six.iteritems(sac.assignments),
-                vector_names=set(["d"]),
+        loopy_insns = to_loopy_insns(sac.assignments.items(),
+                vector_names={"d"},
                 pymbolic_expr_maps=[
                         knl.get_code_transformer() for knl in self.kernels],
                 retain_names=result_names,
diff --git a/sumpy/point_calculus.py b/sumpy/point_calculus.py
index 38a5f05b74f9afcf87094c806278747fa1f0998d..9d2642e04aecb72e5aa2bf3b420d900d57fe3793 100644
--- a/sumpy/point_calculus.py
+++ b/sumpy/point_calculus.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2017 Andreas Kloeckner"
 
 __license__ = """
@@ -33,7 +31,7 @@ __doc__ = """
 """
 
 
-class CalculusPatch(object):
+class CalculusPatch:
     """Sets up a grid of points on which derivatives can be calculated. Useful
     to verify that an evaluated potential actually solves a PDE.
 
@@ -188,7 +186,7 @@ class CalculusPatch(object):
         tgt_axes = (axes[:axis] + "i" + axes[axis:])[:dim]
 
         return np.einsum(
-                "ij,%s->%s" % (src_axes, tgt_axes),
+                f"ij,{src_axes}->{tgt_axes}",
                 self._diff_mat_1d(nderivs),
                 f_values.reshape(*self._pshape)).reshape(-1)
 
diff --git a/sumpy/qbx.py b/sumpy/qbx.py
index dd6a827928832aadf74d6108df0489950836cc59..7d539587d3d1d2a6725a71b334a5a8bd122b9d4a 100644
--- a/sumpy/qbx.py
+++ b/sumpy/qbx.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = """
 Copyright (C) 2012 Andreas Kloeckner
 Copyright (C) 2018 Alexandru Fikl
@@ -26,8 +24,6 @@ THE SOFTWARE.
 """
 
 
-import six
-from six.moves import range
 import numpy as np
 import loopy as lp
 from loopy.version import MOST_RECENT_LANGUAGE_VERSION
@@ -124,8 +120,8 @@ class LayerPotentialBase(KernelComputation, KernelCacheWrapper):
 
         from sumpy.codegen import to_loopy_insns
         loopy_insns = to_loopy_insns(
-                six.iteritems(sac.assignments),
-                vector_names=set(["a", "b"]),
+                sac.assignments.items(),
+                vector_names={"a", "b"},
                 pymbolic_expr_maps=[
                     expn.kernel.get_code_transformer() for expn in self.expansions],
                 retain_names=result_names,
@@ -534,7 +530,7 @@ def find_jump_term(kernel, arg_provider):
 
 # {{{ symbolic argument provider
 
-class _JumpTermSymbolicArgumentProvider(object):
+class _JumpTermSymbolicArgumentProvider:
     """This class answers requests by :func:`find_jump_term` for symbolic values
     of quantities needed for the computation of the jump terms and tracks what
     data was requested. This tracking allows assembling the argument list of the
diff --git a/sumpy/symbolic.py b/sumpy/symbolic.py
index f04293a55571c94a45cd6ffe7eda678f2b44e468..9948eb79c8128ebeee0867305853992410bddb91 100644
--- a/sumpy/symbolic.py
+++ b/sumpy/symbolic.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -23,9 +21,6 @@ THE SOFTWARE.
 """
 
 
-import six
-from six.moves import range
-
 import numpy as np
 from pymbolic.mapper import IdentityMapper as IdentityMapperBase
 import pymbolic.primitives as prim
@@ -131,11 +126,11 @@ class _DerivativeKiller(IdentityMapperBase):
 
 
 def _get_assignments_in_maxima(assignments, prefix=""):
-    my_variable_names = set(six.iterkeys(assignments))
+    my_variable_names = set(assignments.keys())
     written_assignments = set()
 
-    prefix_subst_dict = dict(
-            (vn, prefix+vn) for vn in my_variable_names)
+    prefix_subst_dict = {
+            vn: prefix+vn for vn in my_variable_names}
 
     from pymbolic.maxima import MaximaStringifyMapper
     mstr = MaximaStringifyMapper()
@@ -153,12 +148,12 @@ def _get_assignments_in_maxima(assignments, prefix=""):
             if symb.name not in written_assignments:
                 write_assignment(symb.name)
 
-        result.append("%s%s : %s;" % (
+        result.append("{}{} : {};".format(
             prefix, name, mstr(dkill(s2p(
                 assignments[name].subs(prefix_subst_dict))))))
         written_assignments.add(name)
 
-    for name in six.iterkeys(assignments):
+    for name in assignments.keys():
         if name not in written_assignments:
             write_assignment(name)
 
@@ -172,12 +167,12 @@ def checked_cse(exprs, symbols=None):
 
     new_assignments, new_exprs = sym.cse(exprs, **kwargs)
 
-    max_old = _get_assignments_in_maxima(dict(
-            ("old_expr%d" % i, expr)
-            for i, expr in enumerate(exprs)))
-    new_ass_dict = dict(
-            ("new_expr%d" % i, expr)
-            for i, expr in enumerate(new_exprs))
+    max_old = _get_assignments_in_maxima({
+            "old_expr%d" % i: expr
+            for i, expr in enumerate(exprs)})
+    new_ass_dict = {
+            "new_expr%d" % i: expr
+            for i, expr in enumerate(new_exprs)}
     for name, val in new_assignments:
         new_ass_dict[name.name] = val
     max_new = _get_assignments_in_maxima(new_ass_dict)
diff --git a/sumpy/tools.py b/sumpy/tools.py
index 6003e5c4d98aa08bc674d541f21d45718d777529..e97b339089edefe2c943c11ff3cb0939fcf941b7 100644
--- a/sumpy/tools.py
+++ b/sumpy/tools.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = """
 Copyright (C) 2012 Andreas Kloeckner
 Copyright (C) 2018 Alexandru Fikl
@@ -34,8 +32,6 @@ __doc__ = """
  .. autoclass:: MatrixBlockIndexRanges
 """
 
-import six
-from six.moves import range, zip
 from pytools import memoize_method, memoize_in
 import numpy as np
 import sumpy.symbolic as sym
@@ -76,7 +72,7 @@ def mi_power(vector, mi, evaluate=True):
     return result
 
 
-class MiDerivativeTaker(object):
+class MiDerivativeTaker:
 
     def __init__(self, expr, var_list):
         assert isinstance(expr, sym.Basic)
@@ -180,7 +176,7 @@ def gather_arguments(kernel_likes):
         for arg in knl.get_args():
             _merge_kernel_arguments(result, arg)
 
-    return sorted(six.itervalues(result), key=lambda arg: arg.name)
+    return sorted(result.values(), key=lambda arg: arg.name)
 
 
 def gather_source_arguments(kernel_likes):
@@ -189,7 +185,7 @@ def gather_source_arguments(kernel_likes):
         for arg in knl.get_args() + knl.get_source_args():
             _merge_kernel_arguments(result, arg)
 
-    return sorted(six.itervalues(result), key=lambda arg: arg.name)
+    return sorted(result.values(), key=lambda arg: arg.name)
 
 
 def gather_loopy_arguments(kernel_likes):
@@ -202,7 +198,7 @@ def gather_loopy_source_arguments(kernel_likes):
 
 # {{{  KernelComputation
 
-class KernelComputation(object):
+class KernelComputation:
     """Common input processing for kernel computations."""
 
     def __init__(self, ctx, kernels, strength_usage,
@@ -283,7 +279,7 @@ def _to_host(x, queue=None):
     return x
 
 
-class BlockIndexRanges(object):
+class BlockIndexRanges:
     """Convenience class for working with blocks of a global array.
 
     .. attribute:: indices
@@ -337,7 +333,7 @@ class BlockIndexRanges(object):
         return x[self.block_indices(i)]
 
 
-class MatrixBlockIndexRanges(object):
+class MatrixBlockIndexRanges:
     """Keep track of different ways to index into matrix blocks.
 
     .. attribute:: row
@@ -583,8 +579,8 @@ class OrderedSet(MutableSet):
 
     def __repr__(self):
         if not self:
-            return "%s()" % (self.__class__.__name__,)
-        return "%s(%r)" % (self.__class__.__name__, list(self))
+            return f"{self.__class__.__name__}()"
+        return "{}({!r})".format(self.__class__.__name__, list(self))
 
     def __eq__(self, other):
         if isinstance(other, OrderedSet):
@@ -594,7 +590,7 @@ class OrderedSet(MutableSet):
 # }}}
 
 
-class KernelCacheWrapper(object):
+class KernelCacheWrapper:
     @memoize_method
     def get_cached_optimized_kernel(self, **kwargs):
         from sumpy import code_cache, CACHING_ENABLED, OPT_ENABLED
@@ -604,14 +600,14 @@ class KernelCacheWrapper(object):
             from sumpy.version import KERNEL_VERSION
             cache_key = (
                     self.get_cache_key()
-                    + tuple(sorted(six.iteritems(kwargs)))
+                    + tuple(sorted(kwargs.items()))
                     + (loopy.version.DATA_MODEL_VERSION,)
                     + (KERNEL_VERSION,)
                     + (OPT_ENABLED,))
 
             try:
                 result = code_cache[cache_key]
-                logger.debug("%s: kernel cache hit [key=%s]" % (
+                logger.debug("{}: kernel cache hit [key={}]".format(
                     self.name, cache_key))
                 return result
             except KeyError:
@@ -619,7 +615,7 @@ class KernelCacheWrapper(object):
 
         logger.info("%s: kernel cache miss" % self.name)
         if CACHING_ENABLED:
-            logger.info("%s: kernel cache miss [key=%s]" % (
+            logger.info("{}: kernel cache miss [key={}]".format(
                 self.name, cache_key))
 
         from pytools import MinRecursionLimit
@@ -649,10 +645,9 @@ def my_syntactic_subs(expr, subst_dict):
     elif isinstance(expr, Subs):
         new_point = tuple(my_syntactic_subs(p, subst_dict) for p in expr.point)
 
-        import six
-        new_subst_dict = dict(
-            (var, subs) for var, subs in six.iteritems(subst_dict)
-            if var not in expr.variables)
+        new_subst_dict = {
+            var: subs for var, subs in subst_dict.items()
+            if var not in expr.variables}
 
         new_expr = my_syntactic_subs(expr.expr, new_subst_dict)
 
diff --git a/sumpy/toys.py b/sumpy/toys.py
index b824d9575aa105dc81ca531be868a5c19abbee7c..6fceebd562c9a3522cc88d7768590bd592ec7f3e 100644
--- a/sumpy/toys.py
+++ b/sumpy/toys.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = """
 Copyright (C) 2017 Andreas Kloeckner
 Copyright (C) 2017 Matt Wala
@@ -25,9 +23,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-import six  # noqa: F401
-from six.moves import range  # noqa: F401
-
 from pytools import memoize_method
 from numbers import Number
 from sumpy.kernel import TargetDerivativeRemover
@@ -82,7 +77,7 @@ by users:
 
 # {{{ context
 
-class ToyContext(object):
+class ToyContext:
     """This class functions as a container for generated code and 'behind-the-scenes'
     information.
 
@@ -290,7 +285,7 @@ def _e2e(psource, to_center, to_rscale, to_order, e2e, expn_class, expn_kwargs):
 
 # {{{ potential source classes
 
-class PotentialSource(object):
+class PotentialSource:
     """A base class for all classes representing potentials that can be
     evaluated anywhere in space.
 
@@ -349,7 +344,7 @@ class ConstantPotential(PotentialSource):
     """
 
     def __init__(self, toy_ctx, value):
-        super(ConstantPotential, self).__init__(toy_ctx)
+        super().__init__(toy_ctx)
         self.value = np.array(value)
 
     def eval(self, targets):
@@ -363,7 +358,7 @@ class OneOnBallPotential(PotentialSource):
     .. automethod:: __init__
     """
     def __init__(self, toy_ctx, center, radius):
-        super(OneOnBallPotential, self).__init__(toy_ctx)
+        super().__init__(toy_ctx)
         self.center = np.asarray(center)
         self.radius = radius
 
@@ -377,7 +372,7 @@ class HalfspaceOnePotential(PotentialSource):
     .. automethod:: __init__
     """
     def __init__(self, toy_ctx, center, axis, side=1):
-        super(HalfspaceOnePotential, self).__init__(toy_ctx)
+        super().__init__(toy_ctx)
         self.center = np.asarray(center)
         self.axis = axis
         self.side = side
@@ -398,7 +393,7 @@ class PointSources(PotentialSource):
     """
 
     def __init__(self, toy_ctx, points, weights, center=None):
-        super(PointSources, self).__init__(toy_ctx)
+        super().__init__(toy_ctx)
 
         self.points = points
         self.weights = weights
@@ -434,7 +429,7 @@ class ExpansionPotentialSource(PotentialSource):
     """
     def __init__(self, toy_ctx, center, rscale, order, coeffs, derived_from,
             radius=None, expn_style=None, text_kwargs=None):
-        super(ExpansionPotentialSource, self).__init__(toy_ctx)
+        super().__init__(toy_ctx)
         self.center = np.asarray(center)
         self.rscale = rscale
         self.order = order
@@ -464,7 +459,7 @@ class LocalExpansion(ExpansionPotentialSource):
 class PotentialExpressionNode(PotentialSource):
     def __init__(self, psources):
         from pytools import single_valued
-        super(PotentialExpressionNode, self).__init__(
+        super().__init__(
                 single_valued(psource.toy_ctx for psource in psources))
 
         self.psources = psources
@@ -665,7 +660,7 @@ def draw_annotation(to_pt, from_pt, label, arrowprops={}, **kwargs):
             arrowprops=my_arrowprops, **kwargs)
 
 
-class SchematicVisitor(object):
+class SchematicVisitor:
     def __init__(self, default_expn_style="circle"):
         self.default_expn_style = default_expn_style
 
@@ -707,7 +702,7 @@ class SchematicVisitor(object):
                 verticalalignment="center",
                 horizontalalignment="center")
 
-        label = "$%s_{%s}$" % (
+        label = "${}_{{{}}}$".format(
                 type(psource).__name__[0].lower().replace("l", "\\ell"),
                 psource.order)
 
diff --git a/sumpy/version.py b/sumpy/version.py
index ced3319c9da32decaffd911398df47c239e034b9..74ea3737e244516867c4c113293e65e186c3bcc6 100644
--- a/sumpy/version.py
+++ b/sumpy/version.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2014 Andreas Kloeckner"
 
 __license__ = """
diff --git a/sumpy/visualization.py b/sumpy/visualization.py
index d1eee517a9d99b1544f8e30c566e91debe9a72d8..a3d5c74824a5188aa57d0c37f2b23e38ab7c799d 100644
--- a/sumpy/visualization.py
+++ b/sumpy/visualization.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
@@ -29,7 +27,6 @@ __doc__ = """
 """
 
 import numpy as np
-from six.moves import range
 
 
 def separate_by_real_and_imag(data, real_only):
@@ -83,7 +80,7 @@ def make_field_plotter_from_bbox(bbox, h, extend_factor=0):
     return FieldPlotter(center, extent, npoints)
 
 
-class FieldPlotter(object):
+class FieldPlotter:
     """
     .. automethod:: set_matplotlib_limits
     .. automethod:: show_scalar_in_matplotlib
diff --git a/test/test_codegen.py b/test/test_codegen.py
index a3683c8fcc2ec71c4ba0840e395ca13f942d978d..369200001c80cd0e52a3061e5cd3a008980a2643 100644
--- a/test/test_codegen.py
+++ b/test/test_codegen.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2017 Matt Wala"
 
 __license__ = """
diff --git a/test/test_cse.py b/test/test_cse.py
index 69501b0f15b314b12b694b26c870ef10f87c6f1b..ba9986bed133a9155e78ee644a0a459d16e5862f 100644
--- a/test/test_cse.py
+++ b/test/test_cse.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division
-
 __copyright__ = """
 Copyright (C) 2017 Matt Wala
 Copyright (C) 2006-2016 SymPy Development Team
diff --git a/test/test_fmm.py b/test/test_fmm.py
index b429456628e35d997cb0cbe8f4eec4588e1b66fd..45c17c38fb42fa6aad21001fed896bb530491ff4 100644
--- a/test/test_fmm.py
+++ b/test/test_fmm.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
 
 __license__ = """
@@ -22,7 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from six.moves import range
 
 import sys
 import numpy as np
diff --git a/test/test_kernels.py b/test/test_kernels.py
index 2af6a86a06cf303723380e25e383e1c87fb13511..f8d5122d2ce5ecfce05b0fa85453328127781c6e 100644
--- a/test/test_kernels.py
+++ b/test/test_kernels.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
 __license__ = """
diff --git a/test/test_matrixgen.py b/test/test_matrixgen.py
index 9ebce95783c74d7b15b8062acfefe3bbc0bcf7ae..d56341d273aab24444cd028be98feb16f57183cd 100644
--- a/test/test_matrixgen.py
+++ b/test/test_matrixgen.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2018 Alexandru Fikl"
 
 __license__ = """
diff --git a/test/test_misc.py b/test/test_misc.py
index ff382ba3e96151686f3129eb15518ea02a291055..c7f83eba69c9dfdfab4dee150303e36cc2f59da9 100644
--- a/test/test_misc.py
+++ b/test/test_misc.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2017 Andreas Kloeckner"
 
 __license__ = """
diff --git a/test/test_qbx.py b/test/test_qbx.py
index 8b73224c0ec23dc8f9249e2baad5fd92afb2d638..f26f7bf873001d0a298a3765d0e59cdd8b40b4fa 100644
--- a/test/test_qbx.py
+++ b/test/test_qbx.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2017 Matt Wala"
 
 __license__ = """