https://github.com/python/cpython/commit/796513306fdc1184452ea644b485f83511f39528
commit: 796513306fdc1184452ea644b485f83511f39528
branch: 3.14
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-03-22T09:25:02+02:00
summary:

[3.14] gh-146056: Fix repr() for lists and tuples containing NULLs (GH-146129) 
(GH-146155)

(cherry picked from commit 0f2246b1553f401da5ade47e0fd1c80ad7a8dfa5)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/C_API/2026-03-18-20-18-59.gh-issue-146056.nnZIgp.rst
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-03-18-18-52-00.gh-issue-146056.r1tVSo.rst
M Doc/c-api/file.rst
M Doc/c-api/object.rst
M Doc/c-api/unicode.rst
M Lib/test/test_capi/test_list.py
M Lib/test/test_capi/test_tuple.py
M Lib/test/test_capi/test_unicode.py
M Objects/listobject.c
M Objects/unicodeobject.c

diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst
index 0580e4c8f79db0..d89072ab24e241 100644
--- a/Doc/c-api/file.rst
+++ b/Doc/c-api/file.rst
@@ -123,9 +123,12 @@ the :mod:`io` APIs instead.
 
    Write object *obj* to file object *p*.  The only supported flag for *flags* 
is
    :c:macro:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written
-   instead of the :func:`repr`.  Return ``0`` on success or ``-1`` on failure; 
the
-   appropriate exception will be set.
+   instead of the :func:`repr`.
+
+   If *obj* is ``NULL``, write the string ``"<NULL>"``.
 
+   Return ``0`` on success or ``-1`` on failure; the
+   appropriate exception will be set.
 
 .. c:function:: int PyFile_WriteString(const char *s, PyObject *p)
 
diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index 228b3c8aa30844..44e1220d109d05 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -319,6 +319,8 @@ Object Protocol
    representation on success, ``NULL`` on failure.  This is the equivalent of 
the
    Python expression ``repr(o)``.  Called by the :func:`repr` built-in 
function.
 
+   If argument is ``NULL``, return the string ``'<NULL>'``.
+
    .. versionchanged:: 3.4
       This function now includes a debug assertion to help ensure that it
       does not silently discard an active exception.
@@ -333,6 +335,8 @@ Object Protocol
    a string similar to that returned by :c:func:`PyObject_Repr` in Python 2.
    Called by the :func:`ascii` built-in function.
 
+   If argument is ``NULL``, return the string ``'<NULL>'``.
+
    .. index:: string; PyObject_Str (C function)
 
 
@@ -343,6 +347,8 @@ Object Protocol
    Python expression ``str(o)``.  Called by the :func:`str` built-in function
    and, therefore, by the :func:`print` function.
 
+   If argument is ``NULL``, return the string ``'<NULL>'``.
+
    .. versionchanged:: 3.4
       This function now includes a debug assertion to help ensure that it
       does not silently discard an active exception.
@@ -358,6 +364,8 @@ Object Protocol
    a TypeError is raised when *o* is an integer instead of a zero-initialized
    bytes object.
 
+   If argument is ``NULL``, return the :class:`bytes` object ``b'<NULL>'``.
+
 
 .. c:function:: int PyObject_IsSubclass(PyObject *derived, PyObject *cls)
 
diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index b7d3aaf3227bf0..53a13e80944c70 100644
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -1842,8 +1842,6 @@ object.
    On success, return ``0``.
    On error, set an exception, leave the writer unchanged, and return ``-1``.
 
-   .. versionadded:: 3.14
-
 .. c:function:: int PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, 
const wchar_t *str, Py_ssize_t size)
 
    Write the wide string *str* into *writer*.
@@ -1874,9 +1872,15 @@ object.
 
    Call :c:func:`PyObject_Repr` on *obj* and write the output into *writer*.
 
+   If *obj* is ``NULL``, write the string ``"<NULL>"`` into *writer*.
+
    On success, return ``0``.
    On error, set an exception, leave the writer unchanged, and return ``-1``.
 
+   .. versionchanged:: 3.14.4
+
+      Added support for ``NULL``.
+
 .. c:function:: int PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, 
PyObject *str, Py_ssize_t start, Py_ssize_t end)
 
    Write the substring ``str[start:end]`` into *writer*.
diff --git a/Lib/test/test_capi/test_list.py b/Lib/test/test_capi/test_list.py
index 67ed5d0b4f8722..b95b8ba960bd8b 100644
--- a/Lib/test/test_capi/test_list.py
+++ b/Lib/test/test_capi/test_list.py
@@ -350,6 +350,10 @@ def test_list_extend(self):
         # CRASHES list_extend(NULL, [])
         # CRASHES list_extend([], NULL)
 
+    def test_uninitialized_list_repr(self):
+        lst = _testlimitedcapi.list_new(3)
+        self.assertEqual(repr(lst), '[<NULL>, <NULL>, <NULL>]')
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_capi/test_tuple.py b/Lib/test/test_capi/test_tuple.py
index be6c9155831206..3683c008dbcaaf 100644
--- a/Lib/test/test_capi/test_tuple.py
+++ b/Lib/test/test_capi/test_tuple.py
@@ -292,6 +292,10 @@ def my_iter():
         self.assertEqual(tuple(my_iter()), (TAG, *range(10)))
         self.assertEqual(tuples, [])
 
+    def test_uninitialized_tuple_repr(self):
+        tup = _testlimitedcapi.tuple_new(3)
+        self.assertEqual(repr(tup), '(<NULL>, <NULL>, <NULL>)')
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_capi/test_unicode.py 
b/Lib/test/test_capi/test_unicode.py
index be19146cc657c8..7e5f4c9dac94f9 100644
--- a/Lib/test/test_capi/test_unicode.py
+++ b/Lib/test/test_capi/test_unicode.py
@@ -1767,6 +1767,13 @@ def test_basic(self):
         self.assertEqual(writer.finish(),
                          "var=long value 'repr'")
 
+    def test_repr_null(self):
+        writer = self.create_writer(0)
+        writer.write_utf8(b'var=', -1)
+        writer.write_repr(NULL)
+        self.assertEqual(writer.finish(),
+                         "var=<NULL>")
+
     def test_write_char(self):
         writer = self.create_writer(0)
         writer.write_char(0)
diff --git 
a/Misc/NEWS.d/next/C_API/2026-03-18-20-18-59.gh-issue-146056.nnZIgp.rst 
b/Misc/NEWS.d/next/C_API/2026-03-18-20-18-59.gh-issue-146056.nnZIgp.rst
new file mode 100644
index 00000000000000..7c5fc7a0538e21
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-03-18-20-18-59.gh-issue-146056.nnZIgp.rst
@@ -0,0 +1 @@
+:c:func:`PyUnicodeWriter_WriteRepr` now supports ``NULL`` argument.
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-18-18-52-00.gh-issue-146056.r1tVSo.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-18-18-52-00.gh-issue-146056.r1tVSo.rst
new file mode 100644
index 00000000000000..ab6eab2c968e8f
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-18-18-52-00.gh-issue-146056.r1tVSo.rst
@@ -0,0 +1 @@
+Fix :func:`repr` for lists and tuples containing ``NULL``\ s.
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 98c90665be5721..6407d0ac5833fe 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -595,7 +595,7 @@ list_repr_impl(PyListObject *v)
        so must refetch the list size on each iteration. */
     for (Py_ssize_t i = 0; i < Py_SIZE(v); ++i) {
         /* Hold a strong reference since repr(item) can mutate the list */
-        item = Py_NewRef(v->ob_item[i]);
+        item = Py_XNewRef(v->ob_item[i]);
 
         if (i > 0) {
             if (PyUnicodeWriter_WriteChar(writer, ',') < 0) {
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 4a457c4ac9ff3b..aef89c15b30a21 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -13976,6 +13976,10 @@ PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, 
PyObject *obj)
 int
 PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj)
 {
+    if (obj == NULL) {
+        return _PyUnicodeWriter_WriteASCIIString((_PyUnicodeWriter*)writer, 
"<NULL>", 6);
+    }
+
     if (Py_TYPE(obj) == &PyLong_Type) {
         return _PyLong_FormatWriter((_PyUnicodeWriter*)writer, obj, 10, 0);
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to