diff --git a/CHANGES.txt b/CHANGES.txt index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_Q0hBTkdFUy50eHQ=..3aefd4db0de3b5241e2fcab096873f3dd87fa594_Q0hBTkdFUy50eHQ= 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +Version 2.5.0 released 2012-03-29 + +* New item_sort_key option for encoder to allow fine grained control of sorted + output + Version 2.4.0 released 2012-03-06 * New bigint_as_string option for encoder to trade JavaScript number precision diff --git a/conf.py b/conf.py index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_Y29uZi5weQ==..3aefd4db0de3b5241e2fcab096873f3dd87fa594_Y29uZi5weQ== 100644 --- a/conf.py +++ b/conf.py @@ -42,5 +42,5 @@ # other places throughout the built documents. # # The short X.Y version. -version = '2.4' +version = '2.5' # The full version, including alpha/beta/rc tags. @@ -46,5 +46,5 @@ # The full version, including alpha/beta/rc tags. -release = '2.4.0' +release = '2.5.0' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff --git a/index.rst b/index.rst index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_aW5kZXgucnN0..3aefd4db0de3b5241e2fcab096873f3dd87fa594_aW5kZXgucnN0 100644 --- a/index.rst +++ b/index.rst @@ -129,7 +129,7 @@ Basic Usage ----------- -.. function:: dump(obj, fp[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, **kw]]]]]]]]]]]]]) +.. function:: dump(obj, fp[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, sort_keys[, item_sort_key[, **kw]]]]]]]]]]]]]]]]) Serialize *obj* as a JSON formatted stream to *fp* (a ``.write()``-supporting file-like object). @@ -189,7 +189,10 @@ .. versionchanged:: 2.2.0 The default of *use_decimal* changed to ``True`` in 2.2.0. - If *namedtuple_as_object* is true (default: ``True``), - objects with ``_asdict()`` methods will be encoded - as JSON objects. + If *namedtuple_as_object* is true (default: ``True``), + objects with ``_asdict()`` methods will be encoded + as JSON objects. + + .. versionchanged:: 2.2.0 + *namedtuple_as_object* is new in 2.2.0. @@ -195,4 +198,11 @@ - .. versionchanged:: 2.2.0 - *namedtuple_as_object* is new in 2.2.0. + .. versionchanged:: 2.3.0 + *namedtuple_as_object* no longer requires that these objects be + subclasses of :class:`tuple`. + + If *tuple_as_array* is true (default: ``True``), + :class:`tuple` (and subclasses) will be encoded as JSON arrays. + + .. versionchanged:: 2.2.0 + *tuple_as_array* is new in 2.2.0. @@ -198,5 +208,9 @@ - .. versionchanged:: 2.3.0 - *namedtuple_as_object* no longer requires that these objects be - subclasses of :class:`tuple`. + If *bigint_as_string* is true (default: ``False``), :class:`int`` ``2**53`` + and higher or lower than ``-2**53`` will be encoded as strings. This is to + avoid the rounding that happens in Javascript otherwise. Note that this + option loses type information, so use with extreme caution. + + .. versionchanged:: 2.4.0 + *bigint_as_string* is new in 2.4.0. @@ -202,4 +216,5 @@ - If *tuple_as_array* is true (default: ``True``), - :class:`tuple` (and subclasses) will be encoded as JSON arrays. + If *sort_keys* is true (not the default), then the output of dictionaries + will be sorted by key; this is useful for regression tests to ensure that + JSON serializations can be compared on a day-to-day basis. @@ -205,6 +220,11 @@ - .. versionchanged:: 2.2.0 - *tuple_as_array* is new in 2.2.0. + If *item_sort_key* is a callable (not the default), then the output of + dictionaries will be sorted with it. The callable will be used like this: + ``sorted(dct.items(), key=sort_item_key)``. This option takes precedence + over *sort_keys*. + + .. versionchanged:: 2.5.0 + *item_sort_key* is new in 2.5.0. .. note:: @@ -213,7 +233,7 @@ container protocol to delimit them. -.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, **kw]]]]]]]]]]]]]) +.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, sort_keys[, item_sort_key[, **kw]]]]]]]]]]]]]]]]) Serialize *obj* to a JSON formatted :class:`str`. @@ -410,7 +430,7 @@ :exc:`JSONDecodeError` will be raised if the given JSON document is not valid. -.. class:: JSONEncoder([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array]]]]]]]]]]]]) +.. class:: JSONEncoder([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, item_sort_key]]]]]]]]]]]]]) Extensible JSON encoder for Python data structures. @@ -465,6 +485,14 @@ will be sorted by key; this is useful for regression tests to ensure that JSON serializations can be compared on a day-to-day basis. + If *item_sort_key* is a callable (not the default), then the output of + dictionaries will be sorted with it. The callable will be used like this: + ``sorted(dct.items(), key=sort_item_key)``. This option takes precedence + over *sort_keys*. + + .. versionchanged:: 2.5.0 + *item_sort_key* is new in 2.5.0. + If *indent* is a string, then JSON array elements and object members will be pretty-printed with a newline followed by that string repeated for each level of nesting. ``None`` (the default) selects the most compact @@ -504,6 +532,15 @@ .. versionchanged:: 2.2.0 *tuple_as_array* is new in 2.2.0. + If *bigint_as_string* is true (default: ``False``), :class:`int`` ``2**53`` + and higher or lower than ``-2**53`` will be encoded as strings. This is to + avoid the rounding that happens in Javascript otherwise. Note that this + option loses type information, so use with extreme caution. + + .. versionchanged:: 2.4.0 + *bigint_as_string* is new in 2.4.0. + + .. method:: default(o) Implement this method in a subclass such that it returns a serializable @@ -544,7 +581,7 @@ Note that :meth:`encode` has much better performance than :meth:`iterencode`. -.. class:: JSONEncoderForHTML([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default]]]]]]]]]) +.. class:: JSONEncoderForHTML([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, item_sort_key]]]]]]]]]]]]]) Subclass of :class:`JSONEncoder` that escapes &, <, and > for embedding in HTML. diff --git a/setup.py b/setup.py index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_c2V0dXAucHk=..3aefd4db0de3b5241e2fcab096873f3dd87fa594_c2V0dXAucHk= 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ DistutilsPlatformError IS_PYPY = hasattr(sys, 'pypy_translation_info') -VERSION = '2.4.0' +VERSION = '2.5.0' DESCRIPTION = "Simple, fast, extensible JSON encoder/decoder for Python" LONG_DESCRIPTION = open('README.rst', 'r').read() diff --git a/simplejson/__init__.py b/simplejson/__init__.py index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_c2ltcGxlanNvbi9fX2luaXRfXy5weQ==..3aefd4db0de3b5241e2fcab096873f3dd87fa594_c2ltcGxlanNvbi9fX2luaXRfXy5weQ== 100644 --- a/simplejson/__init__.py +++ b/simplejson/__init__.py @@ -97,7 +97,7 @@ $ echo '{ 1.2:3.4}' | python -m simplejson.tool Expecting property name: line 1 column 2 (char 2) """ -__version__ = '2.4.0' +__version__ = '2.5.0' __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', @@ -101,7 +101,7 @@ __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', - 'OrderedDict', 'simple_first' + 'OrderedDict', 'simple_first', ] __author__ = 'Bob Ippolito <bob@redivi.com>' @@ -146,7 +146,8 @@ allow_nan=True, cls=None, indent=None, separators=None, encoding='utf-8', default=None, use_decimal=True, namedtuple_as_object=True, tuple_as_array=True, - bigint_as_string=False, item_sort_key=None, **kw): + bigint_as_string=False, sort_keys=False, item_sort_key=None, + **kw): """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object). @@ -203,7 +204,11 @@ If specified, *item_sort_key* is a callable used to sort the items in each dictionary. This is useful if you want to sort items other than - in alphabetical order by key. + in alphabetical order by key. This option takes precedence over + *sort_keys*. + + If *sort_keys* is true (default: ``False``), the output of dictionaries + will be sorted by item. To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with @@ -228,6 +233,7 @@ namedtuple_as_object=namedtuple_as_object, tuple_as_array=tuple_as_array, bigint_as_string=bigint_as_string, + sort_keys=sort_keys, item_sort_key=item_sort_key, **kw).iterencode(obj) # could accelerate with writelines in some versions of Python, at @@ -240,7 +246,7 @@ allow_nan=True, cls=None, indent=None, separators=None, encoding='utf-8', default=None, use_decimal=True, namedtuple_as_object=True, tuple_as_array=True, - bigint_as_string=False, item_sort_key=None, + bigint_as_string=False, sort_keys=False, item_sort_key=None, **kw): """Serialize ``obj`` to a JSON formatted ``str``. @@ -287,9 +293,9 @@ If *tuple_as_array* is true (default: ``True``), :class:`tuple` (and subclasses) will be encoded as JSON arrays. - If bigint_as_string is true (not the default), ints 2**53 and higher + If *bigint_as_string* is true (not the default), ints 2**53 and higher or lower than -2**53 will be encoded as strings. This is to avoid the rounding that happens in Javascript otherwise. If specified, *item_sort_key* is a callable used to sort the items in each dictionary. This is useful if you want to sort items other than @@ -291,9 +297,13 @@ or lower than -2**53 will be encoded as strings. This is to avoid the rounding that happens in Javascript otherwise. If specified, *item_sort_key* is a callable used to sort the items in each dictionary. This is useful if you want to sort items other than - in alphabetical order by key. + in alphabetical order by key. This option takes precendence over + *sort_keys*. + + If *sort_keys* is true (default: ``False``), the output of dictionaries + will be sorted by item. To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with @@ -306,7 +316,8 @@ cls is None and indent is None and separators is None and encoding == 'utf-8' and default is None and use_decimal and namedtuple_as_object and tuple_as_array - and not bigint_as_string and not item_sort_key and not kw): + and not bigint_as_string and not sort_keys + and not item_sort_key and not kw): return _default_encoder.encode(obj) if cls is None: cls = JSONEncoder @@ -318,6 +329,7 @@ namedtuple_as_object=namedtuple_as_object, tuple_as_array=tuple_as_array, bigint_as_string=bigint_as_string, + sort_keys=sort_keys, item_sort_key=item_sort_key, **kw).encode(obj) @@ -495,4 +507,4 @@ """Helper function to pass to item_sort_key to sort simple elements to the top, then container elements. """ - return isinstance(kv[1],(list,dict,tuple)), kv[0] + return (isinstance(kv[1], (list, dict, tuple)), kv[0]) diff --git a/simplejson/_speedups.c b/simplejson/_speedups.c index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_c2ltcGxlanNvbi9fc3BlZWR1cHMuYw==..3aefd4db0de3b5241e2fcab096873f3dd87fa594_c2ltcGxlanNvbi9fc3BlZWR1cHMuYw== 100644 --- a/simplejson/_speedups.c +++ b/simplejson/_speedups.c @@ -2361,9 +2361,9 @@ */ } - if (PyCallable_Check(s->item_sort_key)) { + if (PyCallable_Check(s->item_sort_key)) { if (PyDict_CheckExact(dct)) items = PyDict_Items(dct); else items = PyMapping_Items(dct); PyObject_CallMethod(items, "sort", "OO", Py_None, s->item_sort_key); @@ -2365,9 +2365,9 @@ if (PyDict_CheckExact(dct)) items = PyDict_Items(dct); else items = PyMapping_Items(dct); PyObject_CallMethod(items, "sort", "OO", Py_None, s->item_sort_key); - } + } else if (PyObject_IsTrue(s->sort_keys)) { /* First sort the keys then replace them with (key, value) tuples. */ Py_ssize_t i, nitems; diff --git a/simplejson/encoder.py b/simplejson/encoder.py index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_c2ltcGxlanNvbi9lbmNvZGVyLnB5..3aefd4db0de3b5241e2fcab096873f3dd87fa594_c2ltcGxlanNvbi9lbmNvZGVyLnB5 100644 --- a/simplejson/encoder.py +++ b/simplejson/encoder.py @@ -358,6 +358,8 @@ str=str, tuple=tuple, ): + if _item_sort_key and not callable(_item_sort_key): + raise TypeError("item_sort_key must be None or callable") def _iterencode_list(lst, _current_indent_level): if not lst: diff --git a/simplejson/tests/test_item_sort_key.py b/simplejson/tests/test_item_sort_key.py index 46a2e2f798a0e41dd6a312ec22fb86d5887d4733_c2ltcGxlanNvbi90ZXN0cy90ZXN0X2l0ZW1fc29ydF9rZXkucHk=..3aefd4db0de3b5241e2fcab096873f3dd87fa594_c2ltcGxlanNvbi90ZXN0cy90ZXN0X2l0ZW1fc29ydF9rZXkucHk= 100644 --- a/simplejson/tests/test_item_sort_key.py +++ b/simplejson/tests/test_item_sort_key.py @@ -5,6 +5,6 @@ class TestItemSortKey(TestCase): def test_simple_first(self): - a={'a': 1, 'c': 5, 'jack': 'jill', 'pick': 'axe', 'array': [1, 5, 6, 9], 'tuple': (83, 12, 3), 'crate': 'dog', 'zeak': 'oh'} + a = {'a': 1, 'c': 5, 'jack': 'jill', 'pick': 'axe', 'array': [1, 5, 6, 9], 'tuple': (83, 12, 3), 'crate': 'dog', 'zeak': 'oh'} self.assertEquals( '{"a": 1, "c": 5, "crate": "dog", "jack": "jill", "pick": "axe", "zeak": "oh", "array": [1, 5, 6, 9], "tuple": [83, 12, 3]}', @@ -9,5 +9,5 @@ self.assertEquals( '{"a": 1, "c": 5, "crate": "dog", "jack": "jill", "pick": "axe", "zeak": "oh", "array": [1, 5, 6, 9], "tuple": [83, 12, 3]}', - json.dumps(a,item_sort_key=json.simple_first)) + json.dumps(a, item_sort_key=json.simple_first)) def test_case(self): @@ -12,5 +12,5 @@ def test_case(self): - a={'a': 1, 'c': 5, 'Jack': 'jill', 'pick': 'axe', 'Array': [1, 5, 6, 9], 'tuple': (83, 12, 3), 'crate': 'dog', 'zeak': 'oh'} + a = {'a': 1, 'c': 5, 'Jack': 'jill', 'pick': 'axe', 'Array': [1, 5, 6, 9], 'tuple': (83, 12, 3), 'crate': 'dog', 'zeak': 'oh'} self.assertEquals( '{"Array": [1, 5, 6, 9], "Jack": "jill", "a": 1, "c": 5, "crate": "dog", "pick": "axe", "tuple": [83, 12, 3], "zeak": "oh"}', @@ -15,5 +15,5 @@ self.assertEquals( '{"Array": [1, 5, 6, 9], "Jack": "jill", "a": 1, "c": 5, "crate": "dog", "pick": "axe", "tuple": [83, 12, 3], "zeak": "oh"}', - json.dumps(a,item_sort_key=itemgetter(0))) + json.dumps(a, item_sort_key=itemgetter(0))) self.assertEquals( '{"a": 1, "Array": [1, 5, 6, 9], "c": 5, "crate": "dog", "Jack": "jill", "pick": "axe", "tuple": [83, 12, 3], "zeak": "oh"}', @@ -18,4 +18,3 @@ self.assertEquals( '{"a": 1, "Array": [1, 5, 6, 9], "c": 5, "crate": "dog", "Jack": "jill", "pick": "axe", "tuple": [83, 12, 3], "zeak": "oh"}', - json.dumps(a,item_sort_key=lambda kv: kv[0].lower())) - + json.dumps(a, item_sort_key=lambda kv: kv[0].lower()))