diff --git a/lib/simplejson/__init__.py b/lib/simplejson/__init__.py index 20327ead..c79ad1bc 100644 --- a/lib/simplejson/__init__.py +++ b/lib/simplejson/__init__.py @@ -118,7 +118,7 @@ Serializing multiple objects to JSON lines (newline-delimited JSON):: """ from __future__ import absolute_import -__version__ = '3.17.6' +__version__ = '3.18.0' __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', @@ -300,7 +300,7 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, iterable_as_array=False, **kw): """Serialize ``obj`` to a JSON formatted ``str``. - If ``skipkeys`` is false then ``dict`` keys that are not basic types + If ``skipkeys`` is true then ``dict`` keys that are not basic types (``str``, ``int``, ``long``, ``float``, ``bool``, ``None``) will be skipped instead of raising a ``TypeError``. diff --git a/lib/simplejson/decoder.py b/lib/simplejson/decoder.py index 7f0b0568..1a8f772f 100644 --- a/lib/simplejson/decoder.py +++ b/lib/simplejson/decoder.py @@ -109,6 +109,8 @@ def py_scanstring(s, end, encoding=None, strict=True, uni = int(esc, 16) except ValueError: raise JSONDecodeError(msg, s, end - 1) + if uni < 0 or uni > _maxunicode: + raise JSONDecodeError(msg, s, end - 1) end += 5 # Check for surrogate pair on UCS-4 systems # Note that this will join high/low surrogate pairs diff --git a/lib/simplejson/encoder.py b/lib/simplejson/encoder.py index 2f81cabc..2bc8bbad 100644 --- a/lib/simplejson/encoder.py +++ b/lib/simplejson/encoder.py @@ -450,6 +450,15 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, not isinstance(_int_as_string_bitcount, integer_types))): raise TypeError("int_as_string_bitcount must be a positive integer") + def call_method(obj, method_name): + method = getattr(obj, method_name, None) + if callable(method): + try: + return (method(),) + except TypeError: + pass + return None + def _encode_int(value): skip_quoting = ( _int_as_string_bitcount is None @@ -512,15 +521,15 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, yield buf + str(value) else: yield buf - for_json = _for_json and getattr(value, 'for_json', None) - if for_json and callable(for_json): - chunks = _iterencode(for_json(), _current_indent_level) + for_json = _for_json and call_method(value, 'for_json') + if for_json: + chunks = _iterencode(for_json[0], _current_indent_level) elif isinstance(value, list): chunks = _iterencode_list(value, _current_indent_level) else: - _asdict = _namedtuple_as_object and getattr(value, '_asdict', None) - if _asdict and callable(_asdict): - dct = _asdict() + _asdict = _namedtuple_as_object and call_method(value, '_asdict') + if _asdict: + dct = _asdict[0] if not isinstance(dct, dict): raise TypeError("_asdict() must return a dict, not %s" % (type(dct).__name__,)) chunks = _iterencode_dict(dct, @@ -636,15 +645,15 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, elif _use_decimal and isinstance(value, Decimal): yield str(value) else: - for_json = _for_json and getattr(value, 'for_json', None) - if for_json and callable(for_json): - chunks = _iterencode(for_json(), _current_indent_level) + for_json = _for_json and call_method(value, 'for_json') + if for_json: + chunks = _iterencode(for_json[0], _current_indent_level) elif isinstance(value, list): chunks = _iterencode_list(value, _current_indent_level) else: - _asdict = _namedtuple_as_object and getattr(value, '_asdict', None) - if _asdict and callable(_asdict): - dct = _asdict() + _asdict = _namedtuple_as_object and call_method(value, '_asdict') + if _asdict: + dct = _asdict[0] if not isinstance(dct, dict): raise TypeError("_asdict() must return a dict, not %s" % (type(dct).__name__,)) chunks = _iterencode_dict(dct, @@ -682,17 +691,17 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, elif isinstance(o, float): yield _floatstr(o) else: - for_json = _for_json and getattr(o, 'for_json', None) - if for_json and callable(for_json): - for chunk in _iterencode(for_json(), _current_indent_level): + for_json = _for_json and call_method(o, 'for_json') + if for_json: + for chunk in _iterencode(for_json[0], _current_indent_level): yield chunk elif isinstance(o, list): for chunk in _iterencode_list(o, _current_indent_level): yield chunk else: - _asdict = _namedtuple_as_object and getattr(o, '_asdict', None) - if _asdict and callable(_asdict): - dct = _asdict() + _asdict = _namedtuple_as_object and call_method(o, '_asdict') + if _asdict: + dct = _asdict[0] if not isinstance(dct, dict): raise TypeError("_asdict() must return a dict, not %s" % (type(dct).__name__,)) for chunk in _iterencode_dict(dct, _current_indent_level): diff --git a/lib/simplejson/tests/test_namedtuple.py b/lib/simplejson/tests/test_namedtuple.py index 8035a5f5..cc0f8aa1 100644 --- a/lib/simplejson/tests/test_namedtuple.py +++ b/lib/simplejson/tests/test_namedtuple.py @@ -110,22 +110,47 @@ class TestNamedTuple(unittest.TestCase): def test_asdict_not_callable_dump(self): for f in CONSTRUCTORS: - self.assertRaises(TypeError, - json.dump, f(DeadDuck()), StringIO(), namedtuple_as_object=True) + self.assertRaises( + TypeError, + json.dump, + f(DeadDuck()), + StringIO(), + namedtuple_as_object=True + ) sio = StringIO() json.dump(f(DeadDict()), sio, namedtuple_as_object=True) self.assertEqual( json.dumps(f({})), sio.getvalue()) + self.assertRaises( + TypeError, + json.dump, + f(Value), + StringIO(), + namedtuple_as_object=True + ) def test_asdict_not_callable_dumps(self): for f in CONSTRUCTORS: self.assertRaises(TypeError, json.dumps, f(DeadDuck()), namedtuple_as_object=True) + self.assertRaises( + TypeError, + json.dumps, + f(Value), + namedtuple_as_object=True + ) self.assertEqual( json.dumps(f({})), json.dumps(f(DeadDict()), namedtuple_as_object=True)) + def test_asdict_unbound_method_dumps(self): + for f in CONSTRUCTORS: + self.assertEqual( + json.dumps(f(Value), default=lambda v: v.__name__), + json.dumps(f(Value.__name__)) + ) + def test_asdict_does_not_return_dict(self): if not mock: if hasattr(unittest, "SkipTest"): diff --git a/lib/simplejson/tests/test_scanstring.py b/lib/simplejson/tests/test_scanstring.py index d5de1801..c6c53b81 100644 --- a/lib/simplejson/tests/test_scanstring.py +++ b/lib/simplejson/tests/test_scanstring.py @@ -132,6 +132,8 @@ class TestScanString(TestCase): self.assertRaises(ValueError, scanstring, '\\ud834\\x0123"', 0, None, True) + self.assertRaises(json.JSONDecodeError, scanstring, "\\u-123", 0, None, True) + def test_issue3623(self): self.assertRaises(ValueError, json.decoder.scanstring, "xxx", 1, "xxx") diff --git a/requirements.txt b/requirements.txt index ac0b1975..827ed814 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,7 +38,7 @@ pytz==2022.6 requests==2.28.1 requests-oauthlib==1.3.1 rumps==0.4.0; platform_system == "Darwin" -simplejson==3.17.6 +simplejson==3.18.0 six==1.16.0 soupsieve==2.3.2.post1 tempora==5.0.2