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},
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy