8000 Add PyIter_NextItem() function (#113) · python/pythoncapi-compat@38e2d32 · GitHub
[go: up one dir, main page]

Skip to content

Commit 38e2d32

Browse files
authored
Add PyIter_NextItem() function (#113)
1 parent 3f1c06d commit 38e2d32

File tree

4 files changed

+84
-4
lines changed

4 files changed

+84
-4
lines changed

docs/api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Python 3.14
3333

3434
See `PyLong_GetSign() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_GetSign>`__.
3535

36+
.. c:function:: PyObject* PyIter_NextItem(PyObject *sep, PyObject *iterable)
37+
38+
See `PyIter_NextItem() documentation <https://docs.python.org/dev/c-api/iter.html#c.PyIter_NextItem>`__.
39+
3640
.. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable)
3741

3842
See `PyBytes_Join() documentation <https://docs.python.org/dev/c-api/bytes.html#c.PyBytes_Join>`__.

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog
44
* 2024-10-09: Add functions:
55

66
* ``PyBytes_Join()``
7+
* ``PyIter_NextItem()``
78
* ``PyUnicode_Equal()``
89
* ``Py_HashBuffer()``
910

pythoncapi_compat.h

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,14 +1519,12 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign)
15191519
static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2)
15201520
{
15211521
if (!PyUnicode_Check(str1)) {
1522-
PyErr_Format(PyExc_TypeError,
1523-
"first argument must be str, not %s",
1522+
PyErr_Format(PyExc_TypeError, "first argument must be str, not %s",
15241523
Py_TYPE(str1)->tp_name);
15251524
return -1;
15261525
}
15271526
if (!PyUnicode_Check(str2)) {
1528-
PyErr_Format(PyExc_TypeError,
1529-
"second argument must be str, not %s",
1527+
PyErr_Format(PyExc_TypeError, "second argument must be str, not %s",
15301528
Py_TYPE(str2)->tp_name);
15311529
return -1;
15321530
}
@@ -1576,6 +1574,37 @@ static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len)
15761574
#endif
15771575

15781576

1577+
#if PY_VERSION_HEX < 0x030E00A0
1578+
static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
1579+
{
1580+
iternextfunc tp_iternext;
1581+
1582+
assert(iter != NULL);
1583+
assert(item != NULL);
1584+
1585+
tp_iternext = Py_TYPE(iter)->tp_iternext;
1586+
if (tp_iternext == NULL) {
1587+
*item = NULL;
1588+
PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'",
1589+
Py_TYPE(iter)->tp_name);
1590+
return -1;
1591+
}
1592+
1593+
if ((*item = tp_iternext(iter))) {
1594+
return 1;
1595+
}
1596+
if (!PyErr_Occurred()) {
1597+
return 0;
1598+
}
1599+
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
1600+
PyErr_Clear();
1601+
return 0;
1602+
}
1603+
return -1;
1604+
}
1605+
#endif
1606+
1607+
15791608
#ifdef __cplusplus
15801609
}
15811610
#endif

tests/test_pythoncapi_compat_cext.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,12 +1437,14 @@ static int
14371437
heapmanaged_traverse(PyObject *self, visitproc visit, void *arg)
14381438
{
14391439
Py_VISIT(Py_TYPE(self));
1440+
// Test PyObject_VisitManagedDict()
14401441
return PyObject_VisitManagedDict(self, visit, arg);
14411442
}
14421443

14431444
static int
14441445
heapmanaged_clear(PyObject *self)
14451446
{
1447+
// Test PyObject_ClearManagedDict()
14461448
PyObject_ClearManagedDict(self);
14471449
return 0;
14481450
}
@@ -1475,6 +1477,7 @@ static PyType_Spec HeapCTypeWithManagedDict_spec = {
14751477
static PyObject *
14761478
test_managed_dict(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
14771479
{
1480+
// Test PyObject_VisitManagedDict() and PyObject_ClearManagedDict()
14781481
PyObject *type = PyType_FromSpec(&HeapCTypeWithManagedDict_spec);
14791482
if (type == NULL) {
14801483
return NULL;
@@ -1926,6 +1929,48 @@ test_bytes(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
19261929
}
19271930

19281931

1932+
static PyObject *
1933+
test_iter(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1934+
{
1935+
// Test PyIter_NextItem()
1936+
PyObject *tuple = Py_BuildValue("(i)", 123);
1937+
if (tuple == NULL) {
1938+
return NULL;
1939+
}
1940+
PyObject *iter = PyObject_GetIter(tuple);
1941+
Py_DECREF(tuple);
1942+
if (iter == NULL) {
1943+
return NULL;
1944+
}
1945+
1946+
// first item
1947+
PyObject *item = UNINITIALIZED_OBJ;
1948+
assert(PyIter_NextItem(iter, &item) == 1);
1949+
{
1950+
PyObject *expected = PyLong_FromLong(123);
1951+
assert(PyObject_RichCompareBool(item, expected, Py_EQ) == 1);
1952+
assert(expected != NULL);
1953+
Py_DECREF(expected);
1954+
}
1955+
1956+
// StopIteration
1957+
item = UNINITIALIZED_OBJ;
1958+
assert(PyIter_NextItem(iter, &item) == 0);
1959+
assert(item == NULL);
1960+
assert(!PyErr_Occurred());
1961+
1962+
// non-iterable object
1963+
item = UNINITIALIZED_OBJ;
1964+
assert(PyIter_NextItem(Py_None, &item) == -1);
1965+
assert(item == NULL);
1966+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
1967+
PyErr_Clear();
1968+
1969+
Py_DECREF(iter);
1970+
Py_RETURN_NONE;
1971+
}
1972+
1973+
19291974
static struct PyMethodDef methods[] = {
19301975
{"test_object", test_object, METH_NOARGS, _Py_NULL},
19311976
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -1970,6 +2015,7 @@ static struct PyMethodDef methods[] = {
19702015
{"test_unicodewriter_format", test_unicodewriter_format, METH_NOARGS, _Py_NULL},
60EC
19712016
#endif
19722017
{"test_bytes", test_bytes, METH_NOARGS, _Py_NULL},
2018+
{"test_iter", test_iter, METH_NOARGS, _Py_NULL},
19732019
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
19742020
};
19752021

0 commit comments

Comments
 (0)
0