https://github.com/python/cpython/commit/b301c298a6f173450eecb8a311c030282ca2f770
commit: b301c298a6f173450eecb8a311c030282ca2f770
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-11-04T14:33:50+01:00
summary:

gh-161172: Add PyUnicodeWriter to the limited C API (#161175)

Keep tests in _testcapi to access the deprecated function
_PyUnicodeWriter_WriteLatin1String() which is excluded from the
limited C API.

files:
A Misc/NEWS.d/next/C_API/2026-11-02-13-15-22.gh-issue-161172.U6kpuN.rst
M Doc/c-api/unicode.rst
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.14.rst
M Doc/whatsnew/3.16.rst
M Include/cpython/unicodeobject.h
M Include/unicodeobject.h
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M PC/python3dll.c

diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index 8db6ef22b00d156..b937c87b170963b 100644
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -1697,10 +1697,12 @@ They all return ``NULL`` or ``-1`` if an exception 
occurs.
       intern-related details, may change between CPython versions.
 
 
+.. _pyunicodewriter:
+
 PyUnicodeWriter
 ^^^^^^^^^^^^^^^
 
-The :c:type:`PyUnicodeWriter` API can be used to create a Python :class:`str`
+The ``PyUnicodeWriter`` API can be used to create a Python :class:`str`
 object.
 
 .. versionadded:: 3.14
@@ -1916,7 +1918,7 @@ then filling in its contents.
 They are :term:`soft deprecated`: they break the assumption that
 strings are immutable, making them hard to use correctly.
 
-Prefer using the :c:type:`PyUnicodeWriter` API,
+Prefer using the :ref:`PyUnicodeWriter API <pyunicodewriter>`,
 or one of the ``PyUnicode_From*``
 functions such as :c:func:`PyUnicode_FromStringAndSize`.
 
@@ -1971,7 +1973,7 @@ Python does not always check these requirements.
    .. versionadded:: 3.3
 
    .. soft-deprecated:: 3.16
-      Use the :c:type:`PyUnicodeWriter` API instead.
+      Use the :ref:`PyUnicodeWriter API <pyunicodewriter>` instead.
 
 
 .. c:function:: Py_ssize_t PyUnicode_CopyCharacters(PyObject *to, \
@@ -1991,7 +1993,7 @@ Python does not always check these requirements.
    .. versionadded:: 3.3
 
    .. soft-deprecated:: 3.16
-      Use the :c:type:`PyUnicodeWriter` API instead.
+      Use the :ref:`PyUnicodeWriter API <pyunicodewriter>` instead.
 
 
 .. c:function:: int PyUnicode_Resize(PyObject **unicode, Py_ssize_t length);
@@ -2012,7 +2014,7 @@ Python does not always check these requirements.
    See :ref:`pyunicode-new-mutating` for details.
 
    .. soft-deprecated:: 3.16
-      Use the :c:type:`PyUnicodeWriter` API instead.
+      Use the :ref:`PyUnicodeWriter API <pyunicodewriter>` instead.
 
 
 .. c:function:: Py_ssize_t PyUnicode_Fill(PyObject *unicode, Py_ssize_t start, 
\
@@ -2033,7 +2035,7 @@ Python does not always check these requirements.
    .. versionadded:: 3.3
 
    .. soft-deprecated:: 3.16
-      Use the :c:type:`PyUnicodeWriter` API instead.
+      Use the :ref:`PyUnicodeWriter API <pyunicodewriter>` instead.
 
 
 .. c:function:: int PyUnicode_WriteChar(PyObject *unicode, Py_ssize_t index, \
@@ -2053,4 +2055,4 @@ Python does not always check these requirements.
    .. versionadded:: 3.3
 
    .. soft-deprecated:: 3.16
-      Use the :c:type:`PyUnicodeWriter` API instead.
+      Use the :ref:`PyUnicodeWriter API <pyunicodewriter>` instead.
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 3a6832c1ff72e11..5f424391519d591 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -829,6 +829,19 @@ function,PyUnicodeTranslateError_GetStart,3.2,,
 function,PyUnicodeTranslateError_SetEnd,3.2,,
 function,PyUnicodeTranslateError_SetReason,3.2,,
 function,PyUnicodeTranslateError_SetStart,3.2,,
+function,PyUnicodeWriter_Create,3.16,,
+function,PyUnicodeWriter_DecodeUTF8Stateful,3.16,,
+function,PyUnicodeWriter_Discard,3.16,,
+function,PyUnicodeWriter_Finish,3.16,,
+function,PyUnicodeWriter_Format,3.16,,
+function,PyUnicodeWriter_WriteASCII,3.16,,
+function,PyUnicodeWriter_WriteChar,3.16,,
+function,PyUnicodeWriter_WriteRepr,3.16,,
+function,PyUnicodeWriter_WriteStr,3.16,,
+function,PyUnicodeWriter_WriteSubstring,3.16,,
+function,PyUnicodeWriter_WriteUCS4,3.16,,
+function,PyUnicodeWriter_WriteUTF8,3.16,,
+function,PyUnicodeWriter_WriteWideChar,3.16,,
 function,PyUnicode_Append,3.2,,
 function,PyUnicode_AppendAndDel,3.2,,
 function,PyUnicode_AsASCIIString,3.2,,
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index f72b1344bfdb0c8..c0123802a59f031 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -2990,7 +2990,7 @@ New features in the C API
   The function is also added to the Limited C API.
   (Contributed by Victor Stinner in :gh:`124502`.)
 
-* Add a new :c:type:`PyUnicodeWriter` API to create a Python :class:`str`
+* Add a new :ref:`PyUnicodeWriter API <pyunicodewriter>` to create a Python 
:class:`str`
   object, with the following functions:
 
   * :c:func:`PyUnicodeWriter_Create`
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index dcfa9ff5bdff127..ed87669ee4e4a5f 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -1387,6 +1387,9 @@ New features
   API.
   (Contributed by Nathan Goldbaum and Victor Stinner in :gh:`158842`.)
 
+* Add the :ref:`PyUnicodeWriter API <pyunicodewriter>` to the limited C API.
+  (Contributed by Victor Stinner in :gh:`161172`.)
+
 Porting to Python 3.16
 ----------------------
 
@@ -1434,7 +1437,7 @@ Deprecated C APIs
   :c:func:`PyUnicode_New`, :c:func:`PyUnicode_CopyCharacters`,
   :c:func:`PyUnicode_Fill`, :c:func:`PyUnicode_Resize`,
   :c:func:`PyUnicode_WRITE` and :c:func:`PyUnicode_WriteChar`.
-  Use the safer :c:type:`PyUnicodeWriter` API instead.
+  Use the safer :ref:`PyUnicodeWriter API <pyunicodewriter>` instead.
   (Contributed by Victor Stinner in :gh:`157710`.)
 
 .. Add C API deprecations above alphabetically, not here at the end.
diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h
index 54defdb2061aa96..147a33675d47b0c 100644
--- a/Include/cpython/unicodeobject.h
+++ b/Include/cpython/unicodeobject.h
@@ -471,57 +471,6 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromKindAndData(
     Py_ssize_t size);
 
 
-/* --- Public PyUnicodeWriter API ----------------------------------------- */
-
-typedef struct PyUnicodeWriter PyUnicodeWriter;
-
-PyAPI_FUNC(PyUnicodeWriter*) PyUnicodeWriter_Create(Py_ssize_t length);
-PyAPI_FUNC(void) PyUnicodeWriter_Discard(PyUnicodeWriter *writer);
-PyAPI_FUNC(PyObject*) PyUnicodeWriter_Finish(PyUnicodeWriter *writer);
-
-PyAPI_FUNC(int) PyUnicodeWriter_WriteChar(
-    PyUnicodeWriter *writer,
-    Py_UCS4 ch);
-PyAPI_FUNC(int) PyUnicodeWriter_WriteUTF8(
-    PyUnicodeWriter *writer,
-    const char *str,
-    Py_ssize_t size);
-PyAPI_FUNC(int) PyUnicodeWriter_WriteASCII(
-    PyUnicodeWriter *writer,
-    const char *str,
-    Py_ssize_t size);
-PyAPI_FUNC(int) PyUnicodeWriter_WriteWideChar(
-    PyUnicodeWriter *writer,
-    const wchar_t *str,
-    Py_ssize_t size);
-PyAPI_FUNC(int) PyUnicodeWriter_WriteUCS4(
-    PyUnicodeWriter *writer,
-    const Py_UCS4 *str,
-    Py_ssize_t size);
-
-PyAPI_FUNC(int) PyUnicodeWriter_WriteStr(
-    PyUnicodeWriter *writer,
-    PyObject *obj);
-PyAPI_FUNC(int) PyUnicodeWriter_WriteRepr(
-    PyUnicodeWriter *writer,
-    PyObject *obj);
-PyAPI_FUNC(int) PyUnicodeWriter_WriteSubstring(
-    PyUnicodeWriter *writer,
-    PyObject *str,
-    Py_ssize_t start,
-    Py_ssize_t end);
-PyAPI_FUNC(int) PyUnicodeWriter_Format(
-    PyUnicodeWriter *writer,
-    const char *format,
-    ...);
-PyAPI_FUNC(int) PyUnicodeWriter_DecodeUTF8Stateful(
-    PyUnicodeWriter *writer,
-    const char *str,            /* UTF-8 encoded string */
-    Py_ssize_t size,            /* size of string */
-    const char *errors,         /* error handling */
-    Py_ssize_t *consumed);      /* bytes consumed */
-
-
 /* --- Private _PyUnicodeWriter API --------------------------------------- */
 
 typedef struct {
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
index 29f1d1b01c161f4..fbced4896bd0143 100644
--- a/Include/unicodeobject.h
+++ b/Include/unicodeobject.h
@@ -954,7 +954,59 @@ PyAPI_FUNC(int) PyUnicode_Contains(
 
 PyAPI_FUNC(int) PyUnicode_IsIdentifier(PyObject *s);
 
-/* === Characters Type APIs =============================================== */
+
+/* === Public PyUnicodeWriter API ========================================= */
+
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 16)
+typedef struct PyUnicodeWriter PyUnicodeWriter;
+
+PyAPI_FUNC(PyUnicodeWriter*) PyUnicodeWriter_Create(Py_ssize_t length);
+PyAPI_FUNC(void) PyUnicodeWriter_Discard(PyUnicodeWriter *writer);
+PyAPI_FUNC(PyObject*) PyUnicodeWriter_Finish(PyUnicodeWriter *writer);
+
+PyAPI_FUNC(int) PyUnicodeWriter_WriteChar(
+    PyUnicodeWriter *writer,
+    Py_UCS4 ch);
+PyAPI_FUNC(int) PyUnicodeWriter_WriteUTF8(
+    PyUnicodeWriter *writer,
+    const char *str,
+    Py_ssize_t size);
+PyAPI_FUNC(int) PyUnicodeWriter_WriteASCII(
+    PyUnicodeWriter *writer,
+    const char *str,
+    Py_ssize_t size);
+PyAPI_FUNC(int) PyUnicodeWriter_WriteWideChar(
+    PyUnicodeWriter *writer,
+    const wchar_t *str,
+    Py_ssize_t size);
+PyAPI_FUNC(int) PyUnicodeWriter_WriteUCS4(
+    PyUnicodeWriter *writer,
+    const Py_UCS4 *str,
+    Py_ssize_t size);
+
+PyAPI_FUNC(int) PyUnicodeWriter_WriteStr(
+    PyUnicodeWriter *writer,
+    PyObject *obj);
+PyAPI_FUNC(int) PyUnicodeWriter_WriteRepr(
+    PyUnicodeWriter *writer,
+    PyObject *obj);
+PyAPI_FUNC(int) PyUnicodeWriter_WriteSubstring(
+    PyUnicodeWriter *writer,
+    PyObject *str,
+    Py_ssize_t start,
+    Py_ssize_t end);
+PyAPI_FUNC(int) PyUnicodeWriter_Format(
+    PyUnicodeWriter *writer,
+    const char *format,
+    ...);
+PyAPI_FUNC(int) PyUnicodeWriter_DecodeUTF8Stateful(
+    PyUnicodeWriter *writer,
+    const char *str,            /* UTF-8 encoded string */
+    Py_ssize_t size,            /* size of string */
+    const char *errors,         /* error handling */
+    Py_ssize_t *consumed);      /* bytes consumed */
+#endif  // !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 
16)
+
 
 #ifndef Py_LIMITED_API
 #  define Py_CPYTHON_UNICODEOBJECT_H
diff --git a/Lib/test/test_stable_abi_ctypes.py 
b/Lib/test/test_stable_abi_ctypes.py
index aed7451e89b1563..f9125f395de0b4b 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -802,6 +802,19 @@ def test_windows_feature_macros(self):
     "PyUnicodeTranslateError_SetEnd",
     "PyUnicodeTranslateError_SetReason",
     "PyUnicodeTranslateError_SetStart",
+    "PyUnicodeWriter_Create",
+    "PyUnicodeWriter_DecodeUTF8Stateful",
+    "PyUnicodeWriter_Discard",
+    "PyUnicodeWriter_Finish",
+    "PyUnicodeWriter_Format",
+    "PyUnicodeWriter_WriteASCII",
+    "PyUnicodeWriter_WriteChar",
+    "PyUnicodeWriter_WriteRepr",
+    "PyUnicodeWriter_WriteStr",
+    "PyUnicodeWriter_WriteSubstring",
+    "PyUnicodeWriter_WriteUCS4",
+    "PyUnicodeWriter_WriteUTF8",
+    "PyUnicodeWriter_WriteWideChar",
     "PyUnicode_Append",
     "PyUnicode_AppendAndDel",
     "PyUnicode_AsASCIIString",
diff --git 
a/Misc/NEWS.d/next/C_API/2026-11-02-13-15-22.gh-issue-161172.U6kpuN.rst 
b/Misc/NEWS.d/next/C_API/2026-11-02-13-15-22.gh-issue-161172.U6kpuN.rst
new file mode 100644
index 000000000000000..638290ef247f37a
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-11-02-13-15-22.gh-issue-161172.U6kpuN.rst
@@ -0,0 +1,2 @@
+Add the :ref:`PyUnicodeWriter API <pyunicodewriter>` to the limited C API.
+Patch by Victor Stinner.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index e79fa33319a39b0..dc892fc5f737eee 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2915,3 +2915,29 @@
     added = '3.16'
 [macro.Py_XSETREF]
     added = '3.16'
+[function.PyUnicodeWriter_Create]
+    added = '3.16'
+[function.PyUnicodeWriter_Discard]
+    added = '3.16'
+[function.PyUnicodeWriter_Finish]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteChar]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteUTF8]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteASCII]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteWideChar]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteUCS4]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteStr]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteRepr]
+    added = '3.16'
+[function.PyUnicodeWriter_WriteSubstring]
+    added = '3.16'
+[function.PyUnicodeWriter_Format]
+    added = '3.16'
+[function.PyUnicodeWriter_DecodeUTF8Stateful]
+    added = '3.16'
diff --git a/PC/python3dll.c b/PC/python3dll.c
index d1b7bafe6abb3cb..412dd5ce66cbc30 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -827,6 +827,19 @@ EXPORT_FUNC(PyUnicodeTranslateError_GetStart)
 EXPORT_FUNC(PyUnicodeTranslateError_SetEnd)
 EXPORT_FUNC(PyUnicodeTranslateError_SetReason)
 EXPORT_FUNC(PyUnicodeTranslateError_SetStart)
+EXPORT_FUNC(PyUnicodeWriter_Create)
+EXPORT_FUNC(PyUnicodeWriter_DecodeUTF8Stateful)
+EXPORT_FUNC(PyUnicodeWriter_Discard)
+EXPORT_FUNC(PyUnicodeWriter_Finish)
+EXPORT_FUNC(PyUnicodeWriter_Format)
+EXPORT_FUNC(PyUnicodeWriter_WriteASCII)
+EXPORT_FUNC(PyUnicodeWriter_WriteChar)
+EXPORT_FUNC(PyUnicodeWriter_WriteRepr)
+EXPORT_FUNC(PyUnicodeWriter_WriteStr)
+EXPORT_FUNC(PyUnicodeWriter_WriteSubstring)
+EXPORT_FUNC(PyUnicodeWriter_WriteUCS4)
+EXPORT_FUNC(PyUnicodeWriter_WriteUTF8)
+EXPORT_FUNC(PyUnicodeWriter_WriteWideChar)
 EXPORT_FUNC(PyVectorcall_Call)
 EXPORT_FUNC(PyVectorcall_NARGS)
 EXPORT_FUNC(PyWeakref_GetObject)

_______________________________________________
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