From bdd18027cb449f2fb0cbeb3e0530569ef5fdb278 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Wed, 26 Nov 2014 14:48:51 -0600
Subject: [PATCH] Make pytools get by without 2to3

---
 pytools/__init__.py             | 109 +++++++++++++++++---------------
 pytools/arithmetic_container.py |  12 ++--
 pytools/batchjob.py             |   8 ++-
 pytools/convergence.py          |   5 +-
 pytools/datatable.py            |  18 ++++--
 pytools/debug.py                |  32 ++++++----
 pytools/decorator.py            |   7 +-
 pytools/diskdict.py             |  16 +++--
 pytools/lex.py                  |   7 +-
 pytools/log.py                  |  31 +++++----
 pytools/mpi.py                  |   1 +
 pytools/mpiwrap.py              |   1 +
 pytools/obj_array.py            |   1 +
 pytools/persistent_dict.py      |  20 +++---
 pytools/prefork.py              |  13 ++--
 pytools/spatial_btree.py        |   8 ++-
 pytools/stopwatch.py            |   8 ++-
 pytools/test.py                 |   1 +
 setup.py                        |  12 +---
 test/test_data_table.py         |   5 +-
 test/test_math_stuff.py         |   1 +
 test/test_persistent_dict.py    |   9 ++-
 test/test_pytools.py            |   1 +
 23 files changed, 187 insertions(+), 139 deletions(-)

diff --git a/pytools/__init__.py b/pytools/__init__.py
index 8bf62b7..85e34a5 100644
--- a/pytools/__init__.py
+++ b/pytools/__init__.py
@@ -1,4 +1,11 @@
 from __future__ import division
+from __future__ import absolute_import
+from __future__ import print_function
+import six
+from six.moves import range
+from six.moves import zip
+from functools import reduce
+from six.moves import input
 
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
@@ -61,7 +68,7 @@ def levi_civita(tup):
 def factorial(n):
     from operator import mul
     assert n == int(n)
-    return reduce(mul, (i for i in xrange(1, n+1)), 1)
+    return reduce(mul, (i for i in range(1, n+1)), 1)
 
 
 def perm(n, k):
@@ -133,7 +140,7 @@ class RecordWithoutPickling(object):
         if valuedict is not None:
             kwargs.update(valuedict)
 
-        for key, value in kwargs.iteritems():
+        for key, value in six.iteritems(kwargs):
             if not key in exclude:
                 fields.add(key)
                 setattr(self, key, value)
@@ -181,7 +188,7 @@ class Record(RecordWithoutPickling):
         except AttributeError:
             self.__class__.fields = fields = set()
 
-        for key, value in valuedict.iteritems():
+        for key, value in six.iteritems(valuedict):
             fields.add(key)
             setattr(self, key, value)
 
@@ -230,13 +237,13 @@ class DictionaryWithDefault(object):
         return True
 
     def iterkeys(self):
-        return self._Dictionary.iterkeys()
+        return six.iterkeys(self._Dictionary)
 
     def __iter__(self):
         return self._Dictionary.__iter__()
 
     def iteritems(self):
-        return self._Dictionary.iteritems()
+        return six.iteritems(self._Dictionary)
 
 # }}}
 
@@ -284,16 +291,16 @@ class DependentDictionary(object):
         self._Dictionary[key] = value
 
     def genuineKeys(self):
-        return self._Dictionary.keys()
+        return list(self._Dictionary.keys())
 
     def iteritems(self):
-        return self._Dictionary.iteritems()
+        return six.iteritems(self._Dictionary)
 
     def iterkeys(self):
-        return self._Dictionary.iterkeys()
+        return six.iterkeys(self._Dictionary)
 
     def itervalues(self):
-        return self._Dictionary.itervalues()
+        return six.itervalues(self._Dictionary)
 
 # }}}
 
@@ -308,13 +315,13 @@ def one(iterable):
     """
     it = iter(iterable)
     try:
-        v = it.next()
+        v = next(it)
     except StopIteration:
         raise ValueError("empty iterable passed to 'one()'")
 
     def no_more():
         try:
-            it.next()
+            next(it)
             raise ValueError("iterable with more than one entry passed to 'one()'")
         except StopIteration:
             return True
@@ -327,7 +334,7 @@ def one(iterable):
 def is_single_valued(iterable, equality_pred=operator.eq):
     it = iter(iterable)
     try:
-        first_item = it.next()
+        first_item = next(it)
     except StopIteration:
         raise ValueError("empty iterable passed to 'single_valued()'")
 
@@ -351,7 +358,7 @@ def single_valued(iterable, equality_pred=operator.eq):
     """
     it = iter(iterable)
     try:
-        first_item = it.next()
+        first_item = next(it)
     except StopIteration:
         raise ValueError("empty iterable passed to 'single_valued()'")
 
@@ -385,7 +392,7 @@ def memoize(*args, **kwargs):
 
     if use_kw:
         def default_key_func(*inner_args, **inner_kwargs):
-            return inner_args, frozenset(inner_kwargs.iteritems())
+            return inner_args, frozenset(six.iteritems(inner_kwargs))
     else:
         default_key_func = None
 
@@ -394,7 +401,7 @@ def memoize(*args, **kwargs):
     if kwargs:
         raise TypeError(
             "memoize received unexpected keyword arguments: %s"
-            % ", ".join(kwargs.keys()))
+            % ", ".join(list(kwargs.keys())))
 
     if key_func is not None:
         @my_decorator
@@ -454,7 +461,7 @@ def memoize_method(method):
 
     def wrapper(self, *args, **kwargs):
         if kwargs:
-            key = (_HasKwargs, frozenset(kwargs.iteritems())) + args
+            key = (_HasKwargs, frozenset(six.iteritems(kwargs))) + args
         else:
             key = args
 
@@ -495,7 +502,7 @@ def memoize_on_first_arg(function):
 
     def wrapper(obj, *args, **kwargs):
         if kwargs:
-            key = (_HasKwargs, frozenset(kwargs.iteritems())) + args
+            key = (_HasKwargs, frozenset(six.iteritems(kwargs))) + args
         else:
             key = args
 
@@ -549,7 +556,7 @@ def memoize_method_with_uncached(uncached_args=[], uncached_kwargs=set()):
                 for name in uncached_kwargs:
                     cache_kwargs.pop(name, None)
 
-                key = (_HasKwargs, frozenset(cache_kwargs.iteritems())) + cache_args
+                key = (_HasKwargs, frozenset(six.iteritems(cache_kwargs))) + cache_args
             else:
                 key = cache_args
 
@@ -587,8 +594,8 @@ def memoize_method_nested(inner):
 
     from functools import wraps
     cache_dict_name = intern("_memoize_inner_dic_%s_%s_%d"
-            % (inner.__name__, inner.func_code.co_filename,
-                inner.func_code.co_firstlineno))
+            % (inner.__name__, inner.__code__.co_filename,
+                inner.__code__.co_firstlineno))
 
     from inspect import currentframe, getouterframes
     outer_frame = getouterframes(currentframe())[1][0]
@@ -650,7 +657,7 @@ def monkeypatch_class(name, bases, namespace):
 
     assert len(bases) == 1, "Exactly one base class required"
     base = bases[0]
-    for name, value in namespace.iteritems():
+    for name, value in six.iteritems(namespace):
         if name != "__metaclass__":
             setattr(base, name, value)
     return base
@@ -716,7 +723,7 @@ def linear_combination(coefficients, vectors):
 def common_prefix(iterable, empty=None):
     it = iter(iterable)
     try:
-        pfx = it.next()
+        pfx = next(it)
     except StopIteration:
         return empty
 
@@ -732,7 +739,7 @@ def common_prefix(iterable, empty=None):
 
 
 def decorate(function, list):
-    return map(lambda x: (x, function(x)), list)
+    return [(x, function(x)) for x in list]
 
 
 def partition(criterion, list):
@@ -781,7 +788,7 @@ except AttributeError:
 
 def reverse_dictionary(the_dict):
     result = {}
-    for key, value in the_dict.iteritems():
+    for key, value in six.iteritems(the_dict):
         if value in result:
             raise RuntimeError(
                     "non-reversible mapping, duplicate key '%s'" % value)
@@ -884,7 +891,7 @@ def find_max_where(predicate, prec=1e-5, initial_guess=1, fail_bound=1e38):
 def argmin2(iterable, return_value=False):
     it = iter(iterable)
     try:
-        current_argmin, current_min = it.next()
+        current_argmin, current_min = next(it)
     except StopIteration:
         raise ValueError("argmin of empty iterable")
 
@@ -902,7 +909,7 @@ def argmin2(iterable, return_value=False):
 def argmax2(iterable, return_value=False):
     it = iter(iterable)
     try:
-        current_argmax, current_max = it.next()
+        current_argmax, current_max = next(it)
     except StopIteration:
         raise ValueError("argmax of empty iterable")
 
@@ -963,7 +970,7 @@ def average(iterable):
     it = iterable.__iter__()
 
     try:
-        sum = it.next()
+        sum = next(it)
         count = 1
     except StopIteration:
         raise ValueError("empty average")
@@ -1033,11 +1040,11 @@ def indices_in_shape(shape):
     if len(shape) == 0:
         yield ()
     elif len(shape) == 1:
-        for i in xrange(0, shape[0]):
+        for i in range(0, shape[0]):
             yield (i,)
     else:
         remainder = shape[1:]
-        for i in xrange(0, shape[0]):
+        for i in range(0, shape[0]):
             for rest in indices_in_shape(remainder):
                 yield (i,)+rest
 
@@ -1193,7 +1200,7 @@ def get_read_from_map_from_permutation(original, permuted):
     """
     assert len(original) == len(permuted)
     where_in_original = dict(
-            (original[i], i) for i in xrange(len(original)))
+            (original[i], i) for i in range(len(original)))
     assert len(where_in_original) == len(original)
     return tuple(where_in_original[pi] for pi in permuted)
 
@@ -1217,7 +1224,7 @@ def get_write_to_map_from_permutation(original, permuted):
     assert len(original) == len(permuted)
 
     where_in_permuted = dict(
-            (permuted[i], i) for i in xrange(len(permuted)))
+            (permuted[i], i) for i in range(len(permuted)))
 
     assert len(where_in_permuted) == len(permuted)
     return tuple(where_in_permuted[oi] for oi in original)
@@ -1351,7 +1358,7 @@ def string_histogram(iterable, min_value=None, max_value=None,
             try:
                 bins[bin_nr] += 1
             except:
-                print value, bin_nr, bin_starts
+                print(value, bin_nr, bin_starts)
                 raise
 
     from math import floor, ceil
@@ -1420,26 +1427,26 @@ class CPyUserInterface(object):
         self.doc = doc
 
     def show_usage(self, progname):
-        print "usage: %s <FILE-OR-STATEMENTS>" % progname
-        print
-        print "FILE-OR-STATEMENTS may either be Python statements of the form"
-        print "'variable1 = value1; variable2 = value2' or the name of a file"
-        print "containing such statements. Any valid Python code may be used"
-        print "on the command line or in a command file. If new variables are"
-        print "used, they must start with 'user_' or just '_'."
-        print
-        print "The following variables are recognized:"
+        print("usage: %s <FILE-OR-STATEMENTS>" % progname)
+        print()
+        print("FILE-OR-STATEMENTS may either be Python statements of the form")
+        print("'variable1 = value1; variable2 = value2' or the name of a file")
+        print("containing such statements. Any valid Python code may be used")
+        print("on the command line or in a command file. If new variables are")
+        print("used, they must start with 'user_' or just '_'.")
+        print()
+        print("The following variables are recognized:")
         for v in sorted(self.variables):
-            print "  %s = %s" % (v, self.variables[v])
+            print("  %s = %s" % (v, self.variables[v]))
             if v in self.doc:
-                print "    %s" % self.doc[v]
+                print("    %s" % self.doc[v])
 
-        print
-        print "The following constants are supplied:"
+        print()
+        print("The following constants are supplied:")
         for c in sorted(self.constants):
-            print "  %s = %s" % (c, self.constants[c])
+            print("  %s = %s" % (c, self.constants[c]))
             if c in self.doc:
-                print "    %s" % self.doc[c]
+                print("    %s" % self.doc[c])
 
     def gather(self, argv=None):
         import sys
@@ -1530,7 +1537,7 @@ def typedump(val, max_seq=5, special_handlers={}):
             return "{%s}" % (
                     ", ".join(
                         "%r: %s" % (str(k), typedump(v))
-                        for k, v in val.iteritems()))
+                        for k, v in six.iteritems(val)))
 
         try:
             if len(val) > max_seq:
@@ -1564,9 +1571,9 @@ def invoke_editor(s, filename="edit.txt", descr="the file"):
         p = Popen([os.environ["EDITOR"], full_name])
         os.waitpid(p.pid, 0)[1]
     else:
-        print "(Set the EDITOR environment variable to be " \
-                "dropped directly into an editor next time.)"
-        raw_input("Edit %s at %s now, then hit [Enter]:"
+        print("(Set the EDITOR environment variable to be " \
+                "dropped directly into an editor next time.)")
+        input("Edit %s at %s now, then hit [Enter]:"
                 % (descr, full_name))
 
     inf = open(full_name, "r")
diff --git a/pytools/arithmetic_container.py b/pytools/arithmetic_container.py
index e4bd107..8b15382 100644
--- a/pytools/arithmetic_container.py
+++ b/pytools/arithmetic_container.py
@@ -1,6 +1,10 @@
 from __future__ import division
-from decorator import decorator
+from __future__ import absolute_import
+from .decorator import decorator
 import operator
+import six
+from six.moves import range
+from six.moves import zip
 
 
 
@@ -214,7 +218,7 @@ def work_with_arithmetic_containers(f, *args, **kwargs):
         else:
             formal_args.append(SimpleArg(len(formal_args)))
 
-    for name, arg in kwargs.iteritems():
+    for name, arg in six.iteritems(kwargs):
         if isinstance(arg, ArithmeticList):
             formal_kwargs[name] = ListArg(len(lists))
             lists.append(arg)
@@ -229,7 +233,7 @@ def work_with_arithmetic_containers(f, *args, **kwargs):
                 f(
                     *list(formal_arg.eval(tp) for formal_arg in formal_args), 
                     **dict((name, formal_arg.eval(tp)) 
-                        for name, formal_arg in formal_kwargs.iteritems())
+                        for name, formal_arg in six.iteritems(formal_kwargs))
                     )
                 for tp in zip(*lists))
     else:
@@ -268,7 +272,7 @@ class ArithmeticListMatrix:
 
         for i, row in enumerate(self.matrix):
             if len(row) != len(other):
-                raise ValueError, "matrix width does not match ArithmeticList"
+                raise ValueError("matrix width does not match ArithmeticList")
 
             for j, entry in enumerate(row):
                 if not isinstance(entry, (int, float)) or entry:
diff --git a/pytools/batchjob.py b/pytools/batchjob.py
index 24a757f..4a190a4 100644
--- a/pytools/batchjob.py
+++ b/pytools/batchjob.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+import six
 def _cp(src, dest):
     from pytools import assert_not_a_file
     assert_not_a_file(dest)
@@ -85,7 +87,7 @@ class GridEngineJob(BatchJob):
 
         from os import getenv
 
-        for var, value in env.iteritems():
+        for var, value in six.iteritems(env):
             if value is INHERIT:
                 value = getenv(var)
 
@@ -117,7 +119,7 @@ class PBSJob(BatchJob):
 
         from os import getenv
 
-        for var, value in env.iteritems():
+        for var, value in six.iteritems(env):
             if value is INHERIT:
                 value = getenv(var)
 
@@ -160,7 +162,7 @@ class ConstructorPlaceholder:
         return "%s(%s)" % (self.classname,
                 ",".join(
                     [str(arg) for arg in self.args]
-                    + ["%s=%s" % (kw, repr(val)) for kw, val in self.kwargs.iteritems()]
+                    + ["%s=%s" % (kw, repr(val)) for kw, val in six.iteritems(self.kwargs)]
                     )
                 )
     __repr__ = __str__
diff --git a/pytools/convergence.py b/pytools/convergence.py
index cdedc83..d9616c3 100644
--- a/pytools/convergence.py
+++ b/pytools/convergence.py
@@ -1,4 +1,7 @@
+from __future__ import absolute_import
 import numpy as np
+from six.moves import range
+from six.moves import zip
 
 
 # {{{ eoc estimation --------------------------------------------------------------
@@ -67,7 +70,7 @@ class EOCRecorder(object):
         return self.pretty_print()
 
     def write_gnuplot_file(self, filename):
-        outfile = file(filename, "w")
+        outfile = open(filename, "w")
         for absc, err in self.history:
             outfile.write("%f %f\n" % (absc, err))
         result = self.estimate_order_of_convergence()
diff --git a/pytools/datatable.py b/pytools/datatable.py
index 8019f30..d66013d 100644
--- a/pytools/datatable.py
+++ b/pytools/datatable.py
@@ -1,4 +1,8 @@
+from __future__ import absolute_import
 from pytools import Record
+import six
+from six.moves import range
+from six.moves import zip
 
 
 class Row(Record):
@@ -58,7 +62,7 @@ class DataTable:
     def insert(self, **kwargs):
         values = [None for i in range(len(self.column_names))]
 
-        for key, val in kwargs.iteritems():
+        for key, val in six.iteritems(kwargs):
             values[self.column_indices[key]] = val
 
         self.insert_row(tuple(values))
@@ -78,7 +82,7 @@ class DataTable:
 
         criteria = tuple(
                 (self.column_indices[key], value)
-                for key, value in kwargs.iteritems())
+                for key, value in six.iteritems(kwargs))
 
         result_data = []
 
@@ -101,7 +105,7 @@ class DataTable:
         if len(filtered) > 1:
             raise RuntimeError("more than one matching entry for get()")
 
-        return Row(dict(zip(self.column_names, filtered.data[0])))
+        return Row(dict(list(zip(self.column_names, filtered.data[0]))))
 
     def clear(self):
         del self.data[:]
@@ -188,8 +192,8 @@ class DataTable:
 
         result_data = []
 
-        this_row = this_iter.next()
-        other_row = other_iter.next()
+        this_row = next(this_iter)
+        other_row = next(other_iter)
 
         this_over = False
         other_over = False
@@ -216,7 +220,7 @@ class DataTable:
                 while this_row[this_key_idx] == this_key:
                     this_batch.append(this_row)
                     try:
-                        this_row = this_iter.next()
+                        this_row = next(this_iter)
                     except StopIteration:
                         this_over = True
                         break
@@ -229,7 +233,7 @@ class DataTable:
                 while other_row[other_key_idx] == other_key:
                     other_batch.append(other_row)
                     try:
-                        other_row = other_iter.next()
+                        other_row = next(other_iter)
                     except StopIteration:
                         other_over = True
                         break
diff --git a/pytools/debug.py b/pytools/debug.py
index 064913a..6ddb171 100644
--- a/pytools/debug.py
+++ b/pytools/debug.py
@@ -1,4 +1,8 @@
+from __future__ import absolute_import
+from __future__ import print_function
 from pytools import memoize
+import six
+from six.moves import input
 
 
 # {{{ debug files -------------------------------------------------------------
@@ -15,7 +19,7 @@ def make_unique_filesystem_object(stem, extension="", directory="",
     if creator is None:
         def creator(name):
             return os.fdopen(os.open(name,
-                    os.O_CREAT | os.O_WRONLY | os.O_EXCL, 0444), "w")
+                    os.O_CREAT | os.O_WRONLY | os.O_EXCL, 0o444), "w")
 
     i = 0
     while True:
@@ -75,7 +79,7 @@ def refdebug(obj, top_level=True, exclude=[]):
     else:
         import gc
         print_head = True
-        print "-------------->"
+        print("-------------->")
         try:
             reflist = [x for x in gc.get_referrers(obj)
                     if not is_excluded(x)]
@@ -83,8 +87,8 @@ def refdebug(obj, top_level=True, exclude=[]):
             idx = 0
             while True:
                 if print_head:
-                    print "referring to", id(obj), type(obj), obj
-                    print "----------------------"
+                    print("referring to", id(obj), type(obj), obj)
+                    print("----------------------")
                     print_head = False
                 r = reflist[idx]
 
@@ -93,16 +97,16 @@ def refdebug(obj, top_level=True, exclude=[]):
                 else:
                     s = str(r)
 
-                print "%d/%d: " % (idx, len(reflist)), id(r), type(r), s
+                print("%d/%d: " % (idx, len(reflist)), id(r), type(r), s)
 
                 if isinstance(r, dict):
-                    for k, v in r.iteritems():
+                    for k, v in six.iteritems(r):
                         if v is obj:
-                            print "...referred to from key", k
+                            print("...referred to from key", k)
 
-                print "[d]ig, [n]ext, [p]rev, [e]val, [r]eturn, [q]uit?"
+                print("[d]ig, [n]ext, [p]rev, [e]val, [r]eturn, [q]uit?")
 
-                response = raw_input()
+                response = input()
 
                 if response == "d":
                     refdebug(r, top_level=False, exclude=exclude+[reflist])
@@ -114,23 +118,23 @@ def refdebug(obj, top_level=True, exclude=[]):
                     if idx - 1 >= 0:
                         idx -= 1
                 elif response == "e":
-                    print "type expression, obj is your object:"
-                    expr_str = raw_input()
+                    print("type expression, obj is your object:")
+                    expr_str = input()
                     try:
                         res = eval(expr_str, {"obj": r})
                     except:
                         from traceback import print_exc
                         print_exc()
-                    print res
+                    print(res)
                 elif response == "r":
                     return
                 elif response == "q":
                     raise RefDebugQuit()
                 else:
-                    print "WHAT YOU SAY!!! (invalid choice)"
+                    print("WHAT YOU SAY!!! (invalid choice)")
 
         finally:
-            print "<--------------"
+            print("<--------------")
 
 # }}}
 
diff --git a/pytools/decorator.py b/pytools/decorator.py
index 502da0f..1602965 100644
--- a/pytools/decorator.py
+++ b/pytools/decorator.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
 # Python decorator module
 # by Michele Simionato
 # http://www.phyast.pitt.edu/~micheles/python/
@@ -47,9 +48,9 @@ def getinfo(func):
     signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
                                       formatvalue=lambda value: "")[1:-1]
     return dict(name=func.__name__, argnames=argnames, signature=signature,
-                defaults = func.func_defaults, doc=func.__doc__,
+                defaults = func.__defaults__, doc=func.__doc__,
                 module=func.__module__, dict=func.__dict__,
-                globals=func.func_globals, closure=func.func_closure)
+                globals=func.__globals__, closure=func.__closure__)
 
 def update_wrapper(wrapper, wrapped, create=False):
     """
@@ -76,7 +77,7 @@ def update_wrapper(wrapper, wrapped, create=False):
     wrapper.__doc__ = infodict['doc']
     wrapper.__module__ = infodict['module']
     wrapper.__dict__.update(infodict['dict'])
-    wrapper.func_defaults = infodict['defaults']
+    wrapper.__defaults__ = infodict['defaults']
     return wrapper
 
 # the real meat is here
diff --git a/pytools/diskdict.py b/pytools/diskdict.py
index b8489a8..d130389 100644
--- a/pytools/diskdict.py
+++ b/pytools/diskdict.py
@@ -1,6 +1,8 @@
+from __future__ import absolute_import
 # see end of file for sqlite import
 
 from pytools import memoize
+import six
 
 
 @memoize
@@ -20,7 +22,7 @@ def get_disk_dict(name, version, **kwargs):
 
     try:
         os.mkdir(cache_dir)
-    except OSError, e:
+    except OSError as e:
         from errno import EEXIST
         if e.errno != EEXIST:
             raise
@@ -61,14 +63,14 @@ class DiskDict(object):
                     result_pickle blob)""")
 
         def mtime(file):
-            if not isinstance(file, basestring):
+            if not isinstance(file, six.string_types):
                 # assume file names a module
                 file = file.__file__
 
             import os
             return os.stat(file).st_mtime
 
-        from cPickle import dumps
+        from six.moves.cPickle import dumps
         self.version = (version_base,) + tuple(
             mtime(dm) for dm in dep_modules)
         self.version_pickle = dumps(self.version)
@@ -83,7 +85,7 @@ class DiskDict(object):
         if key in self.cache:
             return True
         else:
-            from cPickle import loads
+            from six.moves.cPickle import loads
             for key_pickle, version_pickle, result_pickle in self.db_conn.execute(
                     "select key_pickle, version_pickle, result_pickle from data"
                     " where key_hash = ? and version_hash = ?",
@@ -100,7 +102,7 @@ class DiskDict(object):
         try:
             return self.cache[key]
         except KeyError:
-            from cPickle import loads
+            from six.moves.cPickle import loads
             for key_pickle, version_pickle, result_pickle in self.db_conn.execute(
                     "select key_pickle, version_pickle, result_pickle from data"
                     " where key_hash = ? and version_hash = ?",
@@ -117,7 +119,7 @@ class DiskDict(object):
         if key in self.cache:
             del self.cache[key]
 
-        from cPickle import loads
+        from six.moves.cPickle import loads
         for item_id, key_pickle, version_pickle in self.db_conn.execute(
                 "select id, key_pickle, version_pickle from data"
                 " where key_hash = ? and version_hash = ?",
@@ -135,7 +137,7 @@ class DiskDict(object):
 
         self.cache[key] = value
 
-        from cPickle import dumps
+        from six.moves.cPickle import dumps
         self.db_conn.execute("insert into data"
                 " (key_hash, key_pickle, version_hash, "
                 "    version_pickle, result_pickle)"
diff --git a/pytools/lex.py b/pytools/lex.py
index 961526b..a1c4914 100644
--- a/pytools/lex.py
+++ b/pytools/lex.py
@@ -1,4 +1,7 @@
+from __future__ import absolute_import
+from __future__ import print_function
 import re
+import six
 
 
 class RuleError(RuntimeError):
@@ -48,7 +51,7 @@ def lex(lex_table, s, debug=False, match_objects=False):
 
     def matches_rule(rule, s, start):
         if debug:
-            print "Trying", rule, "on", s[start:]
+            print("Trying", rule, "on", s[start:])
         if isinstance(rule, tuple):
             if rule[0] == "|":
                 for subrule in rule[1:]:
@@ -67,7 +70,7 @@ def lex(lex_table, s, debug=False, match_objects=False):
                     else:
                         return 0, None
                 return my_match_length, None
-        elif isinstance(rule, basestring):
+        elif isinstance(rule, six.string_types):
             return matches_rule(rule_dict[rule], s, start)
         elif isinstance(rule, RE):
             match_obj = rule.RE.match(s, start)
diff --git a/pytools/log.py b/pytools/log.py
index 7ff25a6..d7f1f95 100644
--- a/pytools/log.py
+++ b/pytools/log.py
@@ -1,6 +1,11 @@
 from __future__ import division
+from __future__ import absolute_import
+from __future__ import print_function
 
 import logging
+import six
+from six.moves import range
+from six.moves import zip
 logger = logging.getLogger(__name__)
 
 
@@ -169,7 +174,7 @@ def _join_by_first_of_tuple(list_of_iterables):
     loi = [i.__iter__() for i in list_of_iterables]
     if not loi:
         return
-    key_vals = [iter.next() for iter in loi]
+    key_vals = [next(iter) for iter in loi]
     keys = [kv[0] for kv in key_vals]
     values = [kv[1] for kv in key_vals]
     target_key = max(keys)
@@ -180,7 +185,7 @@ def _join_by_first_of_tuple(list_of_iterables):
     while True:
         while keys[i] < target_key or force_advance:
             try:
-                new_key, new_value = loi[i].next()
+                new_key, new_value = next(loi[i])
             except StopIteration:
                 return
             assert keys[i] < new_key
@@ -229,7 +234,7 @@ def _get_random_suffix(n):
             + [chr(48+i) for i in range(10)])
 
     from random import choice
-    return "".join(choice(characters) for i in xrange(n))
+    return "".join(choice(characters) for i in range(n))
 
 
 def _set_up_schema(db_conn):
@@ -311,7 +316,7 @@ class LogManager(object):
           is requested.
         """
 
-        assert isinstance(mode, basestring), "mode must be a string"
+        assert isinstance(mode, six.string_types), "mode must be a string"
         assert mode in ["w", "r", "wu"], "invalid mode"
 
         self.quantity_data = {}
@@ -554,7 +559,7 @@ class LogManager(object):
             self.db_conn.execute("insert into %s values (?,?,?)" % name,
                     (self.tick_count, self.rank, float(value)))
         except:
-            print "while adding datapoint for '%s':" % name
+            print("while adding datapoint for '%s':" % name)
             raise
 
     def _gather_for_descriptor(self, gd):
@@ -638,7 +643,7 @@ class LogManager(object):
         from sqlite3 import OperationalError
         try:
             self.db_conn.commit()
-        except OperationalError, e:
+        except OperationalError as e:
             from warnings import warn
             warn("encountered sqlite error during commit: %s" % e)
 
@@ -715,8 +720,8 @@ class LogManager(object):
 
             unit_dict = dict((dd.varname, dd.qdat.unit) for dd in dep_data)
             from pytools import all
-            if all(v is not None for v in unit_dict.itervalues()):
-                unit_dict = dict((k, parse(v)) for k, v in unit_dict.iteritems())
+            if all(v is not None for v in six.itervalues(unit_dict)):
+                unit_dict = dict((k, parse(v)) for k, v in six.iteritems(unit_dict))
                 unit = substitute(parsed, unit_dict)
             else:
                 unit = None
@@ -786,7 +791,7 @@ class LogManager(object):
         stepless_data = [tup for step, tup in data]
 
         if stepless_data:
-            data_x, data_y = zip(*stepless_data)
+            data_x, data_y = list(zip(*stepless_data))
         else:
             data_x = []
             data_y = []
@@ -927,7 +932,7 @@ class LogManager(object):
             return
 
         data_block = dict((qname, self.last_values.get(qname, 0))
-                for qname in self.quantity_data.iterkeys())
+                for qname in six.iterkeys(self.quantity_data))
 
         if self.mpi_comm is not None and self.have_nonlocal_watches:
             gathered_data = self.mpi_comm.gather(data_block, self.head_rank)
@@ -937,7 +942,7 @@ class LogManager(object):
         if self.rank == self.head_rank:
             values = {}
             for data_block in gathered_data:
-                for name, value in data_block.iteritems():
+                for name, value in six.iteritems(data_block):
                     values.setdefault(name, []).append(value)
 
             def compute_watch_str(watch):
@@ -949,8 +954,8 @@ class LogManager(object):
                     return "%s:div0" % watch.display
 
             if self.watches:
-                print " | ".join(
-                        compute_watch_str(watch) for watch in self.watches)
+                print(" | ".join(
+                        compute_watch_str(watch) for watch in self.watches))
 
         ticks_per_sec = self.tick_count/max(1, time()-self.start_time)
         self.next_watch_tick = self.tick_count + int(max(1, ticks_per_sec))
diff --git a/pytools/mpi.py b/pytools/mpi.py
index a575c07..5d9c931 100644
--- a/pytools/mpi.py
+++ b/pytools/mpi.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
 def check_for_mpi_relaunch(argv):
     if argv[1] != "--mpi-relaunch":
         return
diff --git a/pytools/mpiwrap.py b/pytools/mpiwrap.py
index fd458ab..0090e88 100644
--- a/pytools/mpiwrap.py
+++ b/pytools/mpiwrap.py
@@ -1,4 +1,5 @@
 """See pytools.prefork for this module's reason for being."""
+from __future__ import absolute_import
 
 import mpi4py.rc
 
diff --git a/pytools/obj_array.py b/pytools/obj_array.py
index 6dfd52b..7dc49ae 100644
--- a/pytools/obj_array.py
+++ b/pytools/obj_array.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
 import numpy
 from pytools import my_decorator as decorator
 
diff --git a/pytools/persistent_dict.py b/pytools/persistent_dict.py
index 30e50b5..7a9f8e3 100644
--- a/pytools/persistent_dict.py
+++ b/pytools/persistent_dict.py
@@ -1,6 +1,8 @@
 """Generic persistent, concurrent dictionary-like facility."""
 
 from __future__ import division, with_statement
+from __future__ import absolute_import
+import six
 
 __copyright__ = "Copyright (C) 2011,2014 Andreas Kloeckner"
 
@@ -54,7 +56,7 @@ def _erase_dir(dir):
 
 
 def update_checksum(checksum, obj):
-    if isinstance(obj, unicode):
+    if isinstance(obj, six.text_type):
         checksum.update(obj.encode("utf8"))
     else:
         checksum.update(obj)
@@ -254,7 +256,7 @@ class PersistentDict(object):
 
         try:
             os.makedirs(self.container_dir)
-        except OSError, e:
+        except OSError as e:
             from errno import EEXIST
             if e.errno != EEXIST:
                 raise
@@ -276,13 +278,13 @@ class PersistentDict(object):
                 if item_dir_m.existed:
                     item_dir_m.reset()
 
-                for info_name, info_value in info_files.iteritems():
+                for info_name, info_value in six.iteritems(info_files):
                     info_path = item_dir_m.sub("info_"+info_name)
 
                     with open(info_path, "wt") as outf:
                         outf.write(info_value)
 
-                from cPickle import dump, HIGHEST_PROTOCOL
+                from six.moves.cPickle import dump, HIGHEST_PROTOCOL
                 value_path = item_dir_m.sub("contents")
                 with open(value_path, "wb") as outf:
                     dump(value, outf, protocol=HIGHEST_PROTOCOL)
@@ -315,7 +317,7 @@ class PersistentDict(object):
                 key_path = item_dir_m.sub("key")
                 value_path = item_dir_m.sub("contents")
 
-                from cPickle import load
+                from six.moves.cPickle import load
 
                 # {{{ load key file
 
@@ -324,9 +326,9 @@ class PersistentDict(object):
                 try:
                     with open(key_path, "rb") as inf:
                         read_key = load(inf)
-                except IOError, e:
+                except IOError as e:
                     exc = e
-                except EOFError, e:
+                except EOFError as e:
                     exc = e
 
                 if exc is not None:
@@ -353,9 +355,9 @@ class PersistentDict(object):
                 try:
                     with open(value_path, "rb") as inf:
                         read_contents = load(inf)
-                except IOError, e:
+                except IOError as e:
                     exc = e
-                except EOFError, e:
+                except EOFError as e:
                     exc = e
 
                 if exc is not None:
diff --git a/pytools/prefork.py b/pytools/prefork.py
index 445ddf4..a81c987 100644
--- a/pytools/prefork.py
+++ b/pytools/prefork.py
@@ -5,6 +5,7 @@ parent process.
 
 Since none of this is MPI-specific, it got parked in pytools.
 """
+from __future__ import absolute_import
 
 
 
@@ -22,7 +23,7 @@ class DirectForker:
         from subprocess import call
         try:
             return call(cmdline, cwd=cwd)
-        except OSError, e:
+        except OSError as e:
             raise ExecError("error invoking '%s': %s"
                     % ( " ".join(cmdline), e))
 
@@ -31,7 +32,7 @@ class DirectForker:
         from subprocess import Popen, PIPE
         try:
             return Popen(cmdline, cwd=cwd, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate()[0]
-        except OSError, e:
+        except OSError as e:
             raise ExecError("error invoking '%s': %s"
                     % ( " ".join(cmdline), e))
 
@@ -48,7 +49,7 @@ class DirectForker:
                 raise ExecError("status %d invoking '%s': %s"
                         % (popen.returncode, " ".join(cmdline), stderr_data))
             return popen.returncode, stdout_data, stderr_data
-        except OSError, e:
+        except OSError as e:
             raise ExecError("error invoking '%s': %s"
                     % ( " ".join(cmdline), e))
 
@@ -56,7 +57,7 @@ class DirectForker:
 
 def _send_packet(sock, data):
     from struct import pack
-    from cPickle import dumps
+    from six.moves.cPickle import dumps
 
     packet = dumps(data)
 
@@ -80,7 +81,7 @@ def _recv_packet(sock, who="Process", partner="other end"):
     while len(packet) < size:
         packet += sock.recv(size)
 
-    from cPickle import loads
+    from six.moves.cPickle import loads
     return loads(packet)
 
 
@@ -110,7 +111,7 @@ def _fork_server(sock):
 
             try:
                 result = funcs[func_name](*args, **kwargs)
-            except Exception, e:
+            except Exception as e:
                 _send_packet(sock, ("exception", e))
             else:
                 _send_packet(sock, ("ok", result))
diff --git a/pytools/spatial_btree.py b/pytools/spatial_btree.py
index 5d8f318..238c8c2 100644
--- a/pytools/spatial_btree.py
+++ b/pytools/spatial_btree.py
@@ -1,7 +1,11 @@
 from __future__ import division
+from __future__ import absolute_import
+from six.moves import range
 
 
-def do_boxes_intersect((bl1,tr1), (bl2,tr2)):
+def do_boxes_intersect(xxx_todo_changeme, xxx_todo_changeme1):
+    (bl1,tr1) = xxx_todo_changeme
+    (bl2,tr2) = xxx_todo_changeme1
     (dimension,) = bl1.shape
     for i in range(0, dimension):
         if max(bl1[i], bl2[i]) > min(tr1[i], tr2[i]):
@@ -15,7 +19,7 @@ def _get_elements_bounding_box(elements):
     import numpy
 
     if len(elements) == 0:
-        raise RuntimeError, "Cannot get the bounding box of no elements."
+        raise RuntimeError("Cannot get the bounding box of no elements.")
 
     bboxes = [box for el,box in elements]
     bottom_lefts = [bl for bl,tr in bboxes]
diff --git a/pytools/stopwatch.py b/pytools/stopwatch.py
index 6829b60..231b4c6 100644
--- a/pytools/stopwatch.py
+++ b/pytools/stopwatch.py
@@ -1,4 +1,6 @@
 from __future__ import division
+from __future__ import absolute_import
+from __future__ import print_function
 import time
 import pytools
 
@@ -33,13 +35,13 @@ class Job:
         self.Name = name
         self.StopWatch = StopWatch().start()
         if self.is_visible():
-            print "%s..." % name
+            print("%s..." % name)
 
     def done(self):
         elapsed = self.StopWatch.elapsed()
         JOB_TIMES[self.Name] += elapsed
         if self.is_visible():
-            print " " * (len(self.Name) + 2), elapsed, "seconds"
+            print(" " * (len(self.Name) + 2), elapsed, "seconds")
   
     def is_visible(self):
         if PRINT_JOBS.get():
@@ -69,7 +71,7 @@ class EtaEstimator:
 
 def print_job_summary():
     for key in JOB_TIMES:
-        print key, " " * (50-len(key)), JOB_TIMES[key]
+        print(key, " " * (50-len(key)), JOB_TIMES[key])
 
 
 
diff --git a/pytools/test.py b/pytools/test.py
index d207b8d..e0b28e8 100644
--- a/pytools/test.py
+++ b/pytools/test.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
 try:
     from py.test import mark as mark_test
 except ImportError:
diff --git a/setup.py b/setup.py
index 7303b8e..f704463 100644
--- a/setup.py
+++ b/setup.py
@@ -1,14 +1,9 @@
+from __future__ import absolute_import
 #!/usr/bin/env python
 # -*- coding: latin1 -*-
 
 from setuptools import setup
 
-try:
-    from distutils.command.build_py import build_py_2to3 as build_py
-except ImportError:
-    # 2.x
-    from distutils.command.build_py import build_py
-
 setup(name="pytools",
       version="2014.3.4",
       description="A collection of tools for Python",
@@ -51,7 +46,4 @@ setup(name="pytools",
       url="http://pypi.python.org/pypi/pytools",
       author_email="inform@tiker.net",
       license="MIT",
-      packages=["pytools"],
-
-      # 2to3 invocation
-      cmdclass={'build_py': build_py})
+      packages=["pytools"])
diff --git a/test/test_data_table.py b/test/test_data_table.py
index 2e6fa5c..c1e795a 100644
--- a/test/test_data_table.py
+++ b/test/test_data_table.py
@@ -1,4 +1,7 @@
 from __future__ import division
+from __future__ import absolute_import
+from six.moves import range
+from six.moves import zip
 # data from Wikipedia "join" article
 
 
@@ -71,7 +74,7 @@ def test_aggregate():
 
 def test_aggregate_2():
     from pytools.datatable import DataTable
-    tbl = DataTable(["step", "value"], zip(range(20), range(20)))
+    tbl = DataTable(["step", "value"], list(zip(list(range(20)), list(range(20)))))
     agg = tbl.aggregated(["step"], "value", max)
     assert agg.column_data("step") == list(range(20))
     assert agg.column_data("value") == list(range(20))
diff --git a/test/test_math_stuff.py b/test/test_math_stuff.py
index 5768c83..7450e53 100644
--- a/test/test_math_stuff.py
+++ b/test/test_math_stuff.py
@@ -1,4 +1,5 @@
 from __future__ import division
+from __future__ import absolute_import
 
 
 
diff --git a/test/test_persistent_dict.py b/test/test_persistent_dict.py
index c2c2c99..c1f7ec7 100644
--- a/test/test_persistent_dict.py
+++ b/test/test_persistent_dict.py
@@ -1,7 +1,10 @@
 from __future__ import division, with_statement
+from __future__ import absolute_import
 
 import pytest  # noqa
 import sys  # noqa
+from six.moves import range
+from six.moves import zip
 
 
 def test_persistent_dict():
@@ -19,19 +22,19 @@ def test_persistent_dict():
     keys = [(randrange(2000), rand_str(), None) for i in range(20)]
     values = [randrange(2000) for i in range(20)]
 
-    d = dict(zip(keys, values))
+    d = dict(list(zip(keys, values)))
 
     for k, v in zip(keys, values):
         pdict[k] = v
         pdict.store(k, v, info_files={"hey": str(v)})
 
-    for k, v in d.items():
+    for k, v in list(d.items()):
         assert d[k] == pdict[k]
 
     for k, v in zip(keys, values):
         pdict.store(k, v+1, info_files={"hey": str(v)})
 
-    for k, v in d.items():
+    for k, v in list(d.items()):
         assert d[k] + 1 == pdict[k]
 
 
diff --git a/test/test_pytools.py b/test/test_pytools.py
index 059129e..8279f4f 100644
--- a/test/test_pytools.py
+++ b/test/test_pytools.py
@@ -1,4 +1,5 @@
 from __future__ import division, with_statement
+from __future__ import absolute_import
 
 import pytest
 import sys  # noqa
-- 
GitLab