From 7e4c5325e3404688b3ab2c59b828e578c8c4dbd6 Mon Sep 17 00:00:00 2001 From: Taha Jahangir Date: Sat, 5 Mar 2016 08:40:58 +0330 Subject: [PATCH 1/2] Add Python 3 support (along with Python 2 with six) --- .gitignore | 3 +++ .hgignore | 2 -- .hgtags | 2 -- deep/__init__.py | 50 ++++++++++++++++++++++++++---------------------- deep/test.py | 2 +- deep_test.py | 28 ++++++++++++++++----------- setup.py | 3 ++- test_test.py | 2 +- 8 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 .gitignore delete mode 100644 .hgignore delete mode 100644 .hgtags diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dba2a2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +*~ +__pycache__/ diff --git a/.hgignore b/.hgignore deleted file mode 100644 index cb43c0a..0000000 --- a/.hgignore +++ /dev/null @@ -1,2 +0,0 @@ -.*\.pyc -.*~ diff --git a/.hgtags b/.hgtags deleted file mode 100644 index d20cba0..0000000 --- a/.hgtags +++ /dev/null @@ -1,2 +0,0 @@ -8e0e5d282768b58e232b25c8a1322009ffeba4cf r0.1 -957254f23f8d4b59eb1c7f6f8d5ed54596df61bc r0.2 diff --git a/deep/__init__.py b/deep/__init__.py index 14d15e6..3cf146e 100644 --- a/deep/__init__.py +++ b/deep/__init__.py @@ -106,6 +106,10 @@ class O(object): import sys import traceback +from six import text_type as unicode +from six.moves import range +import six + __all__ = ['diff', 'Equal', 'Is', @@ -151,7 +155,7 @@ def diff(i1, i2, debug=Unspec): class Comparator(object): """Base class for all Comparator objects.""" def render_value(self, value): - return `value` + return repr(value) def expr(self, expr): return expr @@ -215,7 +219,7 @@ def descend(self, i1, i2): # Comparators are not hashable so this will be just id() key = (id(i1), i2) cache = self.cache - if cache.has_key(key): + if key in cache: equals = cache[key] else: cache[key] = True # assume true to match circular structures @@ -300,7 +304,7 @@ def render_full(self): (self.render_path(), self.render_expected(), self.render_actual()) def print_full(self): - print self.render_full() + print(self.render_full()) class DebugComparison(Comparison): """This class is useful if you are debugging a comparison and would like @@ -310,7 +314,7 @@ def __init__(self): Comparison.__init__(self) def debug(self, msg): - print "%s%s" % (" " * self.depth, msg) + print("%s%s" % (" " * self.depth, msg)) def descend(self, i1, i2): self.debug("descend(%s, %s)" % (i1, i2)) @@ -322,7 +326,7 @@ def descend(self, i1, i2): def wrap(self, item): wrapped = super(DebugComparison, self).wrap(item) - self.debug("%s wrapped as %s" % (`item`, `wrapped`)) + self.debug("%s wrapped as %s" % (repr(item), repr(wrapped))) return wrapped class ValueComparator(Comparator): @@ -335,7 +339,7 @@ def render(self): return self.render_value(self.value) def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, `self.value`) + return "%s(%s)" % (self.__class__.__name__, repr(self.value)) class TransformComparator(ValueComparator): """A base class for comparators that transform their inout and then @@ -352,7 +356,7 @@ def trans_args(self): return "" def __repr__(self): - return "%s(%s)==%s" %(self.__class__.__name__, self.trans_args(), `self.value`) + return "%s(%s)==%s" %(self.__class__.__name__, self.trans_args(), repr(self.value)) class Equal(ValueComparator): """Compares using python's == .""" @@ -402,7 +406,7 @@ def expr(self, expr): return "%s[%s]" % (expr, self.render_value(self.index)) def trans_args(self): - return "%s" % `self.index` + return "%s" % repr(self.index) class Len(TransformComparator): """Compares against len(item).""" @@ -460,7 +464,7 @@ def equals(self, item, comp): if len(missing) or len(extra): self.matched = matched self.missing = missing - self.extra = extra.keys() + self.extra = list(six.iterkeys(extra)) return False else: return True @@ -493,7 +497,7 @@ def __init__(self, value): self.value = EqSet(value) def transform(self, item): - return item.keys() + return list(six.iterkeys(item)) def expr(self, expr): return "%s.keys()" % expr @@ -503,7 +507,7 @@ class Dict(ValueComparator): def equals(self, item, comp): v = self.value - for c in (InstanceOf(dict), HasKeys(v.keys())): + for c in (InstanceOf(dict), HasKeys(list(six.iterkeys(v)))): if not comp.descend(item, c): return False @@ -532,10 +536,10 @@ def transform(self, item): return hasattr(item, self.attr) def expr(self, expr): - return "hasattr(%s, %s)" % (expr, `self.attr`) + return "hasattr(%s, %s)" % (expr, repr(self.attr)) def trans_args(self): - return `self.attr` + return repr(self.attr) class CmpAttr(TransformComparator): """Compare item.some_attribute.""" @@ -550,7 +554,7 @@ def expr(self, expr): return "%s.%s" % (expr, self.attr) def trans_args(self): - return `self.attr` + return repr(self.attr) class Attr(Comparator): """Check that item.some_attr exists and compare it to some value.""" @@ -583,7 +587,7 @@ def __init__(self, *args, **qargs): def equals(self, item, comp): v = self.value if isinstance(v, dict): - items = v.items() + items = six.iteritems(v) else: items = v for (attr, c) in items: @@ -608,7 +612,7 @@ def expr(self, expr): if args_s: args.append(args_s) kwargs_a = [("%s=%s" % (x[0], self.render_value(x[1]))) - for x in self.kwargs.items()] + for x in six.iteritems(self.kwargs)] if kwargs_a: args.append(", ".join(kwargs_a)) @@ -631,7 +635,7 @@ def render(self): return self.render_value(self.value) def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, `self.conds`) + return "%s(%s)" % (self.__class__.__name__, repr(self.conds)) class And(AndA): """As AndA but instead of passing in an array object, the argument list @@ -651,12 +655,12 @@ class Re(Comparator): """Check that item matches a regular expression (using re.search).""" def __init__(self, regex, flags=0): if type(regex) is str: - self.orig = "%s" % `regex` + self.orig = "%s" % repr(regex) if flags: self.orig += " (flags=%d)" % flags regex = re.compile(regex, flags) else: - self.orig = `regex` + self.orig = repr(regex) self.regex = regex def equals(self, item, comp): @@ -696,15 +700,15 @@ def render(self): return self.render_value(self.value) def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, `self.value`) + return "%s(%s)" % (self.__class__.__name__, repr(self.value)) class ArrayValues(ValueComparator): """ Compare each element of an array to the value """ def equals(self, item, comp): - return comp.descend(item, Slice(self.value, xrange(0, len(item)))) + return comp.descend(item, Slice(self.value, range(0, len(item)))) + class DictValues(ValueComparator): """ Compare each value in a dictionary to the value """ def equals(self, item, comp): - return comp.descend(item, Slice(self.value, item.keys())) - + return comp.descend(item, Slice(self.value, list(six.iterkeys(item)))) diff --git a/deep/test.py b/deep/test.py index 454990f..5896bc2 100644 --- a/deep/test.py +++ b/deep/test.py @@ -28,7 +28,7 @@ def DeepEq(self, first, second, msg=""): msg = "%s:\nExpected: %s\nActual : %s" % (msg, second, first) msg += "\nDiffered at " + res.render_full() - self.failUnless(not res, msg) + self.assertTrue(not res, msg) class TestCase(unittest.TestCase, TestCaseMix): """ A premixed TestCase class """ diff --git a/deep_test.py b/deep_test.py index 176f282..dbbd710 100755 --- a/deep_test.py +++ b/deep_test.py @@ -51,7 +51,7 @@ def test(self, case): if diff: msg += "\n" + diff.render_full() - case.failUnless(not diff, msg) + case.assertTrue(not diff, msg) class TestNotEqual(object): def __init__(self, i1, i2, path="", actual="", expected="", name=""): @@ -82,6 +82,12 @@ def test(self, case): class DeepTest(unittest.TestCase): def runTest(self): + import sys + if sys.version_info[0] == 2: + type_str = 'type' + else: + type_str = 'class' + tests = [E(1, 1, "1 == 1"), N(1, 2, "x", "1", "2", "1 != 2"), E((1,2), (1,2), "tuple diff"), @@ -93,8 +99,8 @@ def runTest(self): "[] ! Type int"), E(self, d.InstanceOf(unittest.TestCase), "self InstanceOf test"), N([], d.InstanceOf(int), "x", - "instance of ", - "instance of ", + "instance of <%s 'list'>" % type_str, + "instance of <%s 'int'>" % type_str, "[] ! InstanceOf int"), E([0,1], d.IndexedElem(1, 1), "List[1]"), N([0,1], d.IndexedElem(0, 1), "x[0]", "0", "1", "List[0]"), @@ -102,7 +108,7 @@ def runTest(self): N([0,1], d.List([0, 0]), "x[1]", "1", "0", "not list"), E([0,1], [0, 1], "auto list"), N([0,1], d.List([0, 0, 0]), "len(x)", "2", "3", "not list len"), - N(1, d.List([0, 0, 0]), "x", "instance of ", None, "not list type"), + N(1, d.List([0, 0, 0]), "x", "instance of <%s 'int'>" % type_str, None, "not list type"), E([1,0], d.EqSet([0, 1]), "eqset"), N([0,1], d.EqSet([0, 2]), "x as a set (==)", "1 matching element(s), extra: [1], missing: [2]", @@ -123,7 +129,7 @@ def runTest(self): N({"a" : 0, "c" : 1}, d.Dict({"a" : 1, "b" : 1}), 'x.keys() as a set (==)', None, None, "! Dict keys"), N(1, d.Dict({"a" : 1, "b" : 1}), - 'x', "instance of ", None, "! Dict type"), + 'x', "instance of <%s 'int'>" % type_str, None, "! Dict type"), E({"a" : 0, "b" : 1}, {"a" : 0, "b" : 1}, "auto Dict"), E(o, d.HasAttr("an_attr"), "attr"), N(o, d.HasAttr("another_attr"), "hasattr(x, 'another_attr')", "False", "True", "! attr"), @@ -150,7 +156,7 @@ def runTest(self): N(o, d.And(d.Attr("an_attr", 1), d.Attr("an_attr2", 3)), 'x.an_attr2', "2", "3", "! and 2"), E([1, 2], [d.Ignore(), 2], "ignore"), E("feRgal", d.Re("rga", re.IGNORECASE), "Re"), - N("feRgal", d.Re("rga", re.MULTILINE), "x", `"feRgal"`, "something matching 'rga' (flags=8)", "Re"), + N("feRgal", d.Re("rga", re.MULTILINE), "x", repr("feRgal"), "something matching 'rga' (flags=8)", "Re"), E(["abc", "ab", "a"], d.ArrayValues(d.Re("a")), "ArrayValues"), N(["abc", "ab", "a"], d.ArrayValues(d.Re("b")), "x[2]", "a".__repr__(), "something matching 'b'", "ArrayValues"), E(["abc", "ab", "a"], @@ -187,8 +193,8 @@ def runTest(self): "2 matching element(s)", "not eqset"), N([0,1], set([0, 1]), "x as a set (==)", - "instance of ", - "instance of ") + "instance of <%s 'list'>" % type_str, + "instance of <%s 'set'>" % type_str) ]) if hasattr(__builtins__, "frozenset"): @@ -199,8 +205,8 @@ def runTest(self): "2 matching element(s)", "not eqfrozenset"), N([0,1], frozenset([0, 1]), "x as a set (==)", - "instance of ", - "instance of ") + "instance of <%s 'list'>" % type_str, + "instance of <%s 'frozenset'>" % type_str) ]) # for t in (tests[-1],): @@ -215,7 +221,7 @@ def runTest(self): ex = None try: d.diff([0, 1], d.IndexedElem(2, None)) - except Exception, e: + except Exception as e: ex = e for pat in (r"examining x\[2\]", "IndexError"): diff --git a/setup.py b/setup.py index 7573cef..7812b3d 100755 --- a/setup.py +++ b/setup.py @@ -3,10 +3,11 @@ from distutils.core import setup setup(name='deep', - version='0.9', + version='0.9.dev', packages = ['deep'], author="Fergal Daly", author_email="fergal@esatclear.ie", + install_requires=['six'], description="Easy, flexible deep comparison and testing of structured data", url="http://code.google.com/p/python-deep/", download_url="http://code.google.com/p/python-deep/downloads/list", diff --git a/test_test.py b/test_test.py index 15b1e40..6b48ef3 100755 --- a/test_test.py +++ b/test_test.py @@ -21,7 +21,7 @@ class DeepTest(deep.test.TestCase): def testTest(self): - self.assertEquals(1, 1) + self.assertEqual(1, 1) self.DeepEq([1], [1], "wibble") if __name__ == '__main__': From 0767be1ca89ad6c5df00cf7b885dca38c185d3b4 Mon Sep 17 00:00:00 2001 From: Taha Jahangir Date: Sat, 5 Mar 2016 09:50:32 +0330 Subject: [PATCH 2/2] Add travis build script --- .travis.yml | 12 ++++++++++++ README.txt => README.md | 1 + deep_test.py | 10 ---------- setup.py | 2 +- test_test.py | 3 --- 5 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 .travis.yml rename README.txt => README.md (95%) diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..fb62a73 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: python +python: + - "2.6" + - "2.7" + - "3.2" + - "3.3" + - "3.4" + - "3.5" +# command to install dependencies +install: "python setup.py install" +# command to run tests +script: py.test diff --git a/README.txt b/README.md similarity index 95% rename from README.txt rename to README.md index eb3f5b5..0818333 100644 --- a/README.txt +++ b/README.md @@ -1,4 +1,5 @@ Flexible comparison of nested python datastrucures and objects. +[![Build Status](https://travis-ci.org/fergald/python-deep.svg)](https://travis-ci.org/fergald/python-deep) Composable "regular expressions" for data structures. diff --git a/deep_test.py b/deep_test.py index dbbd710..c09be2e 100755 --- a/deep_test.py +++ b/deep_test.py @@ -227,13 +227,3 @@ def runTest(self): for pat in (r"examining x\[2\]", "IndexError"): if not re.search(pat, str(ex)): self.fail("exception didn't match '%s':\n%s" % (pat, ex)) - - -if __name__ == '__main__': - suite = unittest.TestSuite() - suite.addTests([ DeepTest(), - DeepExc(), - ] - ) - unittest.TextTestRunner(verbosity=3).run(suite) - diff --git a/setup.py b/setup.py index 7812b3d..097dcf4 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/python -from distutils.core import setup +from setuptools import setup setup(name='deep', version='0.9.dev', diff --git a/test_test.py b/test_test.py index 6b48ef3..9a16ad9 100755 --- a/test_test.py +++ b/test_test.py @@ -23,6 +23,3 @@ class DeepTest(deep.test.TestCase): def testTest(self): self.assertEqual(1, 1) self.DeepEq([1], [1], "wibble") - -if __name__ == '__main__': - deep.test.main()