Update simplejson-3.17.5

This commit is contained in:
JonnyWong16 2021-10-14 23:52:53 -07:00
parent f165d2d080
commit cdeff390d9
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
6 changed files with 95 additions and 25 deletions

View file

@ -118,7 +118,7 @@ Serializing multiple objects to JSON lines (newline-delimited JSON)::
""" """
from __future__ import absolute_import from __future__ import absolute_import
__version__ = '3.17.0' __version__ = '3.17.5'
__all__ = [ __all__ = [
'dump', 'dumps', 'load', 'loads', 'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
@ -360,7 +360,7 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
If specified, *item_sort_key* is a callable used to sort the items in 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 each dictionary. This is useful if you want to sort items other than
in alphabetical order by key. This option takes precendence over in alphabetical order by key. This option takes precedence over
*sort_keys*. *sort_keys*.
If *sort_keys* is true (default: ``False``), the output of dictionaries If *sort_keys* is true (default: ``False``), the output of dictionaries

View file

@ -386,6 +386,8 @@ static int
_is_namedtuple(PyObject *obj) _is_namedtuple(PyObject *obj)
{ {
int rval = 0; int rval = 0;
/* We intentionally accept anything with a duck typed _asdict method rather
* than requiring it to pass PyTuple_Check(obj). */
PyObject *_asdict = PyObject_GetAttrString(obj, "_asdict"); PyObject *_asdict = PyObject_GetAttrString(obj, "_asdict");
if (_asdict == NULL) { if (_asdict == NULL) {
PyErr_Clear(); PyErr_Clear();
@ -2853,6 +2855,15 @@ encoder_listencode_obj(PyEncoderObject *s, JSON_Accu *rval, PyObject *obj, Py_ss
return rv; return rv;
newobj = PyObject_CallMethod(obj, "_asdict", NULL); newobj = PyObject_CallMethod(obj, "_asdict", NULL);
if (newobj != NULL) { if (newobj != NULL) {
if (!PyDict_Check(newobj)) {
PyErr_Format(
PyExc_TypeError,
"_asdict() must return a dict, not %.80s",
Py_TYPE(newobj)->tp_name
);
Py_DECREF(newobj);
return -1;
}
rv = encoder_listencode_dict(s, rval, newobj, indent_level); rv = encoder_listencode_dict(s, rval, newobj, indent_level);
Py_DECREF(newobj); Py_DECREF(newobj);
} }

View file

@ -520,7 +520,10 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
else: else:
_asdict = _namedtuple_as_object and getattr(value, '_asdict', None) _asdict = _namedtuple_as_object and getattr(value, '_asdict', None)
if _asdict and callable(_asdict): if _asdict and callable(_asdict):
chunks = _iterencode_dict(_asdict(), dct = _asdict()
if not isinstance(dct, dict):
raise TypeError("_asdict() must return a dict, not %s" % (type(dct).__name__,))
chunks = _iterencode_dict(dct,
_current_indent_level) _current_indent_level)
elif _tuple_as_array and isinstance(value, tuple): elif _tuple_as_array and isinstance(value, tuple):
chunks = _iterencode_list(value, _current_indent_level) chunks = _iterencode_list(value, _current_indent_level)
@ -641,7 +644,10 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
else: else:
_asdict = _namedtuple_as_object and getattr(value, '_asdict', None) _asdict = _namedtuple_as_object and getattr(value, '_asdict', None)
if _asdict and callable(_asdict): if _asdict and callable(_asdict):
chunks = _iterencode_dict(_asdict(), dct = _asdict()
if not isinstance(dct, dict):
raise TypeError("_asdict() must return a dict, not %s" % (type(dct).__name__,))
chunks = _iterencode_dict(dct,
_current_indent_level) _current_indent_level)
elif _tuple_as_array and isinstance(value, tuple): elif _tuple_as_array and isinstance(value, tuple):
chunks = _iterencode_list(value, _current_indent_level) chunks = _iterencode_list(value, _current_indent_level)
@ -686,8 +692,10 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
else: else:
_asdict = _namedtuple_as_object and getattr(o, '_asdict', None) _asdict = _namedtuple_as_object and getattr(o, '_asdict', None)
if _asdict and callable(_asdict): if _asdict and callable(_asdict):
for chunk in _iterencode_dict(_asdict(), dct = _asdict()
_current_indent_level): 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):
yield chunk yield chunk
elif (_tuple_as_array and isinstance(o, tuple)): elif (_tuple_as_array and isinstance(o, tuple)):
for chunk in _iterencode_list(o, _current_indent_level): for chunk in _iterencode_list(o, _current_indent_level):

View file

@ -7,6 +7,7 @@ import os
class NoExtensionTestSuite(unittest.TestSuite): class NoExtensionTestSuite(unittest.TestSuite):
def run(self, result): def run(self, result):
import simplejson import simplejson
simplejson._toggle_speedups(False) simplejson._toggle_speedups(False)
result = unittest.TestSuite.run(self, result) result = unittest.TestSuite.run(self, result)
simplejson._toggle_speedups(True) simplejson._toggle_speedups(True)
@ -15,16 +16,17 @@ class NoExtensionTestSuite(unittest.TestSuite):
class TestMissingSpeedups(unittest.TestCase): class TestMissingSpeedups(unittest.TestCase):
def runTest(self): def runTest(self):
if hasattr(sys, 'pypy_translation_info'): if hasattr(sys, "pypy_translation_info"):
"PyPy doesn't need speedups! :)" "PyPy doesn't need speedups! :)"
elif hasattr(self, 'skipTest'): elif hasattr(self, "skipTest"):
self.skipTest('_speedups.so is missing!') self.skipTest("_speedups.so is missing!")
def additional_tests(suite=None): def additional_tests(suite=None, project_dir=None):
import simplejson import simplejson
import simplejson.encoder import simplejson.encoder
import simplejson.decoder import simplejson.decoder
if suite is None: if suite is None:
suite = unittest.TestSuite() suite = unittest.TestSuite()
try: try:
@ -36,39 +38,54 @@ def additional_tests(suite=None):
raise raise
for mod in (simplejson, simplejson.encoder, simplejson.decoder): for mod in (simplejson, simplejson.encoder, simplejson.decoder):
suite.addTest(doctest.DocTestSuite(mod)) suite.addTest(doctest.DocTestSuite(mod))
suite.addTest(doctest.DocFileSuite('../../index.rst')) if project_dir is not None:
suite.addTest(
doctest.DocFileSuite(
os.path.join(project_dir, "index.rst"), module_relative=False
)
)
return suite return suite
def all_tests_suite(): def all_tests_suite(project_dir=None):
def get_suite(): def get_suite():
suite_names = [ suite_names = [
'simplejson.tests.%s' % (os.path.splitext(f)[0],) "simplejson.tests.%s" % (os.path.splitext(f)[0],)
for f in os.listdir(os.path.dirname(__file__)) for f in os.listdir(os.path.dirname(__file__))
if f.startswith('test_') and f.endswith('.py') if f.startswith("test_") and f.endswith(".py")
] ]
return additional_tests( return additional_tests(
unittest.TestLoader().loadTestsFromNames(suite_names)) suite=unittest.TestLoader().loadTestsFromNames(suite_names),
project_dir=project_dir,
)
suite = get_suite() suite = get_suite()
import simplejson import simplejson
if simplejson._import_c_make_encoder() is None: if simplejson._import_c_make_encoder() is None:
suite.addTest(TestMissingSpeedups()) suite.addTest(TestMissingSpeedups())
else: else:
suite = unittest.TestSuite([ suite = unittest.TestSuite(
suite, [
NoExtensionTestSuite([get_suite()]), suite,
]) NoExtensionTestSuite([get_suite()]),
]
)
return suite return suite
def main(): def main(project_dir=None):
runner = unittest.TextTestRunner(verbosity=1 + sys.argv.count('-v')) runner = unittest.TextTestRunner(verbosity=1 + sys.argv.count("-v"))
suite = all_tests_suite() suite = all_tests_suite(project_dir=project_dir)
raise SystemExit(not runner.run(suite).wasSuccessful()) raise SystemExit(not runner.run(suite).wasSuccessful())
if __name__ == '__main__': if __name__ == "__main__":
import os import os
import sys import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
main() project_dir = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
sys.path.insert(0, project_dir)
main(project_dir=project_dir)

View file

@ -0,0 +1,7 @@
"""Internal module for running tests from cibuildwheel"""
import sys
import simplejson.tests
if __name__ == '__main__':
simplejson.tests.main(project_dir=sys.argv[1])

View file

@ -3,6 +3,11 @@ import unittest
import simplejson as json import simplejson as json
from simplejson.compat import StringIO from simplejson.compat import StringIO
try:
from unittest import mock
except ImportError:
mock = None
try: try:
from collections import namedtuple from collections import namedtuple
except ImportError: except ImportError:
@ -120,3 +125,25 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual( self.assertEqual(
json.dumps(f({})), json.dumps(f({})),
json.dumps(f(DeadDict()), namedtuple_as_object=True)) json.dumps(f(DeadDict()), namedtuple_as_object=True))
def test_asdict_does_not_return_dict(self):
if not mock:
if hasattr(unittest, "SkipTest"):
raise unittest.SkipTest("unittest.mock required")
else:
print("unittest.mock not available")
return
fake = mock.Mock()
self.assertTrue(hasattr(fake, '_asdict'))
self.assertTrue(callable(fake._asdict))
self.assertFalse(isinstance(fake._asdict(), dict))
# https://github.com/simplejson/simplejson/pull/284
# when running under a debug build of CPython (COPTS=-UNDEBUG)
# a C assertion could fire due to an unchecked error of an PyDict
# API call on a non-dict internally in _speedups.c. Without a debug
# build of CPython this test likely passes either way despite the
# potential for internal data corruption. Getting it to crash in
# a debug build is not always easy either as it requires an
# assert(!PyErr_Occurred()) that could fire later on.
with self.assertRaises(TypeError):
json.dumps({23: fake}, namedtuple_as_object=True, for_json=False)