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
77 changes: 77 additions & 0 deletions Doc/c-api/tuple.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,83 @@ 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:: int PyTupleWriter_Init(PyTupleWriter *writer, Py_ssize_t size)
Initialize 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 success, return ``0``.
On error, set an exception and return ``-1``.
*size* must be positive or zero.
.. c:function:: PyObject* PyTupleWriter_Finish(PyTupleWriter *writer)
Finish a :c:type:`PyTupleWriter` created by
:c:func:`PyTupleWriter_Init`.
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_Init`.
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 @@ -887,6 +887,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_Init`
* :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 @@ -1017,6 +1028,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
22 changes: 21 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,23 @@ 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 {
char _reserved[sizeof(Py_uintptr_t) * 20];
} PyTupleWriter;

PyAPI_FUNC(int) PyTupleWriter_Init(PyTupleWriter *writer, 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);
4 changes: 4 additions & 0 deletions Include/internal/pycore_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,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_Init()
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_Init`
* :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