https://github.com/python/cpython/commit/77c06751571d9cff9a5d763c9bca87db5e939134
commit: 77c06751571d9cff9a5d763c9bca87db5e939134
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-09-30T07:26:32Z
summary:

gh-158451: Add _PyUnicodeWriter_FinishWithSize() test (#158481)

files:
M Include/internal/pycore_unicodeobject.h
M Lib/test/test_capi/test_unicode.py
M Modules/_testcapi/unicode.c

diff --git a/Include/internal/pycore_unicodeobject.h 
b/Include/internal/pycore_unicodeobject.h
index 3b930e857e3704..112337d90968b3 100644
--- a/Include/internal/pycore_unicodeobject.h
+++ b/Include/internal/pycore_unicodeobject.h
@@ -189,7 +189,8 @@ _PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, 
Py_UCS4 ch)
     return 0;
 }
 
-extern PyObject* _PyUnicodeWriter_FinishWithSize(
+// Export for '_testcapi' shared extension
+PyAPI_FUNC(PyObject*) _PyUnicodeWriter_FinishWithSize(
     _PyUnicodeWriter *writer,
     Py_ssize_t size);
 
diff --git a/Lib/test/test_capi/test_unicode.py 
b/Lib/test/test_capi/test_unicode.py
index 6965b9dc11b57d..9c91d6eb512d2a 100644
--- a/Lib/test/test_capi/test_unicode.py
+++ b/Lib/test/test_capi/test_unicode.py
@@ -2322,6 +2322,19 @@ def test_readonly_optim_large_int(self):
                 self.assertEqual(writer.get_buffer(), expected)
                 self.assertEqual(writer.finish(), large_int_str)
 
+    def test_finish_with_size(self):
+        # Test _PyUnicodeWriter_FinishWithSize(). Truncate text requires
+        # to change the buffer kind.
+        text = 'a\xff\u20ac\U0010ffff'
+        expected = (0x10ffff, False)  # do not test the buffer size
+        for size in range(len(text) + 1):
+            writer = self.create_writer(0)
+            writer.write_utf8(text.encode(), -1)
+            self.assertEqual(writer.get_buffer()[1:], expected)
+            self.assertEqual(writer.finish_with_size(size), text[:size])
+
+        # CRASHES writer.finish_with_size(len(text) + 1)
+
 
 # Test PyUnicodeWriter_Format()
 @unittest.skipIf(ctypes is None, 'need ctypes')
diff --git a/Modules/_testcapi/unicode.c b/Modules/_testcapi/unicode.c
index ba9b205e07ef8b..5138fbab1f4ed6 100644
--- a/Modules/_testcapi/unicode.c
+++ b/Modules/_testcapi/unicode.c
@@ -795,6 +795,31 @@ writer_finish(PyObject *self_raw, PyObject 
*Py_UNUSED(args))
 }
 
 
+static PyObject*
+writer_finish_with_size(PyObject *self_raw, PyObject *args)
+{
+    PyAPI_FUNC(PyObject*) _PyUnicodeWriter_FinishWithSize(
+        _PyUnicodeWriter *writer,
+        Py_ssize_t size);
+
+    WriterObject *self = (WriterObject *)self_raw;
+    if (writer_check(self) < 0) {
+        return NULL;
+    }
+
+    Py_ssize_t size;
+    if (!PyArg_ParseTuple(args, "n", &size)) {
+        return NULL;
+    }
+
+    _PyUnicodeWriter *writer = (_PyUnicodeWriter*)self->writer;
+    PyObject *str = _PyUnicodeWriter_FinishWithSize(writer, size);
+    PyUnicodeWriter_Discard(self->writer);
+    self->writer = NULL;
+    return str;
+}
+
+
 static PyMethodDef writer_methods[] = {
     {"write_char", _PyCFunction_CAST(writer_write_char), METH_VARARGS},
     {"write_utf8", _PyCFunction_CAST(writer_write_utf8), METH_VARARGS},
@@ -809,6 +834,7 @@ static PyMethodDef writer_methods[] = {
     {"get_pointer", _PyCFunction_CAST(writer_get_pointer), METH_VARARGS},
     {"get_buffer", _PyCFunction_CAST(writer_get_buffer), METH_VARARGS},
     {"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS},
+    {"finish_with_size", _PyCFunction_CAST(writer_finish_with_size), 
METH_VARARGS},
     {NULL,              NULL}           /* sentinel */
 };
 

_______________________________________________
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