Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Address Serhiy's review
  • Loading branch information
vstinner committed Oct 7, 2025
commit cc47b1035a33549a0cedb01d0db999f650e915bb
6 changes: 4 additions & 2 deletions Doc/c-api/tuple.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ Tuple Objects
Create a tuple of *size* items and copy references from *array* to the new
tuple.
Comment on lines +42 to +43

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to formulate this in words more similar to PyTuple_New() or PyTuple_Pack() ("Return a new tuple object of size ... "), but this is not so important.


* Return a new reference on success.
* Set an exception and return ``NULL`` on error.
*array* can be NULL if *size* is ``0``.

On success, return a new reference.
On error, set an exception and return ``NULL``.

.. versionadded:: next

Expand Down
34 changes: 22 additions & 12 deletions Lib/test/test_capi/test_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ def test_tuple_new(self):
self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN)
self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX)

def test_tuple_fromarray(self):
# Test PyTuple_FromArray()
tuple_fromarray = _testcapi.tuple_fromarray

tup = tuple([i] for i in range(5))
copy = tuple_fromarray(tup)
self.assertEqual(copy, tup)

tup = ()
copy = tuple_fromarray(tup)
self.assertIs(copy, tup)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not the empty tuple singleton a CPython implementation detail?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole file is a CPython implementation detail (_testcapi), no?


copy = tuple_fromarray(NULL, 0)
self.assertIs(copy, ())

with self.assertRaises(ValueError):
tuple_fromarray(NULL, -1)
with self.assertRaises(ValueError):
tuple_fromarray(NULL, PY_SSIZE_T_MIN)
Comment thread
vstinner marked this conversation as resolved.
Outdated
with self.assertRaises(MemoryError):
tuple_fromarray(NULL, PY_SSIZE_T_MAX)

def test_tuple_pack(self):
# Test PyTuple_Pack()
pack = _testlimitedcapi.tuple_pack
Expand Down Expand Up @@ -279,18 +301,6 @@ def my_iter():
self.assertEqual(tuple(my_iter()), (TAG, *range(10)))
self.assertEqual(tuples, [])

def test_tuple_fromarray(self):
# Test PyTuple_FromArray()
tuple_fromarray = _testcapi.tuple_fromarray

tup = tuple(object() for _ in range(5))
copy = tuple_fromarray(tup)
self.assertEqual(copy, tup)

tup = ()
copy = tuple_fromarray(tup)
self.assertIs(copy, tup)


if __name__ == "__main__":
unittest.main()
40 changes: 13 additions & 27 deletions Modules/_testcapi/tuple.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,40 +108,26 @@ static PyObject *
tuple_fromarray(PyObject* Py_UNUSED(module), PyObject *args)
{
PyObject *src;
if (!PyArg_ParseTuple(args, "O!", &PyTuple_Type, &src)) {
Py_ssize_t size = -1;
if (!PyArg_ParseTuple(args, "O|n", &src, &size)) {
return NULL;
}

Py_ssize_t size = PyTuple_GET_SIZE(src);
PyObject **array = PyMem_Malloc(size * sizeof(PyObject**));
if (array == NULL) {
PyErr_NoMemory();
if (src != Py_None && !PyTuple_Check(src)) {
PyErr_SetString(PyExc_TypeError, "expect a tuple");
return NULL;
}
for (Py_ssize_t i = 0; i < size; i++) {
array[i] = Py_NewRef(PyTuple_GET_ITEM(src, i));
}

PyObject *tuple = PyTuple_FromArray(array, size);
if (tuple == NULL) {
goto done;
}

for (Py_ssize_t i = 0; i < size; i++) {
// check that the array is not modified
assert(array[i] == PyTuple_GET_ITEM(src, i));

// check that the array was copied properly
assert(PyTuple_GET_ITEM(tuple, i) == array[i]);
PyObject **items;
if (src != Py_None) {
items = &PyTuple_GET_ITEM(src, 0);
if (size < 0) {
Comment thread
vstinner marked this conversation as resolved.
Outdated
size = PyTuple_GET_SIZE(src);
}
}

done:
for (Py_ssize_t i = 0; i < size; i++) {
Py_DECREF(array[i]);
else {
items = NULL;
}
PyMem_Free(array);

return tuple;
return PyTuple_FromArray(items, size);
}


Expand Down
4 changes: 4 additions & 0 deletions Objects/tupleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
if (n == 0) {
return tuple_get_empty();
}
if (n < 0) {
PyErr_SetString(PyExc_ValueError, "size must be nonnegative");
return NULL;
}
Comment thread
vstinner marked this conversation as resolved.
Outdated

PyTupleObject *tuple = tuple_alloc(n);
if (tuple == NULL) {
Expand Down
Loading