diff options
-rw-r--r-- | README.txt | 2 | ||||
-rwxr-xr-x | json_diff.py | 50 | ||||
-rw-r--r-- | setup.py | 6 | ||||
-rw-r--r-- | test_json_diff.py | 42 | ||||
-rw-r--r-- | test_strings.py | 55 |
5 files changed, 82 insertions, 73 deletions
@@ -3,5 +3,7 @@ with the result. Allows exclusion of some keys from the comparison, or in other way to include only some keys.
The development repository is at https://gitorious.org/json_diff/mainline
+Patches and pull requests are welcome, but please keep the script compatible
+with python 2.4.
Released under MIT/X11 license.
diff --git a/json_diff.py b/json_diff.py index 8cda9fd..261acdd 100755 --- a/json_diff.py +++ b/json_diff.py @@ -22,27 +22,28 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from __future__ import division, absolute_import, print_function, unicode_literals -import json, sys -# import pdb +try: + import json +except ImportError: + import simplejson as json import logging from optparse import OptionParser __author__ = "Matěj Cepl" -__version__ = "0.9.0" +__version__ = "0.9.2" logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level=logging.INFO) STYLE_MAP = { - "_append": "append_class", - "_remove": "remove_class", - "_update": "update_class" + u"_append": u"append_class", + u"_remove": u"remove_class", + u"_update": u"update_class" } INTERNAL_KEYS = set(STYLE_MAP.keys()) -LEVEL_INDENT = " " +LEVEL_INDENT = u" " -out_str_template = """ +out_str_template = u""" <!DOCTYPE html> <html lang='en'> <meta charset="utf-8" /> @@ -67,9 +68,6 @@ td { %s """ -# I would love to have better solution for this ... -if sys.version_info[0] == 3: unicode = str - def is_scalar(value): """ Primitive version, relying on the fact that JSON cannot @@ -85,16 +83,16 @@ class HTMLFormatter(object): def _generate_page(self, in_dict, title="json_diff result"): out_str = out_str_template % (title, title, self._format_dict(in_dict)) - out_str += """</table> + out_str += u"""</table> </body> </html>""" return out_str def _format_item(self, item, index, typch, level=0): - level_str = ("<td>" + LEVEL_INDENT + "</td>") * level + level_str = (u"<td>" + LEVEL_INDENT + u"</td>") * level if is_scalar(item): - out_str = ("<tr>\n %s<td class='%s'>%s = %s</td>\n </tr>\n" % + out_str = (u"<tr>\n %s<td class='%s'>%s = %s</td>\n </tr>\n" % (level_str, STYLE_MAP[typch], index, unicode(item))) elif isinstance(item, (list, tuple)): out_str = self._format_array(item, typch, level + 1) @@ -139,12 +137,12 @@ class Comparator(object): if fn1: try: self.obj1 = json.load(fn1) - except (TypeError, OverflowError, ValueError) as exc: + except (TypeError, OverflowError, ValueError), exc: raise BadJSONError("Cannot decode object from JSON.\n%s" % unicode(exc)) if fn2: try: self.obj2 = json.load(fn2) - except (TypeError, OverflowError, ValueError) as exc: + except (TypeError, OverflowError, ValueError), exc: raise BadJSONError("Cannot decode object from JSON\n%s" % unicode(exc)) self.excluded_attributes = excluded_attrs self.included_attributes = included_attrs @@ -236,22 +234,22 @@ class Comparator(object): inters = min(len(old_arr), len(new_arr)) # this is the smaller length result = { - "_append": {}, - "_remove": {}, - "_update": {} + u"_append": {}, + u"_remove": {}, + u"_update": {} } for idx in range(inters): res = self._compare_elements(old_arr[idx], new_arr[idx]) if res is not None: - result['_update'][idx] = res + result[u'_update'][idx] = res # the rest of the larger array if (inters == len(old_arr)): for idx in range(inters, len(new_arr)): - result['_append'][idx] = new_arr[idx] + result[u'_append'][idx] = new_arr[idx] else: for idx in range(inters, len(old_arr)): - result['_remove'][idx] = old_arr[idx] + result[u'_remove'][idx] = old_arr[idx] # Clear out unused keys in result out_result = {} @@ -287,14 +285,14 @@ class Comparator(object): for name in keys: # old_obj is missing if name not in old_obj: - result['_append'][name] = new_obj[name] + result[u'_append'][name] = new_obj[name] # new_obj is missing elif name not in new_obj: - result['_remove'][name] = old_obj[name] + result[u'_remove'][name] = old_obj[name] else: res = self._compare_elements(old_obj[name], new_obj[name]) if res is not None: - result['_update'][name] = res + result[u'_update'][name] = res return self._filter_results(result) @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- from distutils.core import setup +import json_diff setup( name = 'json_diff', - version = '0.9.1', + version = '%s' % json_diff.__version__, description = 'Generates diff between two JSON files', author = 'Matěj Cepl', author_email = 'mcepl@redhat.com', url = 'https://gitorious.org/json_diff/mainline', - download_url = "http://mcepl.fedorapeople.org/scripts/json_diff-0.9.1.tar.gz", + download_url = "http://mcepl.fedorapeople.org/scripts/json_diff-%s.tar.gz" % json_diff.__version__, py_modules = ['json_diff', 'test_json_diff', 'test_strings'], package_data = ['test/*'], long_description = """Compares two JSON files (http://json.org) and @@ -17,7 +18,6 @@ keys from the comparison, or in other way to include only some keys.""", keywords = ['json', 'diff'], classifiers = [ "Programming Language :: Python", - "Programming Language :: Python :: 3", "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Information Technology", diff --git a/test_json_diff.py b/test_json_diff.py index 855c170..1b601a4 100644 --- a/test_json_diff.py +++ b/test_json_diff.py @@ -2,15 +2,19 @@ """ PyUnit unit tests """ -from __future__ import division, absolute_import, unicode_literals -import unittest, sys -if sys.version_info[0] == 3: unicode = str -import json +import unittest +try: + import json +except ImportError: + import simplejson as json import json_diff -from io import StringIO +from StringIO import StringIO import codecs -from test_strings import * #@UnusedWildImport +from test_strings import ARRAY_DIFF, ARRAY_NEW, ARRAY_OLD, \ + NESTED_DIFF, NESTED_DIFF_EXCL, NESTED_DIFF_INCL, NESTED_NEW, NESTED_OLD, \ + NO_JSON_NEW, NO_JSON_OLD, SIMPLE_ARRAY_DIFF, SIMPLE_ARRAY_NEW, \ + SIMPLE_ARRAY_OLD, SIMPLE_DIFF, SIMPLE_DIFF_HTML, SIMPLE_NEW, SIMPLE_OLD class OurTestCase(unittest.TestCase): def _run_test(self, oldf, newf, difff, msg="", inc=(), exc=()): @@ -35,7 +39,7 @@ class OurTestCase(unittest.TestCase): "\n\nexpected = %s\n\nobserved = %s" % (expected, diff)) -class TestBasicJSONHappyPath(OurTestCase): +class TestBasicJSON(OurTestCase): def test_empty(self): diffator = json_diff.Comparator({}, {}) diff = diffator.compare_dicts() @@ -56,16 +60,16 @@ class TestBasicJSONHappyPath(OurTestCase): '{"_update": {"a": false}}', "Booleans") def test_integer(self): - self._run_test_strings('{"a": 1}', '{"a": 2}', - '{"_update": {"a": 2}}', "Integers") + self._run_test_strings(u'{"a": 1}', '{"a": 2}', + u'{"_update": {"a": 2}}', "Integers") def test_float(self): - self._run_test_strings('{"a": 1.0}', '{"a": 1.1}', - '{"_update": {"a": 1.1}}', "Floats") + self._run_test_strings(u'{"a": 1.0}', '{"a": 1.1}', + u'{"_update": {"a": 1.1}}', "Floats") def test_int_to_float(self): - self._run_test_strings('{"a": 1}', '{"a": 1.0}', - '{"_update": {"a": 1.0}}', "Integer changed to float") + self._run_test_strings(u'{"a": 1}', '{"a": 1.0}', + u'{"_update": {"a": 1.0}}', "Integer changed to float") def test_simple(self): self._run_test_strings(SIMPLE_OLD, SIMPLE_NEW, SIMPLE_DIFF, @@ -80,6 +84,12 @@ class TestBasicJSONHappyPath(OurTestCase): self._run_test_strings(SIMPLE_ARRAY_OLD, SIMPLE_ARRAY_NEW, SIMPLE_ARRAY_DIFF, "Simple array objects diff.") + def test_another_array(self): + self._run_test_strings(ARRAY_OLD, ARRAY_NEW, + ARRAY_DIFF, "Array objects diff.") + + +class TestHappyPath(OurTestCase): def test_realFile(self): self._run_test(open("test/old.json"), open("test/new.json"), open("test/diff.json"), "Simply nested objects (from file) diff.") @@ -100,19 +110,19 @@ class TestBasicJSONHappyPath(OurTestCase): self._run_test_strings(NESTED_OLD, NESTED_NEW, NESTED_DIFF_INCL, "Nested objects diff.", inc=("nome",)) -class TestBasicJSONSadPath(OurTestCase): +class TestadPath(OurTestCase): def test_no_JSON(self): self.assertRaises(json_diff.BadJSONError, json_diff.Comparator, StringIO(NO_JSON_OLD), StringIO(NO_JSON_NEW)) def test_bad_JSON_no_hex(self): self.assertRaises(json_diff.BadJSONError, self._run_test_strings, - '{"a": 0x1}', '{"a": 2}', '{"_update": {"a": 2}}', + u'{"a": 0x1}', '{"a": 2}', u'{"_update": {"a": 2}}', "Hex numbers not supported") def test_bad_JSON_no_octal(self): self.assertRaises(json_diff.BadJSONError, self._run_test_strings, - '{"a": 01}', '{"a": 2}', '{"_update": {"a": 2}}', + u'{"a": 01}', '{"a": 2}', u'{"_update": {"a": 2}}', "Octal numbers not supported") #class TestPiglitData(OurTestCase): diff --git a/test_strings.py b/test_strings.py index c96db7a..1935912 100644 --- a/test_strings.py +++ b/test_strings.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- -from __future__ import division, absolute_import, unicode_literals -NO_JSON_OLD = """ +NO_JSON_OLD = u""" THIS IS NOT A JSON STRING """ -NO_JSON_NEW = """ +NO_JSON_NEW = u""" AND THIS NEITHER """ -SIMPLE_OLD = """ +SIMPLE_OLD = u""" { "a": 1, "b": true, @@ -17,7 +16,7 @@ SIMPLE_OLD = """ } """ -SIMPLE_NEW = """ +SIMPLE_NEW = u""" { "b": false, "c": "Maruška", @@ -25,7 +24,7 @@ SIMPLE_NEW = """ } """ -SIMPLE_DIFF = """ +SIMPLE_DIFF = u""" { "_append": { "d": "přidáno" @@ -40,7 +39,7 @@ SIMPLE_DIFF = """ } """ -SIMPLE_DIFF_HTML=""" +SIMPLE_DIFF_HTML = u""" <!DOCTYPE html> <html lang='en'> <meta charset="utf-8" /> @@ -76,19 +75,19 @@ color: navy; </html> """ -SIMPLE_ARRAY_OLD = """ +SIMPLE_ARRAY_OLD = u""" { "a": [ 1 ] } """ -SIMPLE_ARRAY_NEW = """ +SIMPLE_ARRAY_NEW = u""" { "a": [ 1, 2 ] } """ -SIMPLE_ARRAY_DIFF = """ +SIMPLE_ARRAY_DIFF = u""" { "_update": { "a": { @@ -100,7 +99,7 @@ SIMPLE_ARRAY_DIFF = """ } """ -NESTED_OLD = """ +NESTED_OLD = u""" { "a": 1, "b": 2, @@ -113,7 +112,7 @@ NESTED_OLD = """ } """ -NESTED_NEW = """ +NESTED_NEW = u""" { "a": 2, "c": 3, @@ -123,7 +122,7 @@ NESTED_NEW = """ } """ -NESTED_DIFF = """ +NESTED_DIFF = u""" { "_append": { "c": 3 @@ -145,7 +144,7 @@ NESTED_DIFF = """ } """ -NESTED_DIFF_EXCL = """ +NESTED_DIFF_EXCL = u""" { "_append": { "c": 3 @@ -162,7 +161,7 @@ NESTED_DIFF_EXCL = """ } """ -NESTED_DIFF_INCL = """ +NESTED_DIFF_INCL = u""" { "_update": { "child": { @@ -174,7 +173,7 @@ NESTED_DIFF_INCL = """ } """ -ARRAY_OLD = """ +ARRAY_OLD = u""" { "a": 1, "b": 2, @@ -184,7 +183,7 @@ ARRAY_OLD = """ } """ -ARRAY_NEW = """ +ARRAY_NEW = u""" { "a": 1, "children": [ @@ -194,20 +193,20 @@ ARRAY_NEW = """ } """ -ARRAY_DIFF = """ -{ - "_remove": { - "b": 2 - }, +ARRAY_DIFF = u""" + { "_append": { "c": 3 }, + "_remove": { + "b": 2 + }, "_update": { - "children": [ - "Pepíček", - "Tonička", - "Maruška" - ] + "children": { + "_update": { + "1": "Tonička" + } + } } } -""" +"""
\ No newline at end of file |