diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index 1ba8b48bd78cde..676bd8ad038680 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -242,12 +242,13 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, def detect_encoding(b): - bstartswith = b.startswith - if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)): - return 'utf-32' - if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)): - return 'utf-16' - if bstartswith(codecs.BOM_UTF8): + for prefix in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE): + if b[:len(prefix)] == prefix: + return 'utf-32' + for prefix in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE): + if b[:len(prefix)] == prefix: + return 'utf-16' + if b[:len(codecs.BOM_UTF8)] == codecs.BOM_UTF8: return 'utf-8-sig' if len(b) >= 4: @@ -337,11 +338,14 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)", s, 0) else: - if not isinstance(s, (bytes, bytearray)): - raise TypeError(f'the JSON object must be str, bytes or bytearray, ' - f'not {s.__class__.__name__}') - s = s.decode(detect_encoding(s), 'surrogatepass') - + if not isinstance(s, (bytes, bytearray, memoryview)): + try: + s = memoryview(s) + except TypeError: + raise TypeError('the JSON object must be str, bytes or ' + 'bytearray or memoryview compatible object, ' + 'not {!r}'.format(s.__class__.__name__)) + s = str(s, detect_encoding(s), 'surrogatepass') if "encoding" in kw: import warnings warnings.warn( diff --git a/Lib/test/test_json/test_decode.py b/Lib/test/test_json/test_decode.py index 895c95b54c3b65..942deb149d0007 100644 --- a/Lib/test/test_json/test_decode.py +++ b/Lib/test/test_json/test_decode.py @@ -1,3 +1,4 @@ +import array import decimal from io import StringIO from collections import OrderedDict @@ -20,6 +21,15 @@ def test_empty_objects(self): self.assertEqual(self.loads('[]'), []) self.assertEqual(self.loads('""'), "") + def test_memoryview(self): + data = memoryview(b'{"key": "val"}') + self.assertEqual(self.loads(data), {"key": "val"}) + + def test_buffer(self): + data = array.array('B') + data.frombytes(b'{"key": "val"}') + self.assertEqual(self.loads(data), {"key": "val"}) + def test_object_pairs_hook(self): s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' p = [("xkd", 1), ("kcw", 2), ("art", 3), ("hxm", 4), diff --git a/Misc/ACKS b/Misc/ACKS index e02e8e1fa5155d..f9742422e28d28 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -841,6 +841,7 @@ Sanyam Khurana Mads Kiilerich Jason Killen Jan Kim +Nikolay Kim Taek Joo Kim Sam Kimbrel Tomohiko Kinebuchi diff --git a/Misc/NEWS.d/next/Library/2019-07-27-11-52-22.bpo-30193.D-PYaS.rst b/Misc/NEWS.d/next/Library/2019-07-27-11-52-22.bpo-30193.D-PYaS.rst new file mode 100644 index 00000000000000..addcdbe7bad7f4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-27-11-52-22.bpo-30193.D-PYaS.rst @@ -0,0 +1,2 @@ +Allow to load buffer objects with `json.loads()`. Patch by Nikolay Kim +.