pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/e006c7371d8e57db26254792c67292956e88d81d

2af645b000615.css" /> gh-105201: Add PyIter_NextItem() (#122331) · python/cpython@e006c73 · GitHub
Skip to content

Commit e006c73

Browse files
gh-105201: Add PyIter_NextItem() (#122331)
Return -1 and set an exception on error; return 0 if the iterator is exhausted, and return 1 if the next item was fetched successfully. Prefer this API to PyIter_Next(), which requires the caller to use PyErr_Occurred() to differentiate between iterator exhaustion and errors. Co-authered-by: Irit Katriel <iritkatriel@yahoo.com>
1 parent 540fcc6 commit e006c73

File tree

12 files changed

+156
-40
lines changed

12 files changed

+156
-40
lines changed

Doc/c-api/iter.rst

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ There are two functions specifically for working with iterators.
1010
.. c:function:: int PyIter_Check(PyObject *o)
1111
1212
Return non-zero if the object *o* can be safely passed to
13-
:c:func:`PyIter_Next`, and ``0`` otherwise. This function always succeeds.
13+
:c:func:`PyIter_NextItem` and ``0`` otherwise.
14+
This function always succeeds.
1415
1516
.. c:function:: int PyAIter_Check(PyObject *o)
1617
@@ -19,41 +20,27 @@ There are two functions specifically for working with iterators.
1920
2021
.. versionadded:: 3.10
2122
23+
.. c:function:: int PyIter_NextItem(PyObject *iter, PyObject **item)
24+
25+
Return ``1`` and set *item* to a :term:`strong reference` of the
26+
next value of the iterator *iter* on success.
27+
Return ``0`` and set *item* to ``NULL`` if there are no remaining values.
28+
Return ``-1``, set *item* to ``NULL`` and set an exception on error.
29+
30+
.. versionadded:: 3.14
31+
2232
.. c:function:: PyObject* PyIter_Next(PyObject *o)
2333
34+
This is an older version of :c:func:`!PyIter_NextItem`,
35+
which is retained for backwards compatibility.
36+
Prefer :c:func:`PyIter_NextItem`.
37+
2438
Return the next value from the iterator *o*. The object must be an iterator
2539
according to :c:func:`PyIter_Check` (it is up to the caller to check this).
2640
If there are no remaining values, returns ``NULL`` with no exception set.
2741
If an error occurs while retrieving the item, returns ``NULL`` and passes
2842
along the exception.
2943
30-
To write a loop which iterates over an iterator, the C code should look
31-
something like this::
32-
33-
PyObject *iterator = PyObject_GetIter(obj);
34-
PyObject *item;
35-
36-
if (iterator == NULL) {
37-
/* propagate error */
38-
}
39-
40-
while ((item = PyIter_Next(iterator))) {
41-
/* do something with item */
42-
...
43-
/* release reference when done */
44-
Py_DECREF(item);
45-
}
46-
47-
Py_DECREF(iterator);
48-
49-
if (PyErr_Occurred()) {
50-
/* propagate error */
51-
}
52-
else {
53-
/* continue doing useful work */
54-
}
55-
56-
5744
.. c:type:: PySendResult
5845
5946
The enum value used to represent different results of :c:func:`PyIter_Send`.

Doc/data/refcounts.dat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,10 @@ PyAIter_Check:PyObject*:o:0:
11321132
PyIter_Next:PyObject*::+1:
11331133
PyIter_Next:PyObject*:o:0:
11341134

1135+
PyIter_NextItem:int:::
1136+
PyIter_NextItem:PyObject*:iter:0:
1137+
PyIter_NextItem:PyObject**:item:+1:
1138+
11351139
PyIter_Send:int:::
11361140
PyIter_Send:PyObject*:iter:0:
11371141
PyIter_Send:PyObject*:arg:0:

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ New Features
404404

405405
(Contributed by Victor Stinner in :gh:`119182`.)
406406

407+
* Add :c:func:`PyIter_NextItem` to replace :c:func:`PyIter_Next`,
408+
which has an ambiguous return value.
409+
(Contributed by Irit Katriel and Erlend Aasland in :gh:`105201`.)
410+
407411
Porting to Python 3.14
408412
----------------------
409413

Include/abstract.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,13 +397,23 @@ PyAPI_FUNC(int) PyIter_Check(PyObject *);
397397
This function always succeeds. */
398398
PyAPI_FUNC(int) PyAIter_Check(PyObject *);
399399

400+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
401+
/* Return 1 and set 'item' to the next item of 'iter' on success.
402+
* Return 0 and set 'item' to NULL when there are no remaining values.
403+
* Return -1, set 'item' to NULL and set an exception on error.
404+
*/
405+
PyAPI_FUNC(int) PyIter_NextItem(PyObject *iter, PyObject **item);
406+
#endif
407+
400408
/* Takes an iterator object and calls its tp_iternext slot,
401409
returning the next value.
402410
403411
If the iterator is exhausted, this returns NULL without setting an
404412
exception.
405413
406-
NULL with an exception means an error occurred. */
414+
NULL with an exception means an error occurred.
415+
416+
Prefer PyIter_NextItem() instead. */
407417
PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *);
408418

409419
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000

Lib/test/test_capi/test_abstract.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,46 @@ def test_object_generichash(self):
10071007
for obj in object(), 1, 'string', []:
10081008
self.assertEqual(generichash(obj), object.__hash__(obj))
10091009

1010+
def run_iter_api_test(self, next_func):
1011+
for data in (), [], (1, 2, 3), [1 , 2, 3], "123":
1012+
with self.subTest(data=data):
1013+
items = []
1014+
it = iter(data)
1015+
while (item := next_func(it)) is not None:
1016+
items.append(item)
1017+
self.assertEqual(items, list(data))
1018+
1019+
class Broken:
1020+
def __init__(self):
1021+
self.count = 0
1022+
1023+
def __next__(self):
1024+
if self.count < 3:
1025+
self.count += 1
1026+
return self.count
1027+
else:
1028+
raise TypeError('bad type')
1029+
1030+
it = Broken()
1031+
self.assertEqual(next_func(it), 1)
1032+
self.assertEqual(next_func(it), 2)
1033+
self.assertEqual(next_func(it), 3)
1034+
with self.assertRaisesRegex(TypeError, 'bad type'):
1035+
next_func(it)
1036+
1037+
def test_iter_next(self):
1038+
from _testcapi import PyIter_Next
1039+
self.run_iter_api_test(PyIter_Next)
1040+
# CRASHES PyIter_Next(10)
1041+
1042+
def test_iter_nextitem(self):
1043+
from _testcapi import PyIter_NextItem
1044+
self.run_iter_api_test(PyIter_NextItem)
1045+
1046+
regex = "expected.*iterator.*got.*'int'"
1047+
with self.assertRaisesRegex(TypeError, regex):
1048+
PyIter_NextItem(10)
1049+
10101050

10111051
if __name__ == "__main__":
10121052
unittest.main()

Lib/test/test_stable_abi_ctypes.py

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyIter_NextItem` to replace :c:func:`PyIter_Next`, which has an
2+
ambiguous return value. Patch by Irit Katriel and Erlend Aasland.

Misc/stable_abi.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,3 +2508,5 @@
25082508

25092509
[function.Py_TYPE]
25102510
added = '3.14'
2511+
[function.PyIter_NextItem]
2512+
added = '3.14'

Modules/_testcapi/abstract.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,33 @@ mapping_getoptionalitem(PyObject *self, PyObject *args)
129129
}
130130
}
131131

132+
static PyObject *
133+
pyiter_next(PyObject *self, PyObject *iter)
134+
{
135+
PyObject *item = PyIter_Next(iter);
136+
if (item == NULL && !PyErr_Occurred()) {
137+
Py_RETURN_NONE;
138+
}
139+
return item;
140+
}
141+
142+
static PyObject *
143+
pyiter_nextitem(PyObject *self, PyObject *iter)
144+
{
145+
PyObject *item;
146+
int rc = PyIter_NextItem(iter, &item);
147+
if (rc < 0) {
148+
assert(PyErr_Occurred());
149+
assert(item == NULL);
150+
return NULL;
151+
}
152+
assert(!PyErr_Occurred());
153+
if (item == NULL) {
154+
Py_RETURN_NONE;
155+
}
156+
return item;
157+
}
158+
132159

133160
static PyMethodDef test_methods[] = {
134161
{"object_getoptionalattr", object_getoptionalattr, METH_VARARGS},
@@ -138,6 +165,8 @@ static PyMethodDef test_methods[] = {
138165
{"mapping_getoptionalitem", mapping_getoptionalitem, METH_VARARGS},
139166
{"mapping_getoptionalitemstring", mapping_getoptionalitemstring, METH_VARARGS},
140167

168+
{"PyIter_Next", pyiter_next, METH_O},
169+
{"PyIter_NextItem", pyiter_nextitem, METH_O},
141170
{NULL},
142171
};
143172

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

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