Skip to content

Commit a412165

Browse files
committed
Add comments explaining why Py_INCREF is needed in iteration functions
Match the style of the comment added by gh-145244 for the dict iteration case. These explain why borrowed references from items lists and sequences must be held as strong references during encoding.
1 parent 646b447 commit a412165

File tree

1 file changed

+8
-4
lines changed

1 file changed

+8
-4
lines changed

Modules/_json.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,9 +1745,11 @@ _encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer,
17451745
PyObject *key, *value;
17461746
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) {
17471747
PyObject *item = PyList_GET_ITEM(items, i);
1748-
1748+
// gh-142831: item is a borrowed ref from the items list.
1749+
// encoder_encode_key_value() may invoke user Python code
1750+
// (the key encoder) that can mutate or clear the list,
1751+
// so we must hold a strong reference.
17491752
Py_INCREF(item);
1750-
17511753
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) {
17521754
PyErr_SetString(PyExc_ValueError, "items must return 2-tuples");
17531755
Py_DECREF(item);
@@ -1895,9 +1897,11 @@ _encoder_iterate_fast_seq_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer,
18951897
{
18961898
for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) {
18971899
PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i);
1898-
1900+
// gh-142831: obj is a borrowed ref from the sequence.
1901+
// encoder_listencode_obj() may invoke user Python code
1902+
// (the 'default' callback) that can mutate or clear the
1903+
// sequence, so we must hold a strong reference.
18991904
Py_INCREF(obj);
1900-
19011905
if (i) {
19021906
if (PyUnicodeWriter_WriteStr(writer, separator) < 0) {
19031907
Py_DECREF(obj);

0 commit comments

Comments
 (0)