Skip to content

Commit d22d502

Browse files
committed
Attempted port to py2/py3 polyglottitude
1 parent fe2bbc6 commit d22d502

28 files changed

+205
-182
lines changed

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
include .coveragerc tox.ini
1+
include setup.cfg .coveragerc tox.ini
22
include TODO LICENSE.txt README.rst
33
recursive-include tools *.py *.R
44
recursive-include doc *

README.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ Downloads:
1515
http://pypi.python.org/pypi/patsy/
1616

1717
Dependencies:
18-
* Python. We test against versions 2.6 and later, and 3.2 and
19-
later. (Earlier versions may also work -- bug reports/patches
20-
accepted!)
18+
* Python (2.6+, or 3.2+)
2119
* numpy
2220

2321
Optional dependencies:

patsy/build.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
__all__ = ["design_matrix_builders", "DesignMatrixBuilder",
99
"build_design_matrices"]
1010

11+
import itertools
12+
import six
13+
1114
import numpy as np
1215
from patsy import PatsyError
1316
from patsy.categorical import (guess_categorical,
@@ -21,7 +24,7 @@
2124
from patsy.desc import ModelDesc
2225
from patsy.eval import EvalEnvironment
2326
from patsy.contrasts import code_contrast_matrix, Treatment
24-
from patsy.compat import itertools_product, OrderedDict
27+
from patsy.compat import OrderedDict
2528
from patsy.missing import NAAction
2629

2730
if have_pandas:
@@ -206,8 +209,8 @@ def test__CatFactorEvaluator():
206209

207210
def _column_combinations(columns_per_factor):
208211
# For consistency with R, the left-most item iterates fastest:
209-
iterators = [xrange(n) for n in reversed(columns_per_factor)]
210-
for reversed_combo in itertools_product(*iterators):
212+
iterators = [range(n) for n in reversed(columns_per_factor)]
213+
for reversed_combo in itertools.product(*iterators):
211214
yield reversed_combo[::-1]
212215

213216
def test__column_combinations():
@@ -335,7 +338,7 @@ def _factors_memorize(factors, data_iter_maker):
335338
# Now, cycle through the data until all the factors have finished
336339
# memorizing everything:
337340
memorize_needed = set()
338-
for factor, passes in passes_needed.iteritems():
341+
for factor, passes in six.iteritems(passes_needed):
339342
if passes > 0:
340343
memorize_needed.add(factor)
341344
which_pass = 0
@@ -377,7 +380,7 @@ class Data(object):
377380
CHUNKS = 3
378381
def __init__(self):
379382
self.calls = 0
380-
self.data = [{"chunk": i} for i in xrange(self.CHUNKS)]
383+
self.data = [{"chunk": i} for i in range(self.CHUNKS)]
381384
def __call__(self):
382385
self.calls += 1
383386
return iter(self.data)
@@ -437,7 +440,7 @@ def _examine_factor_types(factors, factor_states, data_iter_maker, NA_action):
437440
break
438441
# Pull out the levels
439442
cat_levels_contrasts = {}
440-
for factor, sniffer in cat_sniffers.iteritems():
443+
for factor, sniffer in six.iteritems(cat_sniffers):
441444
cat_levels_contrasts[factor] = sniffer.levels_contrast()
442445
return (num_column_counts, cat_levels_contrasts)
443446

@@ -472,6 +475,7 @@ def next(self):
472475
if self.i > 1:
473476
raise StopIteration
474477
return self.i
478+
__next__ = next
475479

476480
num_1dim = MockFactor()
477481
num_1col = MockFactor()
@@ -540,7 +544,7 @@ def next(self):
540544
try:
541545
_examine_factor_types([illegal_factor], illegal_factor_states, it,
542546
NAAction())
543-
except PatsyError, e:
547+
except PatsyError as e:
544548
assert e.origin is illegal_factor.origin
545549
else:
546550
assert False
@@ -640,7 +644,7 @@ def design_matrix_builders(termlists, data_iter_maker, NA_action="drop"):
640644
.. versionadded:: 0.2.0
641645
The ``NA_action`` argument.
642646
"""
643-
if isinstance(NA_action, basestring):
647+
if isinstance(NA_action, str):
644648
NA_action = NAAction(NA_action)
645649
all_factors = set()
646650
for termlist in termlists:
@@ -761,7 +765,7 @@ def subset(self, which_terms):
761765
design_info = self.design_info
762766
term_name_to_term = dict(zip(design_info.term_names,
763767
design_info.terms))
764-
if isinstance(which_terms, basestring):
768+
if isinstance(which_terms, str):
765769
# We don't use this EvalEnvironment -- all we want to do is to
766770
# find matching terms, and we can't do that use == on Term
767771
# objects, because that calls == on factor objects, which in turn
@@ -778,7 +782,7 @@ def subset(self, which_terms):
778782
evaluators = set()
779783
term_to_column_builders = {}
780784
for term_or_name in which_terms:
781-
if isinstance(term_or_name, basestring):
785+
if isinstance(term_or_name, str):
782786
if term_or_name not in term_name_to_term:
783787
raise PatsyError("requested term %r not found in "
784788
"this DesignMatrixBuilder"
@@ -802,7 +806,7 @@ def _build(self, evaluator_to_values, dtype):
802806
factor_to_values = {}
803807
need_reshape = False
804808
num_rows = None
805-
for evaluator, value in evaluator_to_values.iteritems():
809+
for evaluator, value in six.iteritems(evaluator_to_values):
806810
if evaluator in self._evaluators:
807811
factor_to_values[evaluator.factor] = value
808812
if num_rows is not None:
@@ -917,7 +921,7 @@ def build_design_matrices(builders, data,
917921
.. versionadded:: 0.2.0
918922
The ``NA_action`` argument.
919923
"""
920-
if isinstance(NA_action, basestring):
924+
if isinstance(NA_action, str):
921925
NA_action = NAAction(NA_action)
922926
if return_type == "dataframe" and not have_pandas:
923927
raise PatsyError("pandas.DataFrame was requested, but pandas "
@@ -958,8 +962,8 @@ def build_design_matrices(builders, data,
958962
value = np.asarray(value)
959963
evaluator_to_values[evaluator] = value
960964
# Handle NAs
961-
values = evaluator_to_values.values()
962-
is_NAs = evaluator_to_isNAs.values()
965+
values = list(evaluator_to_values.values())
966+
is_NAs = list(evaluator_to_isNAs.values())
963967
origins = [evaluator.factor.origin for evaluator in evaluator_to_values]
964968
pandas_index = index_checker.value
965969
num_rows = rows_checker.value

patsy/builtins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def Q(name):
8686
try:
8787
return env.namespace[name]
8888
except KeyError:
89-
raise NameError, "no data named %r found" % (name,)
89+
raise NameError("no data named %r found" % (name,))
9090

9191
def test_Q():
9292
a = 1

patsy/categorical.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
# handle the different stages of categorical data munging.
3535

3636
import numpy as np
37+
import six
3738
from patsy import PatsyError
3839
from patsy.state import stateful_transform
3940
from patsy.util import (SortAnythingKey,
@@ -269,10 +270,11 @@ def categorical_to_int(data, levels, NA_action, origin=None):
269270
if hasattr(data, "shape") and len(data.shape) > 1:
270271
raise PatsyError("categorical data must be 1-dimensional",
271272
origin)
272-
if not iterable(data) or isinstance(data, basestring):
273+
if (not iterable(data)
274+
or isinstance(data, (six.text_type, six.binary_type))):
273275
raise PatsyError("categorical data must be an iterable container")
274276
try:
275-
level_to_int = dict(zip(levels, xrange(len(levels))))
277+
level_to_int = dict(zip(levels, range(len(levels))))
276278
except TypeError:
277279
raise PatsyError("Error interpreting categorical data: "
278280
"all items must be hashable", origin)

patsy/compat.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,22 +79,6 @@ def scan(self, string):
7979
i = j
8080
return result, string[i:]
8181

82-
# itertools.product available in Python 2.6+
83-
import itertools
84-
if optional_dep_ok and hasattr(itertools, "product"):
85-
itertools_product = itertools.product
86-
else:
87-
# Copied directly from the Python documentation:
88-
def itertools_product(*args, **kwds):
89-
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
90-
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
91-
pools = map(tuple, args) * kwds.get('repeat', 1)
92-
result = [[]]
93-
for pool in pools:
94-
result = [x+[y] for x in result for y in pool]
95-
for prod in result:
96-
yield tuple(prod)
97-
9882
# functools available in Python 2.5+
9983
# This is just a cosmetic thing, so don't bother emulating it if we don't
10084
# have it.
@@ -131,7 +115,7 @@ def do_wrap(f2):
131115
def call_and_wrap_exc(msg, origin, f, *args, **kwargs):
132116
try:
133117
return f(*args, **kwargs)
134-
except Exception, e:
118+
except Exception as e:
135119
if sys.version_info[0] >= 3:
136120
new_exc = PatsyError("%s: %s: %s"
137121
% (msg, e.__class__.__name__, e),

patsy/compat_ordereddict.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
try:
99
from thread import get_ident as _get_ident
1010
except ImportError:
11-
from dummy_thread import get_ident as _get_ident
11+
# Hacked by njs -- I don't have dummy_thread and py3 doesn't have thread,
12+
# so the import fails when nosetests3 tries to load this file.
13+
#from dummy_thread import get_ident as _get_ident
14+
def _get_ident():
15+
return "<no get_ident>"
1216

1317
try:
1418
from _abcoll import KeysView, ValuesView, ItemsView

patsy/constraint.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
# Interpreting linear constraints like "2*x1 + x2 = 0"
66

7+
from __future__ import print_function
8+
79
# These are made available in the patsy.* namespace
810
__all__ = ["LinearConstraint"]
911

1012
import re
13+
import six
1114
import numpy as np
1215
from patsy import PatsyError
1316
from patsy.origin import Origin
@@ -353,10 +356,10 @@ def linear_constraint(constraint_like, variable_names):
353356
dtype=float)
354357
constants = np.zeros(len(constraint_like))
355358
used = set()
356-
for i, (name, value) in enumerate(constraint_like.iteritems()):
359+
for i, (name, value) in enumerate(six.iteritems(constraint_like)):
357360
if name in variable_names:
358361
idx = variable_names.index(name)
359-
elif isinstance(name, (int, long)):
362+
elif isinstance(name, six.integer_types):
360363
idx = name
361364
else:
362365
raise ValueError("unrecognized variable name/index %r"
@@ -369,16 +372,16 @@ def linear_constraint(constraint_like, variable_names):
369372
constants[i] = value
370373
return LinearConstraint(variable_names, coefs, constants)
371374

372-
if isinstance(constraint_like, basestring):
375+
if isinstance(constraint_like, str):
373376
constraint_like = [constraint_like]
374377
# fall-through
375378

376379
if (isinstance(constraint_like, list)
377380
and constraint_like
378-
and isinstance(constraint_like[0], basestring)):
381+
and isinstance(constraint_like[0], str)):
379382
constraints = []
380383
for code in constraint_like:
381-
if not isinstance(code, basestring):
384+
if not isinstance(code, str):
382385
raise ValueError("expected a string, not %r" % (code,))
383386
tree = parse_constraint(code, variable_names)
384387
evaluator = _EvalConstraint(variable_names)
@@ -387,7 +390,7 @@ def linear_constraint(constraint_like, variable_names):
387390

388391
if isinstance(constraint_like, tuple):
389392
if len(constraint_like) != 2:
390-
raise ValueError, "constraint tuple must have length 2"
393+
raise ValueError("constraint tuple must have length 2")
391394
coef, constants = constraint_like
392395
return LinearConstraint(variable_names, coef, constants)
393396

@@ -398,9 +401,9 @@ def linear_constraint(constraint_like, variable_names):
398401
def _check_lincon(input, varnames, coefs, constants):
399402
from numpy.testing.utils import assert_equal
400403
got = linear_constraint(input, varnames)
401-
print "got", got
404+
print("got", got)
402405
expected = LinearConstraint(varnames, coefs, constants)
403-
print "expected", expected
406+
print("expected", expected)
404407
assert_equal(got.variable_names, expected.variable_names)
405408
assert_equal(got.coefs, expected.coefs)
406409
assert_equal(got.constants, expected.constants)

patsy/contrasts.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
# http://www.ats.ucla.edu/stat/r/library/contrast_coding.htm
66
# http://www.ats.ucla.edu/stat/sas/webbooks/reg/chapter5/sasreg5.htm
77

8+
from __future__ import print_function
9+
810
# These are made available in the patsy.* namespace
911
__all__ = ["ContrastMatrix", "Treatment", "Poly", "Sum", "Helmert", "Diff"]
1012

1113
import sys
14+
import six
1215
import numpy as np
1316
from patsy import PatsyError
1417
from patsy.compat import triu_indices, tril_indices, diag_indices
@@ -37,7 +40,7 @@ def __init__(self, matrix, column_suffixes):
3740
self.matrix = np.asarray(matrix)
3841
self.column_suffixes = column_suffixes
3942
if self.matrix.shape[1] != len(column_suffixes):
40-
raise PatsyError, "matrix and column_suffixes don't conform"
43+
raise PatsyError("matrix and column_suffixes don't conform")
4144

4245
__repr__ = repr_pretty_delegate
4346
def _repr_pretty_(self, p, cycle):
@@ -82,19 +85,16 @@ def t(obj, expected):
8285
t(1, "1")
8386
t(1.0, "1.0")
8487
t("asdf", "asdf")
85-
t(u"asdf", "asdf")
88+
t(six.u("asdf"), "asdf")
8689
if sys.version_info >= (3,):
87-
# a utf-8 encoded euro-sign comes out as a real euro sign. We have to
88-
# use u""-style strings here, even though this is a py3-only block,
89-
# because otherwise 2to3 may be clever enough to realize that in py2
90-
# "\u20ac" produces a literal \, and double it for us when
91-
# converting.
92-
t(u"\u20ac".encode("utf-8"), u"\u20ac")
90+
# we can use "foo".encode here b/c this is python 3!
91+
# a utf-8 encoded euro-sign comes out as a real euro sign.
92+
t("\u20ac".encode("utf-8"), six.u("\u20ac"))
9393
# but a iso-8859-15 euro sign can't be decoded, and we fall back on
9494
# repr()
95-
t(u"\u20ac".encode("iso-8859-15"), "b'\\xa4'")
95+
t("\u20ac".encode("iso-8859-15"), "b'\\xa4'")
9696
else:
97-
t(u"\u20ac", "u'\\u20ac'")
97+
t(six.u("\u20ac"), "u'\\u20ac'")
9898

9999
def _name_levels(prefix, levels):
100100
return ["[%s%s]" % (prefix, _obj_to_readable_str(level)) for level in levels]
@@ -115,7 +115,7 @@ def _get_level(levels, level_ref):
115115
raise PatsyError("specified level %r is out of range"
116116
% (level_ref,))
117117
return level_ref
118-
raise PatsyError, "specified level %r not found" % (level_ref,)
118+
raise PatsyError("specified level %r not found" % (level_ref,))
119119

120120
def test__get_level():
121121
assert _get_level(["a", "b", "c"], 0) == 0
@@ -257,7 +257,7 @@ def _code_either(self, intercept, levels):
257257
# The constant term is always all 1's -- we don't normalize it.
258258
q[:, 0] = 1
259259
names = [".Constant", ".Linear", ".Quadratic", ".Cubic"]
260-
names += ["^%s" % (i,) for i in xrange(4, n)]
260+
names += ["^%s" % (i,) for i in range(4, n)]
261261
names = names[:n]
262262
if intercept:
263263
return ContrastMatrix(q, names)
@@ -280,12 +280,12 @@ def test_Poly():
280280
expected = [[1, -7.07106781186548e-01, 0.408248290463863],
281281
[1, 0, -0.816496580927726],
282282
[1, 7.07106781186547e-01, 0.408248290463863]]
283-
print matrix.matrix
283+
print(matrix.matrix)
284284
assert np.allclose(matrix.matrix, expected)
285285
matrix = t1.code_without_intercept(["a", "b", "c"])
286286
assert matrix.column_suffixes == [".Linear", ".Quadratic"]
287287
# Values from R 'options(digits=15); contr.poly(3)'
288-
print matrix.matrix
288+
print(matrix.matrix)
289289
assert np.allclose(matrix.matrix,
290290
[[-7.07106781186548e-01, 0.408248290463863],
291291
[0, -0.816496580927726],
@@ -294,7 +294,7 @@ def test_Poly():
294294
matrix = Poly(scores=[0, 10, 11]).code_with_intercept(["a", "b", "c"])
295295
assert matrix.column_suffixes == [".Constant", ".Linear", ".Quadratic"]
296296
# Values from R 'options(digits=15); contr.poly(3, scores=c(0, 10, 11))'
297-
print matrix.matrix
297+
print(matrix.matrix)
298298
assert np.allclose(matrix.matrix,
299299
[[1, -0.813733471206735, 0.0671156055214024],
300300
[1, 0.348742916231458, -0.7382716607354268],
@@ -305,13 +305,13 @@ def test_Poly():
305305
matrix = Poly(scores=[0, 10, 12]).code_with_intercept(["a", "b", "c"])
306306
assert matrix.column_suffixes == [".Constant", ".Linear", ".Quadratic"]
307307
# Values from R 'options(digits=15); contr.poly(3, scores=c(0, 10, 12))'
308-
print matrix.matrix
308+
print(matrix.matrix)
309309
assert np.allclose(matrix.matrix,
310310
[[1, -0.806559132617443, 0.127000127000191],
311311
[1, 0.293294230042706, -0.762000762001143],
312312
[1, 0.513264902574736, 0.635000635000952]])
313313

314-
matrix = t1.code_with_intercept(range(6))
314+
matrix = t1.code_with_intercept(list(range(6)))
315315
assert matrix.column_suffixes == [".Constant", ".Linear", ".Quadratic",
316316
".Cubic", "^4", "^5"]
317317

0 commit comments

Comments
 (0)