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 `, 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 ` 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 ` 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 ` 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 ` 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 ` 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 ` 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 d7fbc9cf1a1de4a..6f407b50faf254b 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -1384,6 +1384,9 @@ New features API. (Contributed by Nathan Goldbaum and Victor Stinner in :gh:`158842`.) +* Add the :ref:`PyUnicodeWriter API ` to the limited C API. + (Contributed by Victor Stinner in :gh:`161172`.) + Porting to Python 3.16 ---------------------- @@ -1431,7 +1434,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 ` 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 ` 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)