diff --git a/CHANGES.txt b/CHANGES.txt index 76fb7d24552c91406b555b14e727cd5e838cc017_Q0hBTkdFUy50eHQ=..9d88e49d1b73e81558e2c90938ca2cc480e90664_Q0hBTkdFUy50eHQ= 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ Version 2.2.0 released 2011-XX-XX +* use_decimal default for encoding (dump, dumps, JSONEncoder) is now True +* tuple encoding as JSON objects can be turned off with new + tuple_as_array=False option. + https://github.com/simplejson/simplejson/pull/6 * namedtuple (or other tuple subclasses with _asdict methods) are now @@ -3,5 +7,6 @@ * namedtuple (or other tuple subclasses with _asdict methods) are now - encoded as JSON objects rather than arrays. + encoded as JSON objects rather than arrays by default. Can be disabled + and treated as a tuple with the new namedtuple_as_object=False option. https://github.com/simplejson/simplejson/pull/6 * JSONDecodeError is now raised instead of ValueError when a document ends with an opening quote and the C speedups are in use. diff --git a/conf.py b/conf.py index 76fb7d24552c91406b555b14e727cd5e838cc017_Y29uZi5weQ==..9d88e49d1b73e81558e2c90938ca2cc480e90664_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.1' +version = '2.2' # The full version, including alpha/beta/rc tags. @@ -46,5 +46,5 @@ # The full version, including alpha/beta/rc tags. -release = '2.1.7' +release = '2.2.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 76fb7d24552c91406b555b14e727cd5e838cc017_aW5kZXgucnN0..9d88e49d1b73e81558e2c90938ca2cc480e90664_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[, **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[, **kw]]]]]]]]]]]]]) Serialize *obj* as a JSON formatted stream to *fp* (a ``.write()``-supporting file-like object). @@ -180,9 +180,9 @@ :meth:`default` method to serialize additional types), specify it with the *cls* kwarg. - If *use_decimal* is true (default: ``False``) then :class:`decimal.Decimal` + If *use_decimal* is true (default: ``True``) then :class:`decimal.Decimal` will be natively serialized to JSON with full precision. .. versionchanged:: 2.1.0 *use_decimal* is new in 2.1.0. @@ -184,8 +184,24 @@ will be natively serialized to JSON with full precision. .. versionchanged:: 2.1.0 *use_decimal* is new in 2.1.0. + .. versionchanged:: 2.2.0 + The default of *use_decimal* changed to ``True`` in 2.2.0. + + If *namedtuple_as_object* is true (default: ``True``), + :class:`tuple` subclasses with ``_asdict()`` methods will be encoded + as JSON objects. + + .. versionchanged:: 2.2.0 + *namedtuple_as_object* is new in 2.2.0. + + 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. + .. note:: JSON is not a framed protocol so unlike :mod:`pickle` or :mod:`marshal` it @@ -193,7 +209,7 @@ container protocol to delimit them. -.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, **kw]]]]]]]]]]]) +.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, **kw]]]]]]]]]]]]]) Serialize *obj* to a JSON formatted :class:`str`. @@ -390,7 +406,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]]]]]]]]]) +.. class:: JSONEncoder([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array]]]]]]]]]]]]) Extensible JSON encoder for Python data structures. @@ -467,6 +483,18 @@ into unicode using that encoding prior to JSON-encoding. The default is ``'utf-8'``. + If *namedtuple_as_object* is true (default: ``True``), + :class:`tuple` subclasses with ``_asdict()`` methods will be encoded + as JSON objects. + + .. versionchanged:: 2.2.0 + *namedtuple_as_object* is new in 2.2.0. + + 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. .. method:: default(o) diff --git a/setup.py b/setup.py index 76fb7d24552c91406b555b14e727cd5e838cc017_c2V0dXAucHk=..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2V0dXAucHk= 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ DistutilsPlatformError IS_PYPY = hasattr(sys, 'pypy_translation_info') -VERSION = '2.1.7' +VERSION = '2.2.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 76fb7d24552c91406b555b14e727cd5e838cc017_c2ltcGxlanNvbi9fX2luaXRfXy5weQ==..9d88e49d1b73e81558e2c90938ca2cc480e90664_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.1.7' +__version__ = '2.2.0' __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', @@ -135,8 +135,10 @@ separators=None, encoding='utf-8', default=None, - use_decimal=False, + use_decimal=True, + namedtuple_as_object=True, + tuple_as_array=True, ) def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, @@ -139,8 +141,10 @@ ) 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=False, **kw): + encoding='utf-8', default=None, use_decimal=True, + namedtuple_as_object=True, tuple_as_array=True, + **kw): """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object). @@ -179,6 +183,6 @@ ``default(obj)`` is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. - If *use_decimal* is true (default: ``False``) then decimal.Decimal + If *use_decimal* is true (default: ``True``) then decimal.Decimal will be natively serialized to JSON with full precision. @@ -183,5 +187,12 @@ will be natively serialized to JSON with full precision. + If *namedtuple_as_object* is true (default: ``True``), + :class:`tuple` subclasses with ``_asdict()`` methods will be encoded + as JSON objects. + + If *tuple_as_array* is true (default: ``True``), + :class:`tuple` (and subclasses) will be encoded as JSON arrays. + To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with the ``cls`` kwarg. @@ -191,8 +202,8 @@ if (not skipkeys and ensure_ascii and check_circular and allow_nan and cls is None and indent is None and separators is None and - encoding == 'utf-8' and default is None and not use_decimal - and not kw): + encoding == 'utf-8' and default is None and use_decimal + and namedtuple_as_object and tuple_as_array and not kw): iterable = _default_encoder.iterencode(obj) else: if cls is None: @@ -200,7 +211,10 @@ iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, indent=indent, separators=separators, encoding=encoding, - default=default, use_decimal=use_decimal, **kw).iterencode(obj) + default=default, use_decimal=use_decimal, + namedtuple_as_object=namedtuple_as_object, + tuple_as_array=tuple_as_array, + **kw).iterencode(obj) # could accelerate with writelines in some versions of Python, at # a debuggability cost for chunk in iterable: @@ -209,7 +223,10 @@ 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=False, **kw): + encoding='utf-8', default=None, use_decimal=True, + namedtuple_as_object=True, + tuple_as_array=True, + **kw): """Serialize ``obj`` to a JSON formatted ``str``. If ``skipkeys`` is false then ``dict`` keys that are not basic types @@ -245,6 +262,6 @@ ``default(obj)`` is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. - If *use_decimal* is true (default: ``False``) then decimal.Decimal + If *use_decimal* is true (default: ``True``) then decimal.Decimal will be natively serialized to JSON with full precision. @@ -249,5 +266,12 @@ will be natively serialized to JSON with full precision. + If *namedtuple_as_object* is true (default: ``True``), + :class:`tuple` subclasses with ``_asdict()`` methods will be encoded + as JSON objects. + + If *tuple_as_array* is true (default: ``True``), + :class:`tuple` (and subclasses) will be encoded as JSON arrays. + To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with the ``cls`` kwarg. @@ -257,8 +281,8 @@ if (not skipkeys and ensure_ascii and check_circular and allow_nan and cls is None and indent is None and separators is None and - encoding == 'utf-8' and default is None and not use_decimal - and not kw): + encoding == 'utf-8' and default is None and use_decimal + and namedtuple_as_object and tuple_as_array and not kw): return _default_encoder.encode(obj) if cls is None: cls = JSONEncoder @@ -266,7 +290,10 @@ skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, indent=indent, separators=separators, encoding=encoding, default=default, - use_decimal=use_decimal, **kw).encode(obj) + use_decimal=use_decimal, + namedtuple_as_object=namedtuple_as_object, + tuple_as_array=tuple_as_array, + **kw).encode(obj) _default_decoder = JSONDecoder(encoding=None, object_hook=None, @@ -275,7 +302,8 @@ def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, - use_decimal=False, **kw): + use_decimal=False, namedtuple_as_object=True, tuple_as_array=True, + **kw): """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object. diff --git a/simplejson/_speedups.c b/simplejson/_speedups.c index 76fb7d24552c91406b555b14e727cd5e838cc017_c2ltcGxlanNvbi9fc3BlZWR1cHMuYw==..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2ltcGxlanNvbi9fc3BlZWR1cHMuYw== 100644 --- a/simplejson/_speedups.c +++ b/simplejson/_speedups.c @@ -87,6 +87,8 @@ int fast_encode; int allow_nan; int use_decimal; + int namedtuple_as_object; + int tuple_as_array; } PyEncoderObject; static PyMemberDef encoder_members[] = { @@ -151,6 +153,8 @@ _convertPyInt_FromSsize_t(Py_ssize_t *size_ptr); static PyObject * encoder_encode_float(PyEncoderObject *s, PyObject *obj); +static int +_is_namedtuple(PyObject *obj); #define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '"') #define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) @@ -163,6 +167,12 @@ #endif static int +_is_namedtuple(PyObject *obj) +{ + return PyTuple_Check(obj) && PyObject_HasAttrString(obj, "_asdict"); +} + +static int _convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr) { /* PyObject to Py_ssize_t converter */ @@ -2007,7 +2017,7 @@ encoder_init(PyObject *self, PyObject *args, PyObject *kwds) { /* initialize Encoder object */ - static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", "key_memo", "use_decimal", NULL}; + static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", "key_memo", "use_decimal", "namedtuple_as_object", "tuple_as_array", NULL}; PyEncoderObject *s; PyObject *markers, *defaultfn, *encoder, *indent, *key_separator; @@ -2011,8 +2021,8 @@ PyEncoderObject *s; PyObject *markers, *defaultfn, *encoder, *indent, *key_separator; - PyObject *item_separator, *sort_keys, *skipkeys, *allow_nan, *key_memo, *use_decimal; + PyObject *item_separator, *sort_keys, *skipkeys, *allow_nan, *key_memo, *use_decimal, *namedtuple_as_object, *tuple_as_array; assert(PyEncoder_Check(self)); s = (PyEncoderObject *)self; @@ -2015,6 +2025,6 @@ assert(PyEncoder_Check(self)); s = (PyEncoderObject *)self; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOOOO:make_encoder", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOOOOOO:make_encoder", kwlist, &markers, &defaultfn, &encoder, &indent, &key_separator, &item_separator, @@ -2020,5 +2030,6 @@ &markers, &defaultfn, &encoder, &indent, &key_separator, &item_separator, - &sort_keys, &skipkeys, &allow_nan, &key_memo, &use_decimal)) + &sort_keys, &skipkeys, &allow_nan, &key_memo, &use_decimal, + &namedtuple_as_object, &tuple_as_array)) return -1; s->markers = markers; @@ -2033,6 +2044,8 @@ s->fast_encode = (PyCFunction_Check(s->encoder) && PyCFunction_GetFunction(s->encoder) == (PyCFunction)py_encode_basestring_ascii); s->allow_nan = PyObject_IsTrue(allow_nan); s->use_decimal = PyObject_IsTrue(use_decimal); + s->namedtuple_as_object = PyObject_IsTrue(namedtuple_as_object); + s->tuple_as_array = PyObject_IsTrue(tuple_as_array); Py_INCREF(s->markers); Py_INCREF(s->defaultfn); @@ -2176,7 +2189,14 @@ if (encoded != NULL) rv = _steal_list_append(rval, encoded); } - else if (PyList_Check(obj) || PyTuple_Check(obj)) { + else if (s->namedtuple_as_object && _is_namedtuple(obj)) { + PyObject *newobj = PyObject_CallMethod(obj, "_asdict", NULL); + if (newobj != NULL) { + rv = encoder_listencode_dict(s, rval, newobj, indent_level); + Py_DECREF(newobj); + } + } + else if (PyList_Check(obj) || (s->tuple_as_array && PyTuple_Check(obj))) { rv = encoder_listencode_list(s, rval, obj, indent_level); } else if (PyDict_Check(obj)) { diff --git a/simplejson/encoder.py b/simplejson/encoder.py index 76fb7d24552c91406b555b14e727cd5e838cc017_c2ltcGxlanNvbi9lbmNvZGVyLnB5..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2ltcGxlanNvbi9lbmNvZGVyLnB5 100644 --- a/simplejson/encoder.py +++ b/simplejson/encoder.py @@ -106,7 +106,8 @@ def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, encoding='utf-8', default=None, - use_decimal=False): + use_decimal=True, namedtuple_as_object=True, + tuple_as_array=True): """Constructor for JSONEncoder, with sensible defaults. If skipkeys is false, then it is a TypeError to attempt @@ -154,6 +155,11 @@ be supported directly by the encoder. For the inverse, decode JSON with ``parse_float=decimal.Decimal``. + If namedtuple_as_object is true (the default), tuple subclasses with + ``_asdict()`` methods will be encoded as JSON objects. + + If tuple_as_array is true (the default), tuple (and subclasses) will + be encoded as JSON arrays. """ self.skipkeys = skipkeys @@ -162,6 +168,8 @@ self.allow_nan = allow_nan self.sort_keys = sort_keys self.use_decimal = use_decimal + self.namedtuple_as_object = namedtuple_as_object + self.tuple_as_array = tuple_as_array if isinstance(indent, (int, long)): indent = ' ' * indent self.indent = indent @@ -276,8 +284,9 @@ _iterencode = c_make_encoder( markers, self.default, _encoder, self.indent, self.key_separator, self.item_separator, self.sort_keys, - self.skipkeys, self.allow_nan, key_memo, self.use_decimal) + self.skipkeys, self.allow_nan, key_memo, self.use_decimal, + self.namedtuple_as_object, self.tuple_as_array) else: _iterencode = _make_iterencode( markers, self.default, _encoder, self.indent, floatstr, self.key_separator, self.item_separator, self.sort_keys, @@ -280,8 +289,9 @@ else: _iterencode = _make_iterencode( markers, self.default, _encoder, self.indent, floatstr, self.key_separator, self.item_separator, self.sort_keys, - self.skipkeys, _one_shot, self.use_decimal) + self.skipkeys, _one_shot, self.use_decimal, + self.namedtuple_as_object, self.tuple_as_array) try: return _iterencode(o, 0) finally: @@ -317,7 +327,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, - _use_decimal, + _use_decimal, _namedtuple_as_object, _tuple_as_array, ## HACK: hand-optimized bytecode; turn globals into locals False=False, True=True, @@ -375,7 +385,13 @@ yield buf + str(value) else: yield buf - if isinstance(value, (list, tuple)): + if isinstance(value, list): + chunks = _iterencode_list(value, _current_indent_level) + elif (_namedtuple_as_object and isinstance(value, tuple) and + hasattr(value, '_asdict')): + chunks = _iterencode_dict(value._asdict(), + _current_indent_level) + elif _tuple_as_array and isinstance(value, tuple): chunks = _iterencode_list(value, _current_indent_level) elif isinstance(value, dict): chunks = _iterencode_dict(value, _current_indent_level) @@ -454,7 +470,13 @@ elif _use_decimal and isinstance(value, Decimal): yield str(value) else: - if isinstance(value, (list, tuple)): + if isinstance(value, list): + chunks = _iterencode_list(value, _current_indent_level) + elif (_namedtuple_as_object and isinstance(value, tuple) and + hasattr(value, '_asdict')): + chunks = _iterencode_dict(value._asdict(), + _current_indent_level) + elif _tuple_as_array and isinstance(value, tuple): chunks = _iterencode_list(value, _current_indent_level) elif isinstance(value, dict): chunks = _iterencode_dict(value, _current_indent_level) @@ -482,7 +504,14 @@ yield str(o) elif isinstance(o, float): yield _floatstr(o) - elif isinstance(o, (list, tuple)): + elif isinstance(o, list): + for chunk in _iterencode_list(o, _current_indent_level): + yield chunk + elif (_namedtuple_as_object and isinstance(o, tuple) and + hasattr(o, '_asdict')): + for chunk in _iterencode_dict(o._asdict(), _current_indent_level): + yield chunk + elif (_tuple_as_array and isinstance(o, tuple)): for chunk in _iterencode_list(o, _current_indent_level): yield chunk elif isinstance(o, dict): diff --git a/simplejson/tests/__init__.py b/simplejson/tests/__init__.py index 76fb7d24552c91406b555b14e727cd5e838cc017_c2ltcGxlanNvbi90ZXN0cy9fX2luaXRfXy5weQ==..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2ltcGxlanNvbi90ZXN0cy9fX2luaXRfXy5weQ== 100644 --- a/simplejson/tests/__init__.py +++ b/simplejson/tests/__init__.py @@ -46,6 +46,8 @@ 'simplejson.tests.test_speedups', 'simplejson.tests.test_unicode', 'simplejson.tests.test_decimal', + 'simplejson.tests.test_tuple', + 'simplejson.tests.test_namedtuple', ]) suite = additional_tests(suite) return OptionalExtensionTestSuite([suite]) diff --git a/simplejson/tests/test_decimal.py b/simplejson/tests/test_decimal.py index 76fb7d24552c91406b555b14e727cd5e838cc017_c2ltcGxlanNvbi90ZXN0cy90ZXN0X2RlY2ltYWwucHk=..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2ltcGxlanNvbi90ZXN0cy90ZXN0X2RlY2ltYWwucHk= 100644 --- a/simplejson/tests/test_decimal.py +++ b/simplejson/tests/test_decimal.py @@ -41,7 +41,6 @@ [d]) def test_decimal_defaults(self): - d = Decimal(1) - sio = StringIO() - # use_decimal=False is the default + d = Decimal('1.1') + # use_decimal=True is the default self.assertRaises(TypeError, json.dumps, d, use_decimal=False) @@ -47,4 +46,10 @@ self.assertRaises(TypeError, json.dumps, d, use_decimal=False) - self.assertRaises(TypeError, json.dumps, d) - self.assertRaises(TypeError, json.dump, d, sio, use_decimal=False) - self.assertRaises(TypeError, json.dump, d, sio) \ No newline at end of file + self.assertEqual('1.1', json.dumps(d)) + self.assertEqual('1.1', json.dumps(d, use_decimal=True)) + self.assertRaises(TypeError, json.dump, d, StringIO(), use_decimal=False) + sio = StringIO() + json.dump(d, sio) + self.assertEqual('1.1', sio.getvalue()) + sio = StringIO() + json.dump(d, sio, use_decimal=True) + self.assertEqual('1.1', sio.getvalue()) diff --git a/simplejson/tests/test_namedtuple.py b/simplejson/tests/test_namedtuple.py new file mode 100644 index 0000000000000000000000000000000000000000..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2ltcGxlanNvbi90ZXN0cy90ZXN0X25hbWVkdHVwbGUucHk= --- /dev/null +++ b/simplejson/tests/test_namedtuple.py @@ -0,0 +1,65 @@ +import unittest +import simplejson as json +from StringIO import StringIO + +try: + from collections import namedtuple +except ImportError: + class Value(tuple): + def _asdict(self): + return {'value': self[0]} + class Point(tuple): + def _asdict(self): + return {'x': self[0], 'y': self[1]} +else: + Value = namedtuple('Value', ['value']) + Point = namedtuple('Point', ['x', 'y']) + +class TestNamedTuple(unittest.TestCase): + def test_namedtuple_dumps(self): + for v in [Value(1), Point(1, 2)]: + d = v._asdict() + l = list(v) + self.assertEqual(d, json.loads(json.dumps(v))) + self.assertEqual( + d, + json.loads(json.dumps(v, namedtuple_as_object=True))) + self.assertEqual(d, json.loads(json.dumps(v, tuple_as_array=False))) + self.assertEqual( + d, + json.loads(json.dumps(v, namedtuple_as_object=True, + tuple_as_array=False))) + self.assertEqual( + l, + json.loads(json.dumps(v, namedtuple_as_object=False))) + self.assertRaises(TypeError, json.dumps, v, + tuple_as_array=False, namedtuple_as_object=False) + + def test_namedtuple_dump(self): + for v in [Value(1), Point(1, 2)]: + d = v._asdict() + l = list(v) + sio = StringIO() + json.dump(v, sio) + self.assertEqual(d, json.loads(sio.getvalue())) + sio = StringIO() + json.dump(v, sio, namedtuple_as_object=True) + self.assertEqual( + d, + json.loads(sio.getvalue())) + sio = StringIO() + json.dump(v, sio, tuple_as_array=False) + self.assertEqual(d, json.loads(sio.getvalue())) + sio = StringIO() + json.dump(v, sio, namedtuple_as_object=True, + tuple_as_array=False) + self.assertEqual( + d, + json.loads(sio.getvalue())) + sio = StringIO() + json.dump(v, sio, namedtuple_as_object=False) + self.assertEqual( + l, + json.loads(sio.getvalue())) + self.assertRaises(TypeError, json.dump, v, StringIO(), + tuple_as_array=False, namedtuple_as_object=False) diff --git a/simplejson/tests/test_tuple.py b/simplejson/tests/test_tuple.py new file mode 100644 index 0000000000000000000000000000000000000000..9d88e49d1b73e81558e2c90938ca2cc480e90664_c2ltcGxlanNvbi90ZXN0cy90ZXN0X3R1cGxlLnB5 --- /dev/null +++ b/simplejson/tests/test_tuple.py @@ -0,0 +1,49 @@ +import unittest +from StringIO import StringIO + +import simplejson as json + +class TestTuples(unittest.TestCase): + def test_tuple_array_dumps(self): + t = (1, 2, 3) + expect = json.dumps(list(t)) + # Default is True + self.assertEqual(expect, json.dumps(t)) + self.assertEqual(expect, json.dumps(t, tuple_as_array=True)) + self.assertRaises(TypeError, json.dumps, t, tuple_as_array=False) + # Ensure that the "default" does not get called + self.assertEqual(expect, json.dumps(t, default=repr)) + self.assertEqual(expect, json.dumps(t, tuple_as_array=True, default=repr)) + # Ensure that the "default" gets called + self.assertEqual( + json.dumps(repr(t)), + json.dumps(t, tuple_as_array=False, default=repr)) + + def test_tuple_array_dump(self): + t = (1, 2, 3) + expect = json.dumps(list(t)) + # Default is True + sio = StringIO() + json.dump(t, sio) + self.assertEqual(expect, sio.getvalue()) + sio = StringIO() + json.dump(t, sio, tuple_as_array=True) + self.assertEqual(expect, sio.getvalue()) + self.assertRaises(TypeError, json.dump, t, StringIO(), tuple_as_array=False) + # Ensure that the "default" does not get called + sio = StringIO() + json.dump(t, sio, default=repr) + self.assertEqual(expect, sio.getvalue()) + sio = StringIO() + json.dump(t, sio, tuple_as_array=True, default=repr) + self.assertEqual(expect, sio.getvalue()) + # Ensure that the "default" gets called + sio = StringIO() + json.dump(t, sio, tuple_as_array=False, default=repr) + self.assertEqual( + json.dumps(repr(t)), + sio.getvalue()) + +class TestNamedTuple(unittest.TestCase): + def test_namedtuple_dump(self): + pass \ No newline at end of file