Skip to content

Commit fec7f55

Browse files
[3.14] gh-146056: Fix list.__repr__() for lists containing NULLs (GH-146129)
(cherry picked from commit 0f2246b) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 888026f commit fec7f55

File tree

12 files changed

+71
-3
lines changed

12 files changed

+71
-3
lines changed

Doc/c-api/file.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,12 @@ the :mod:`io` APIs instead.
123123
124124
Write object *obj* to file object *p*. The only supported flag for *flags* is
125125
:c:macro:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written
126-
instead of the :func:`repr`. Return ``0`` on success or ``-1`` on failure; the
127-
appropriate exception will be set.
126+
instead of the :func:`repr`.
127+
128+
If *obj* is ``NULL``, write the string ``"<NULL>"``.
128129
130+
Return ``0`` on success or ``-1`` on failure; the
131+
appropriate exception will be set.
129132
130133
.. c:function:: int PyFile_WriteString(const char *s, PyObject *p)
131134

Doc/c-api/object.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ Object Protocol
319319
representation on success, ``NULL`` on failure. This is the equivalent of the
320320
Python expression ``repr(o)``. Called by the :func:`repr` built-in function.
321321
322+
If argument is ``NULL``, return the string ``'<NULL>'``.
323+
322324
.. versionchanged:: 3.4
323325
This function now includes a debug assertion to help ensure that it
324326
does not silently discard an active exception.
@@ -333,6 +335,8 @@ Object Protocol
333335
a string similar to that returned by :c:func:`PyObject_Repr` in Python 2.
334336
Called by the :func:`ascii` built-in function.
335337
338+
If argument is ``NULL``, return the string ``'<NULL>'``.
339+
336340
.. index:: string; PyObject_Str (C function)
337341
338342
@@ -343,6 +347,8 @@ Object Protocol
343347
Python expression ``str(o)``. Called by the :func:`str` built-in function
344348
and, therefore, by the :func:`print` function.
345349
350+
If argument is ``NULL``, return the string ``'<NULL>'``.
351+
346352
.. versionchanged:: 3.4
347353
This function now includes a debug assertion to help ensure that it
348354
does not silently discard an active exception.
@@ -358,6 +364,8 @@ Object Protocol
358364
a TypeError is raised when *o* is an integer instead of a zero-initialized
359365
bytes object.
360366
367+
If argument is ``NULL``, return the :class:`bytes` object ``b'<NULL>'``.
368+
361369
362370
.. c:function:: int PyObject_IsSubclass(PyObject *derived, PyObject *cls)
363371

Doc/c-api/unicode.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,6 +1874,8 @@ object.
18741874
18751875
Call :c:func:`PyObject_Repr` on *obj* and write the output into *writer*.
18761876
1877+
*obj* should not be ``NULL``.
1878+
18771879
On success, return ``0``.
18781880
On error, set an exception, leave the writer unchanged, and return ``-1``.
18791881

Include/cpython/unicodeobject.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,14 @@ PyAPI_FUNC(int) PyUnicodeWriter_WriteStr(
497497
PyAPI_FUNC(int) PyUnicodeWriter_WriteRepr(
498498
PyUnicodeWriter *writer,
499499
PyObject *obj);
500+
501+
PyAPI_FUNC(int) _PyUnicodeWriter_WriteReprTrue(
502+
PyUnicodeWriter *writer,
503+
PyObject *obj);
504+
#if defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_MODULE)
505+
#define PyUnicodeWriter_WriteRepr _PyUnicodeWriter_WriteReprTrue
506+
#endif
507+
500508
PyAPI_FUNC(int) PyUnicodeWriter_WriteSubstring(
501509
PyUnicodeWriter *writer,
502510
PyObject *str,

Lib/test/test_capi/test_list.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ def test_list_extend(self):
350350
# CRASHES list_extend(NULL, [])
351351
# CRASHES list_extend([], NULL)
352352

353+
def test_uninitialized_list_repr(self):
354+
lst = _testlimitedcapi.list_new(3)
355+
self.assertEqual(repr(lst), '[<NULL>, <NULL>, <NULL>]')
356+
353357

354358
if __name__ == "__main__":
355359
unittest.main()

Lib/test/test_capi/test_tuple.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ def my_iter():
292292
self.assertEqual(tuple(my_iter()), (TAG, *range(10)))
293293
self.assertEqual(tuples, [])
294294

295+
def test_uninitialized_tuple_repr(self):
296+
tup = _testlimitedcapi.tuple_new(3)
297+
self.assertEqual(repr(tup), '(<NULL>, <NULL>, <NULL>)')
298+
295299

296300
if __name__ == "__main__":
297301
unittest.main()

Lib/test/test_capi/test_unicode.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,14 @@ def test_basic(self):
17651765
self.assertEqual(writer.finish(),
17661766
"var=long value 'repr'")
17671767

1768+
def test_repr_null(self):
1769+
writer = self.create_writer(0)
1770+
writer.write_utf8(b'var=', -1)
1771+
# CRASHES writer.write_repr(NULL)
1772+
writer.write_repr_true(NULL)
1773+
self.assertEqual(writer.finish(),
1774+
"var=<NULL>")
1775+
17681776
def test_utf8(self):
17691777
writer = self.create_writer(0)
17701778
writer.write_utf8(b"ascii", -1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:c:func:`PyUnicodeWriter_WriteRepr` now supports ``NULL`` argument.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :meth:`!list.__repr__` for lists containing ``NULL``\ s.

Modules/_testcapi/unicode.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ writer_write_repr(PyObject *self_raw, PyObject *args)
443443
if (!PyArg_ParseTuple(args, "O", &obj)) {
444444
return NULL;
445445
}
446+
NULLABLE(obj);
446447

447448
if (PyUnicodeWriter_WriteRepr(self->writer, obj) < 0) {
448449
return NULL;
@@ -451,6 +452,23 @@ writer_write_repr(PyObject *self_raw, PyObject *args)
451452
}
452453

453454

455+
static PyObject*
456+
writer_write_repr_true(PyObject *self_raw, PyObject *obj)
457+
{
458+
WriterObject *self = (WriterObject *)self_raw;
459+
if (writer_check(self) < 0) {
460+
return NULL;
461+
}
462+
463+
NULLABLE(obj);
464+
465+
if (_PyUnicodeWriter_WriteReprTrue(self->writer, obj) < 0) {
466+
return NULL;
467+
}
468+
Py_RETURN_NONE;
469+
}
470+
471+
454472
static PyObject*
455473
writer_write_substring(PyObject *self_raw, PyObject *args)
456474
{
@@ -539,6 +557,7 @@ static PyMethodDef writer_methods[] = {
539557
{"write_ucs4", _PyCFunction_CAST(writer_write_ucs4), METH_VARARGS},
540558
{"write_str", _PyCFunction_CAST(writer_write_str), METH_VARARGS},
541559
{"write_repr", _PyCFunction_CAST(writer_write_repr), METH_VARARGS},
560+
{"write_repr_true", _PyCFunction_CAST(writer_write_repr_true), METH_O},
542561
{"write_substring", _PyCFunction_CAST(writer_write_substring), METH_VARARGS},
543562
{"decodeutf8stateful", _PyCFunction_CAST(writer_decodeutf8stateful), METH_VARARGS},
544563
{"get_pointer", _PyCFunction_CAST(writer_get_pointer), METH_VARARGS},

0 commit comments

Comments
 (0)