From 982257f69e8e3c4cda4aaac1fb78563803aaf8bf Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Mon, 20 Feb 2017 11:41:42 -0800 Subject: [PATCH 01/13] Updates B.index documentation. --- Objects/bytes_methods.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index d5c4fe6346fc53..fe751ac72a5f67 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -542,7 +542,11 @@ _Py_bytes_find(const char *str, Py_ssize_t len, PyObject *args) PyDoc_STRVAR_shared(_Py_index__doc__, "B.index(sub[, start[, end]]) -> int\n\ \n\ -Like B.find() but raise ValueError when the subsection is not found."); +Return the lowest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Raises ValueError when the subsection is not found."); PyObject * _Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args) @@ -811,4 +815,3 @@ PyDoc_STRVAR_shared(_Py_zfill__doc__, "\n" "Pad a numeric string B with zeros on the left, to fill a field\n" "of the specified width. B is never truncated."); - From 5860a7fcb1e58b67e35539eae5b2c5e04cc72b7d Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Mon, 20 Feb 2017 12:05:04 -0800 Subject: [PATCH 02/13] Updates str.index documentation, makes it Argument Clinic compatible. --- Objects/unicodeobject.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index d3516fa45f9156..c3879b0b2e273d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -11687,7 +11687,23 @@ unicode_hash(PyObject *self) PyDoc_STRVAR(index__doc__, "S.index(sub[, start[, end]]) -> int\n\ \n\ -Like S.find() but raise ValueError when the substring is not found."); +Return the lowest index in B where subsection sub is found, \n\ +such that sub is contained within B[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Raises ValueError when the subsection is not found."); + +/*[clinic input] +str.index as unicode_index + +Return the lowest index in B where subsection sub is found. + +Return the lowest index in B where subsection sub is found, +such that sub is contained within B[start,end]. Optional +arguments start and end are interpreted as in slice notation. + +Raises ValueError when the subsection is not found. +[clinic start generated code]*/ static PyObject * unicode_index(PyObject *self, PyObject *args) From 25e2482e8ec445c65652519572d66815963c00da Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 22 Feb 2017 16:03:09 -0800 Subject: [PATCH 03/13] Removes ArgumentClinic code. --- Objects/unicodeobject.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index c3879b0b2e273d..782a4cf1f365e9 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -11693,18 +11693,6 @@ arguments start and end are interpreted as in slice notation.\n\ \n\ Raises ValueError when the subsection is not found."); -/*[clinic input] -str.index as unicode_index - -Return the lowest index in B where subsection sub is found. - -Return the lowest index in B where subsection sub is found, -such that sub is contained within B[start,end]. Optional -arguments start and end are interpreted as in slice notation. - -Raises ValueError when the subsection is not found. -[clinic start generated code]*/ - static PyObject * unicode_index(PyObject *self, PyObject *args) { From edae59d3b9dd1c15045be0fb255223b2c7301246 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 22 Feb 2017 16:07:32 -0800 Subject: [PATCH 04/13] Finishes string.index documentation. --- Objects/unicodeobject.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 782a4cf1f365e9..e0b5e35797f7eb 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -11687,11 +11687,11 @@ unicode_hash(PyObject *self) PyDoc_STRVAR(index__doc__, "S.index(sub[, start[, end]]) -> int\n\ \n\ -Return the lowest index in B where subsection sub is found, \n\ -such that sub is contained within B[start,end]. Optional\n\ +Return the lowest index in S where substring sub is found, \n\ +such that sub is contained within S[start:end]. Optional\n\ arguments start and end are interpreted as in slice notation.\n\ \n\ -Raises ValueError when the subsection is not found."); +Raises ValueError when the substring is not found."); static PyObject * unicode_index(PyObject *self, PyObject *args) From 5e182a6d6292b63494fef2ba2864e01d67d30b88 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 22 Feb 2017 16:10:04 -0800 Subject: [PATCH 05/13] Updates string.rindex documentation. --- Objects/unicodeobject.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index e0b5e35797f7eb..b2fe859c2b5028 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -12807,7 +12807,11 @@ unicode_rfind(PyObject *self, PyObject *args) PyDoc_STRVAR(rindex__doc__, "S.rindex(sub[, start[, end]]) -> int\n\ \n\ -Like S.rfind() but raise ValueError when the substring is not found."); +Return the highest index in S where substring sub is found,\n\ +such that sub is contained within S[start:end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Raises ValueError when the substring is not found."); static PyObject * unicode_rindex(PyObject *self, PyObject *args) From 1e950a26ade47e5b45ce8b2c16f523acac8108bd Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 22 Feb 2017 16:50:40 -0800 Subject: [PATCH 06/13] Documents B.rindex. --- Objects/bytes_methods.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index fe751ac72a5f67..625e242d563d89 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -583,7 +583,11 @@ _Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args) PyDoc_STRVAR_shared(_Py_rindex__doc__, "B.rindex(sub[, start[, end]]) -> int\n\ \n\ -Like B.rfind() but raise ValueError when the subsection is not found."); +Return the highest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Raise ValueError when the subsection is not found."); PyObject * _Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args) From c4649232f494da79ca6b12f0b5f476e29803c1b8 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Sun, 5 Mar 2017 15:48:08 -0800 Subject: [PATCH 07/13] Updates python iterencode for deques. --- Lib/json/encoder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index 41a497c5da0160..6fe2cc4d4b6394 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -1,7 +1,7 @@ """Implementation of JSONEncoder """ import re - +from collections import deque try: from _json import encode_basestring_ascii as c_encode_basestring_ascii except ImportError: @@ -316,7 +316,7 @@ def _iterencode_list(lst, _current_indent_level): yield buf + _floatstr(value) else: yield buf - if isinstance(value, (list, tuple)): + if isinstance(value, (list, tuple, deque)): chunks = _iterencode_list(value, _current_indent_level) elif isinstance(value, dict): chunks = _iterencode_dict(value, _current_indent_level) @@ -395,7 +395,7 @@ def _iterencode_dict(dct, _current_indent_level): # see comment for int/float in _make_iterencode yield _floatstr(value) else: - if isinstance(value, (list, tuple)): + if isinstance(value, (list, tuple, deque)): chunks = _iterencode_list(value, _current_indent_level) elif isinstance(value, dict): chunks = _iterencode_dict(value, _current_indent_level) @@ -424,7 +424,7 @@ def _iterencode(o, _current_indent_level): elif isinstance(o, float): # see comment for int/float in _make_iterencode yield _floatstr(o) - elif isinstance(o, (list, tuple)): + elif isinstance(o, (list, tuple, deque)): yield from _iterencode_list(o, _current_indent_level) elif isinstance(o, dict): yield from _iterencode_dict(o, _current_indent_level) From 82de7ac77d2af0a124a4c9f9f6fc3fa794bc8365 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Tue, 7 Mar 2017 20:45:34 -0800 Subject: [PATCH 08/13] Adds check for deque along with list and tuple. --- Modules/_json.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/_json.c b/Modules/_json.c index f4000f83a55891..22d213cb8a186e 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1493,6 +1493,8 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc, /* Encode Python object obj to a JSON term */ PyObject *newobj; int rv; + PyObject *mo = PyImport_ImportModule("collections"); + PyTypeObject *deque = PyObject_GetAttrString(mo, "deque"); if (obj == Py_None || obj == Py_True || obj == Py_False) { PyObject *cstr = _encoded_const(obj); @@ -1519,7 +1521,7 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc, return -1; return _steal_accumulate(acc, encoded); } - else if (PyList_Check(obj) || PyTuple_Check(obj)) { + else if (PyList_Check(obj) || PyTuple_Check(obj) || Py_TYPE(obj) == deque) { if (Py_EnterRecursiveCall(" while encoding a JSON object")) return -1; rv = encoder_listencode_list(s, acc, obj, indent_level); From 7087e1a49004389d738724de33c577734ea7d464 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Thu, 9 Mar 2017 21:15:46 -0800 Subject: [PATCH 09/13] Adds IsInstance instead of PyTYPE conversion. --- Modules/_json.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_json.c b/Modules/_json.c index 22d213cb8a186e..bb8bbab266e97c 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1494,7 +1494,7 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc, PyObject *newobj; int rv; PyObject *mo = PyImport_ImportModule("collections"); - PyTypeObject *deque = PyObject_GetAttrString(mo, "deque"); + PyObject *deque_type = PyObject_GetAttrString(mo, "deque"); if (obj == Py_None || obj == Py_True || obj == Py_False) { PyObject *cstr = _encoded_const(obj); @@ -1521,7 +1521,7 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc, return -1; return _steal_accumulate(acc, encoded); } - else if (PyList_Check(obj) || PyTuple_Check(obj) || Py_TYPE(obj) == deque) { + else if (PyList_Check(obj) || PyTuple_Check(obj) || PyObject_IsInstance(obj, deque_type)) { if (Py_EnterRecursiveCall(" while encoding a JSON object")) return -1; rv = encoder_listencode_list(s, acc, obj, indent_level); From 30d6ec50e6c153837724d094c39a9ed7bd385796 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Sun, 26 Mar 2017 17:57:47 -0700 Subject: [PATCH 10/13] Reverting old documentation update. --- Objects/bytes_methods.c | 12 ++---------- Objects/unicodeobject.c | 12 ++---------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index 625e242d563d89..189be01c511522 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -542,11 +542,7 @@ _Py_bytes_find(const char *str, Py_ssize_t len, PyObject *args) PyDoc_STRVAR_shared(_Py_index__doc__, "B.index(sub[, start[, end]]) -> int\n\ \n\ -Return the lowest index in B where subsection sub is found,\n\ -such that sub is contained within B[start,end]. Optional\n\ -arguments start and end are interpreted as in slice notation.\n\ -\n\ -Raises ValueError when the subsection is not found."); +Like B.find() but raise ValueError when the subsection is not found."); PyObject * _Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args) @@ -583,11 +579,7 @@ _Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args) PyDoc_STRVAR_shared(_Py_rindex__doc__, "B.rindex(sub[, start[, end]]) -> int\n\ \n\ -Return the highest index in B where subsection sub is found,\n\ -such that sub is contained within B[start,end]. Optional\n\ -arguments start and end are interpreted as in slice notation.\n\ -\n\ -Raise ValueError when the subsection is not found."); +Like B.rfind() but raise ValueError when the subsection is not found."); PyObject * _Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index b2fe859c2b5028..d3516fa45f9156 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -11687,11 +11687,7 @@ unicode_hash(PyObject *self) PyDoc_STRVAR(index__doc__, "S.index(sub[, start[, end]]) -> int\n\ \n\ -Return the lowest index in S where substring sub is found, \n\ -such that sub is contained within S[start:end]. Optional\n\ -arguments start and end are interpreted as in slice notation.\n\ -\n\ -Raises ValueError when the substring is not found."); +Like S.find() but raise ValueError when the substring is not found."); static PyObject * unicode_index(PyObject *self, PyObject *args) @@ -12807,11 +12803,7 @@ unicode_rfind(PyObject *self, PyObject *args) PyDoc_STRVAR(rindex__doc__, "S.rindex(sub[, start[, end]]) -> int\n\ \n\ -Return the highest index in S where substring sub is found,\n\ -such that sub is contained within S[start:end]. Optional\n\ -arguments start and end are interpreted as in slice notation.\n\ -\n\ -Raises ValueError when the substring is not found."); +Like S.rfind() but raise ValueError when the substring is not found."); static PyObject * unicode_rindex(PyObject *self, PyObject *args) From 6161979db7f408b595217c879145cd9c4ce2564e Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Sun, 26 Mar 2017 18:01:08 -0700 Subject: [PATCH 11/13] Fixes weird missing newline at end of file. --- Objects/bytes_methods.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index 189be01c511522..d5c4fe6346fc53 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -811,3 +811,4 @@ PyDoc_STRVAR_shared(_Py_zfill__doc__, "\n" "Pad a numeric string B with zeros on the left, to fill a field\n" "of the specified width. B is never truncated."); + From 697f1bf3a117eaff2d975c363dd083e766cda040 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Tue, 5 Mar 2019 21:04:16 -0800 Subject: [PATCH 12/13] Adds unit tests, docs, fixes ref leak. --- Doc/library/json.rst | 5 ++++- Lib/test/test_json/test_deque.py | 16 ++++++++++++++++ Lib/test/test_json/test_dump.py | 2 +- Modules/_json.c | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 Lib/test/test_json/test_deque.py diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 8103c614aaf450..4688d842d3f4f1 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -390,7 +390,7 @@ Encoders and Decoders +========================================+===============+ | dict | object | +----------------------------------------+---------------+ - | list, tuple | array | + | list, tuple, deque | array | +----------------------------------------+---------------+ | str | string | +----------------------------------------+---------------+ @@ -406,6 +406,9 @@ Encoders and Decoders .. versionchanged:: 3.4 Added support for int- and float-derived Enum classes. + .. versionchanged:: 3.8 + Added support for deque. + To extend this to recognize other objects, subclass and implement a :meth:`default` method with another method that returns a serializable object for ``o`` if possible, otherwise it should call the superclass implementation diff --git a/Lib/test/test_json/test_deque.py b/Lib/test/test_json/test_deque.py new file mode 100644 index 00000000000000..a0593c9df89d56 --- /dev/null +++ b/Lib/test/test_json/test_deque.py @@ -0,0 +1,16 @@ +from collections import deque +from test.test_json import PyTest, CTest + + +class TestDeque: + def test_deque_ints(self): + d = deque(range(0, 10)) + self.assertEqual(self.dumps(d), '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]') + + def test_deque_strings(self): + d = deque('hello') + self.assertEqual(self.dumps(d), '["h", "e", "l", "l", "o"]') + + +class TestPyDeque(TestDeque, PyTest): pass +class TestCDeque(TestDeque, CTest): pass diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index fd0d86b392cee9..cdb9f2386ed25a 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -3,6 +3,7 @@ from test.support import bigmemtest, _1G + class TestDump: def test_dump(self): sio = StringIO() @@ -47,7 +48,6 @@ def __lt__(self, o): d[1337] = "true.dat" self.assertEqual(self.dumps(d, sort_keys=True), '{"1337": "true.dat"}') - class TestPyDump(TestDump, PyTest): pass class TestCDump(TestDump, CTest): diff --git a/Modules/_json.c b/Modules/_json.c index bb8bbab266e97c..5cd02c010ba413 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1496,6 +1496,7 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc, PyObject *mo = PyImport_ImportModule("collections"); PyObject *deque_type = PyObject_GetAttrString(mo, "deque"); + Py_DECREF(mo); if (obj == Py_None || obj == Py_True || obj == Py_False) { PyObject *cstr = _encoded_const(obj); if (cstr == NULL) @@ -1522,6 +1523,7 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc, return _steal_accumulate(acc, encoded); } else if (PyList_Check(obj) || PyTuple_Check(obj) || PyObject_IsInstance(obj, deque_type)) { + Py_DECREF(deque_type); if (Py_EnterRecursiveCall(" while encoding a JSON object")) return -1; rv = encoder_listencode_list(s, acc, obj, indent_level); From 5126064486f967755d42a930d71417ac0a45cb0d Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 6 Mar 2019 19:12:54 -0800 Subject: [PATCH 13/13] Removes blank line. --- Lib/test/test_json/test_dump.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index cdb9f2386ed25a..fd0d86b392cee9 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -3,7 +3,6 @@ from test.support import bigmemtest, _1G - class TestDump: def test_dump(self): sio = StringIO() @@ -48,6 +47,7 @@ def __lt__(self, o): d[1337] = "true.dat" self.assertEqual(self.dumps(d, sort_keys=True), '{"1337": "true.dat"}') + class TestPyDump(TestDump, PyTest): pass class TestCDump(TestDump, CTest):