diff options
-rw-r--r-- | CHANGES.txt | 5 | ||||
-rw-r--r-- | conf.py | 4 | ||||
-rw-r--r-- | index.rst | 69 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | simplejson/__init__.py | 30 | ||||
-rw-r--r-- | simplejson/_speedups.c | 4 | ||||
-rw-r--r-- | simplejson/encoder.py | 2 | ||||
-rw-r--r-- | simplejson/tests/test_item_sort_key.py | 11 |
8 files changed, 91 insertions, 36 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 94e4434..b03ba76 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 @@ -42,9 +42,9 @@ copyright = '2011, Bob Ippolito' # 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. -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: @@ -129,7 +129,7 @@ Using :mod:`simplejson.tool` from the shell to validate and pretty-print:: 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,22 +189,42 @@ Basic Usage .. 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. + .. 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`. + .. 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. - 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. - .. 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. + + 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. + + 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 @@ Basic Usage 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 @@ Encoders and decoders :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 @@ Encoders and decoders 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 @@ Encoders and decoders .. 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 @@ Encoders and decoders 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. @@ -7,7 +7,7 @@ from distutils.errors import CCompilerError, DistutilsExecError, \ 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 3de54de..eb38b46 100644 --- a/simplejson/__init__.py +++ b/simplejson/__init__.py @@ -97,11 +97,11 @@ Using simplejson.tool from the shell to validate and pretty-print:: $ 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', - 'OrderedDict', 'simple_first' + 'OrderedDict', 'simple_first', ] __author__ = 'Bob Ippolito <bob@redivi.com>' @@ -146,7 +146,8 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, 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 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, 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 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, 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 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, 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,13 +293,17 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, 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 - 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 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, 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 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, 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 @@ def simple_first(kv): """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 f6eef80..e8d6563 100644 --- a/simplejson/_speedups.c +++ b/simplejson/_speedups.c @@ -2361,13 +2361,13 @@ encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ss */ } - 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); - } + } 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 1790e70..05dd05c 100644 --- a/simplejson/encoder.py +++ b/simplejson/encoder.py @@ -358,6 +358,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, 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 f630d8e..83bea1e 100644 --- a/simplejson/tests/test_item_sort_key.py +++ b/simplejson/tests/test_item_sort_key.py @@ -5,17 +5,16 @@ from operator import itemgetter 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]}', - json.dumps(a,item_sort_key=json.simple_first)) + json.dumps(a, item_sort_key=json.simple_first)) 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"}', - 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"}', - json.dumps(a,item_sort_key=lambda kv: kv[0].lower())) - + json.dumps(a, item_sort_key=lambda kv: kv[0].lower())) |