Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions Doc/c-api/tuple.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,82 @@ Tuple Objects
``*p`` is destroyed. On failure, returns ``-1`` and sets ``*p`` to ``NULL``, and
raises :exc:`MemoryError` or :exc:`SystemError`.
.. deprecated:: 3.15
Use the :ref:`PyTupleWriter API <pytuplewriter>` instead.
.. _pytuplewriter:
PyTupleWriter
-------------
The :c:type:`PyTupleWriter` API can be used to create a Python :class:`tuple`
object.
.. versionadded:: next
.. c:type:: PyTupleWriter
A tuple writer instance.
The API is **not thread safe**: a writer should only be used by a single
thread at the same time.
The instance must be destroyed by :c:func:`PyTupleWriter_Finish` on
success, or :c:func:`PyTupleWriter_Discard` on error.
.. c:function:: PyTupleWriter* PyTupleWriter_Create(Py_ssize_t size)
Create a :c:type:`PyTupleWriter` to add *size* items.
If *size* is greater than zero, preallocate *size* items. The caller is
responsible to add *size* items using :c:func:`PyTupleWriter_Add`.
On error, set an exception and return ``NULL``.
*size* must be positive or zero.
.. c:function:: PyObject* PyTupleWriter_Finish(PyTupleWriter *writer)
Finish a :c:type:`PyTupleWriter` created by
:c:func:`PyTupleWriter_Create`.
On success, return a Python :class:`tuple` object.
On error, set an exception and return ``NULL``.
The writer instance is invalid after the call in any case.
No API can be called on the writer after :c:func:`PyTupleWriter_Finish`.
.. c:function:: void PyTupleWriter_Discard(PyTupleWriter *writer)
Discard a :c:type:`PyTupleWriter` created by :c:func:`PyTupleWriter_Create`.
Do nothing if *writer* is ``NULL``.
The writer instance is invalid after the call.
No API can be called on the writer after :c:func:`PyTupleWriter_Discard`.
.. c:function:: int PyTupleWriter_Add(PyTupleWriter *writer, PyObject *item)
Add an item to *writer*.
* On success, return ``0``.
* If *item* is ``NULL`` with an exception set, return ``-1``.
* On error, set an exception and return ``-1``.
.. c:function:: int PyTupleWriter_AddSteal(PyTupleWriter *writer, PyObject *item)
Similar to :c:func:`PyTupleWriter_Add`, but take the ownership of *item*.
.. c:function:: int PyTupleWriter_AddArray(PyTupleWriter *writer, PyObject *const *array, Py_ssize_t size)
Add items from an array to *writer*.
On success, return ``0``.
On error, set an exception and return ``-1``.
.. _struct-sequence-objects:
Expand Down
14 changes: 14 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,17 @@ New features
* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
(Contributed by Victor Stinner in :gh:`111489`.)

* Add a :ref:`PyTupleWriter API <pytuplewriter>` to create :class:`tuple`:

* :c:func:`PyTupleWriter_Create`
* :c:func:`PyTupleWriter_Add`
* :c:func:`PyTupleWriter_AddSteal`
* :c:func:`PyTupleWriter_AddArray`
* :c:func:`PyTupleWriter_Finish`
* :c:func:`PyTupleWriter_Discard`

(Contributed by Victor Stinner in :gh:`139888`.)


Porting to Python 3.15
----------------------
Expand Down Expand Up @@ -915,6 +926,9 @@ Deprecated C APIs
since 3.15 and will be removed in 3.17.
(Contributed by Nikita Sobolev in :gh:`136355`.)

* Deprecate the :c:func:`_PyTuple_Resize` function:
use the new :ref:`PyTupleWriter API <pytuplewriter>` instead.
(Contributed by Victor Stinner in :gh:`139888`.)

.. Add C API deprecations above alphabetically, not here at the end.
Expand Down
20 changes: 19 additions & 1 deletion Include/cpython/tupleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ typedef struct {
PyObject *ob_item[1];
} PyTupleObject;

PyAPI_FUNC(int) _PyTuple_Resize(PyObject **, Py_ssize_t);
_Py_DEPRECATED_EXTERNALLY(3.15) PyAPI_FUNC(int) _PyTuple_Resize(PyObject **, Py_ssize_t);

/* Cast argument to PyTupleObject* type. */
#define _PyTuple_CAST(op) \
Expand Down Expand Up @@ -42,3 +42,21 @@ PyTuple_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) {
PyAPI_FUNC(PyObject*) PyTuple_FromArray(
PyObject *const *array,
Py_ssize_t size);

// --- Public PyUnicodeWriter API --------------------------------------------

typedef struct PyTupleWriter PyTupleWriter;

PyAPI_FUNC(PyTupleWriter*) PyTupleWriter_Create(Py_ssize_t size);
PyAPI_FUNC(int) PyTupleWriter_Add(
PyTupleWriter *writer,
PyObject *item);
PyAPI_FUNC(int) PyTupleWriter_AddSteal(
PyTupleWriter *writer,
PyObject *item);
PyAPI_FUNC(int) PyTupleWriter_AddArray(
PyTupleWriter *writer,
PyObject *const *array,
Py_ssize_t size);
PyAPI_FUNC(PyObject*) PyTupleWriter_Finish(PyTupleWriter *writer);
PyAPI_FUNC(void) PyTupleWriter_Discard(PyTupleWriter *writer);
2 changes: 2 additions & 0 deletions Include/internal/pycore_freelist_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern "C" {
# define Py_object_stack_chunks_MAXFREELIST 4
# define Py_unicode_writers_MAXFREELIST 1
# define Py_bytes_writers_MAXFREELIST 1
# define Py_tuple_writers_MAXFREELIST 1
# define Py_pycfunctionobject_MAXFREELIST 16
# define Py_pycmethodobject_MAXFREELIST 16
# define Py_pymethodobjects_MAXFREELIST 20
Expand Down Expand Up @@ -63,6 +64,7 @@ struct _Py_freelists {
struct _Py_freelist object_stack_chunks;
struct _Py_freelist unicode_writers;
struct _Py_freelist bytes_writers;
struct _Py_freelist tuple_writers;
struct _Py_freelist pycfunctionobject;
struct _Py_freelist pycmethodobject;
struct _Py_freelist pymethodobjects;
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ _PyTuple_Recycle(PyObject *op)
#endif
#define _PyTuple_HASH_EMPTY (_PyTuple_HASH_XXPRIME_5 + (_PyTuple_HASH_XXPRIME_5 ^ 3527539UL))

extern PyObject** _PyTupleWriter_GetItems(
PyTupleWriter *writer,
Py_ssize_t *size);

#ifdef __cplusplus
}
#endif
Expand Down
66 changes: 66 additions & 0 deletions Lib/test/test_capi/test_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,5 +302,71 @@ def my_iter():
self.assertEqual(tuples, [])


class TupleWriterTest(unittest.TestCase):
def create_writer(self, size):
return _testcapi.PyTupleWriter(size)

def test_create(self):
# Test PyTupleWriter_Create()
writer = self.create_writer(0)
self.assertIs(writer.finish(), ())

writer = self.create_writer(123)
self.assertIs(writer.finish(), ())

with self.assertRaises(SystemError):
self.create_writer(-2)

def check_add(self, name):
writer = self.create_writer(3)
add = getattr(writer, name)
for ch in 'abc':
add(ch)
self.assertEqual(writer.finish(), ('a', 'b', 'c'))

writer = self.create_writer(0)
add = getattr(writer, name)
for i in range(1024):
add(i)
self.assertEqual(writer.finish(), tuple(range(1024)))

writer = self.create_writer(1)
add = getattr(writer, name)
with self.assertRaises(SystemError):
add(NULL)

def test_add(self):
# Test PyTupleWriter_Add()
self.check_add('add')

def test_add_steal(self):
# Test PyTupleWriter_AddSteal()
self.check_add('add_steal')

def test_add_array(self):
writer = self.create_writer(0)
writer.add_array(('a', 'b', 'c'))
writer.add_array(('d', 'e'))
self.assertEqual(writer.finish(), ('a', 'b', 'c', 'd', 'e'))

writer = self.create_writer(0)
writer.add_array(tuple(range(1024)))
self.assertEqual(writer.finish(), tuple(range(1024)))

def test_discard(self):
# test the small_tuple buffer (16 items)
writer = self.create_writer(3)
writer.add(object())
writer.add(object())
# must not leak references
writer.discard()

# test the tuple code path (17 items or more)
writer = self.create_writer(1024)
writer.add_array(tuple(range(1024)))
# must not leak references
writer.discard()


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Add a :ref:`PyTupleWriter API <pytuplewriter>` to create :class:`tuple`:

* :c:func:`PyTupleWriter_Create`
* :c:func:`PyTupleWriter_Add`
* :c:func:`PyTupleWriter_AddSteal`
* :c:func:`PyTupleWriter_AddArray`
* :c:func:`PyTupleWriter_Finish`
* :c:func:`PyTupleWriter_Discard`

Patch by Victor Stinner.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deprecate the :c:func:`_PyTuple_Resize` function: use the :ref:`PyTupleWriter
API <pytuplewriter>` instead.
Loading
Loading