Nuitka
The Python compiler
Loading...
Searching...
No Matches
iterators.h
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3#ifndef __NUITKA_HELPER_ITERATORS_H__
4#define __NUITKA_HELPER_ITERATORS_H__
5
6#if PYTHON_VERSION >= 0x270
7// Initialize value for "tp_iternext" to compare with, needed by HAS_ITERNEXT
8// which emulates "PyCheck_Iter" but is bug free.
9extern iternextfunc default_iternext;
10extern void _initSlotIterNext(void);
11#endif
12
13// This is like "PyIter_Check" but without bugs due to shared library pointers.
14NUITKA_MAY_BE_UNUSED static inline bool HAS_ITERNEXT(PyObject *value) {
15#if PYTHON_VERSION < 0x300
16 if (!PyType_HasFeature(Py_TYPE(value), Py_TPFLAGS_HAVE_ITER)) {
17 return false;
18 }
19#endif
20
21 iternextfunc tp_iternext = Py_TYPE(value)->tp_iternext;
22
23 if (tp_iternext == NULL) {
24 return false;
25 }
26
27#if PYTHON_VERSION >= 0x270
28 return tp_iternext != default_iternext;
29#else
30 return true;
31#endif
32}
33
34// Taken from CPython implementation, so we can access and create it, needs to match
35// their definition exactly. spell-checker: ignore seqiterobject
36typedef struct {
37 PyObject_HEAD
38#if PYTHON_VERSION < 0x300
39 long it_index;
40#else
41 Py_ssize_t it_index;
42#endif
43 PyObject *it_seq;
45
46NUITKA_MAY_BE_UNUSED static PyObject *MAKE_ITERATOR_INFALLIBLE(PyObject *iterated) {
47 CHECK_OBJECT(iterated);
48
49#if PYTHON_VERSION < 0x300
50 getiterfunc tp_iter = NULL;
51 if (PyType_HasFeature(Py_TYPE(iterated), Py_TPFLAGS_HAVE_ITER)) {
52 tp_iter = Py_TYPE(iterated)->tp_iter;
53 }
54#else
55 getiterfunc tp_iter = Py_TYPE(iterated)->tp_iter;
56#endif
57 if (tp_iter) {
58 PyObject *result = (*tp_iter)(iterated);
59 CHECK_OBJECT(result);
60 assert(HAS_ITERNEXT(result));
61
62 return result;
63 } else {
64 assert(PySequence_Check(iterated));
65
66 seqiterobject *result = (seqiterobject *)Nuitka_GC_New(&PySeqIter_Type);
67 assert(result);
68
69 result->it_index = 0;
70 Py_INCREF(iterated);
71 result->it_seq = iterated;
72
73 Nuitka_GC_Track(result);
74
75 return (PyObject *)result;
76 }
77}
78
79NUITKA_MAY_BE_UNUSED static PyObject *MAKE_ITERATOR(PyThreadState *tstate, PyObject *iterated) {
80 CHECK_OBJECT(iterated);
81
82#if _NUITKA_EXPERIMENTAL_DISABLE_ITERATOR_OPT
83 return PyObject_GetIter(iterated);
84#else
85
86#if PYTHON_VERSION < 0x300
87 getiterfunc tp_iter = NULL;
88 if (PyType_HasFeature(Py_TYPE(iterated), Py_TPFLAGS_HAVE_ITER)) {
89 tp_iter = Py_TYPE(iterated)->tp_iter;
90 }
91#else
92 getiterfunc tp_iter = Py_TYPE(iterated)->tp_iter;
93#endif
94
95 if (tp_iter) {
96 PyObject *result = (*tp_iter)(iterated);
97
98 if (unlikely(result == NULL)) {
99 return NULL;
100 } else if (unlikely(!HAS_ITERNEXT(result))) {
101 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT("iter() returned non-iterator of type '%s'", result);
102
103 Py_DECREF(result);
104
105 return NULL;
106 } else {
107 return result;
108 }
109 } else if (PySequence_Check(iterated)) {
110 seqiterobject *result = (seqiterobject *)Nuitka_GC_New(&PySeqIter_Type);
111 assert(result);
112
113 result->it_index = 0;
114 Py_INCREF(iterated);
115 result->it_seq = iterated;
116
117 Nuitka_GC_Track(result);
118
119 return (PyObject *)result;
120 } else {
121 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT("'%s' object is not iterable", iterated);
122
123 return NULL;
124 }
125#endif
126}
127
128#if PYTHON_VERSION >= 0x370
129
130NUITKA_MAY_BE_UNUSED static PyObject *MAKE_UNPACK_ITERATOR(PyObject *iterated) {
131 CHECK_OBJECT(iterated);
132
133 getiterfunc tp_iter = Py_TYPE(iterated)->tp_iter;
134
135 if (tp_iter) {
136 PyObject *result = (*tp_iter)(iterated);
137
138 if (unlikely(result == NULL)) {
139 return NULL;
140 } else if (unlikely(!HAS_ITERNEXT(result))) {
141 PyErr_Format(PyExc_TypeError, "iter() returned non-iterator of type '%s'", Py_TYPE(result)->tp_name);
142
143 Py_DECREF(result);
144
145 return NULL;
146 } else {
147 return result;
148 }
149 } else if (PySequence_Check(iterated)) {
150 seqiterobject *result = (seqiterobject *)Nuitka_GC_New(&PySeqIter_Type);
151 assert(result);
152
153 result->it_index = 0;
154 Py_INCREF(iterated);
155 result->it_seq = iterated;
156
157 Nuitka_GC_Track(result);
158
159 return (PyObject *)result;
160 } else {
161 PyErr_Format(PyExc_TypeError, "cannot unpack non-iterable %s object", Py_TYPE(iterated)->tp_name);
162
163 return NULL;
164 }
165}
166
167#endif
168
169NUITKA_MAY_BE_UNUSED static PyObject *ITERATOR_NEXT_ITERATOR(PyObject *iterator) {
170 CHECK_OBJECT(iterator);
171 iternextfunc iternext = Py_TYPE(iterator)->tp_iternext;
172 assert(iternext != NULL);
173
174#if _NUITKA_EXPERIMENTAL_DISABLE_ITERATOR_OPT
175 return PyIter_Next(iterator);
176#else
177 PyObject *result = (*iternext)(iterator);
178 CHECK_OBJECT_X(result);
179 return result;
180#endif
181}
182
183NUITKA_MAY_BE_UNUSED static PyObject *ITERATOR_NEXT(PyObject *iterator) {
184 CHECK_OBJECT(iterator);
185
186#if _NUITKA_EXPERIMENTAL_DISABLE_ITERATOR_OPT
187 return PyIter_Next(iterator);
188#else
189 iternextfunc iternext = Py_TYPE(iterator)->tp_iternext;
190
191 if (unlikely(iternext == NULL)) {
192 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT(
193#if PYTHON_VERSION < 0x300 && defined(_NUITKA_FULL_COMPAT)
194 "%s object is not an iterator",
195#else
196 "'%s' object is not an iterator",
197#endif
198 iterator);
199
200 return NULL;
201 }
202
203 PyObject *result = (*iternext)(iterator);
204
205 CHECK_OBJECT_X(result);
206
207 return result;
208#endif
209}
210
211NUITKA_MAY_BE_UNUSED static PyObject *BUILTIN_NEXT2(PyThreadState *tstate, PyObject *iterator,
212 PyObject *default_value) {
213 CHECK_OBJECT(iterator);
214 CHECK_OBJECT(default_value);
215
216 PyObject *result = (*Py_TYPE(iterator)->tp_iternext)(iterator);
217
218 if (unlikely(result == NULL)) {
219 bool stop_iteration_error = CHECK_AND_CLEAR_STOP_ITERATION_OCCURRED(tstate);
220
221 if (unlikely(stop_iteration_error == false)) {
222 return NULL;
223 }
224
225 Py_INCREF(default_value);
226 return default_value;
227 }
228
229 CHECK_OBJECT(result);
230 return result;
231}
232
233// For cases, where no exception raising is needed, because we know it at compile time.
234NUITKA_MAY_BE_UNUSED static PyObject *UNPACK_NEXT_INFALLIBLE(PyObject *iterator) {
235 CHECK_OBJECT(iterator);
236 assert(HAS_ITERNEXT(iterator));
237
238 PyObject *result = (*Py_TYPE(iterator)->tp_iternext)(iterator);
239 CHECK_OBJECT(result);
240 return result;
241}
242
243#if PYTHON_VERSION < 0x350
244NUITKA_MAY_BE_UNUSED static PyObject *UNPACK_NEXT(PyThreadState *tstate,
245 struct Nuitka_ExceptionPreservationItem *exception_state,
246 PyObject *iterator, int seq_size_so_far)
247#else
248NUITKA_MAY_BE_UNUSED static PyObject *UNPACK_NEXT(PyThreadState *tstate,
249 struct Nuitka_ExceptionPreservationItem *exception_state,
250 PyObject *iterator, int seq_size_so_far, int expected)
251#endif
252{
253 CHECK_OBJECT(iterator);
254 assert(HAS_ITERNEXT(iterator));
255
256 PyObject *result = (*Py_TYPE(iterator)->tp_iternext)(iterator);
257
258 if (unlikely(result == NULL)) {
259 bool had_stop_iteration = true;
260
261 PyObject *error = GET_ERROR_OCCURRED(tstate);
262
263 if (error != NULL) {
264 if (EXCEPTION_MATCH_BOOL_SINGLE(tstate, error, PyExc_StopIteration)) {
265 CLEAR_ERROR_OCCURRED(tstate);
266 } else {
267 had_stop_iteration = false;
268 }
269 }
270
271 if (had_stop_iteration) {
272#if PYTHON_VERSION < 0x350
273 if (seq_size_so_far == 1) {
274 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_STR(tstate, exception_state, PyExc_ValueError,
275 "need more than 1 value to unpack");
276 } else {
277 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_FORMAT1(
278 tstate, exception_state, PyExc_ValueError, "need more than %d values to unpack", seq_size_so_far);
279 }
280#else
281 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_FORMAT2(tstate, exception_state, PyExc_ValueError,
282 "not enough values to unpack (expected %d, got %d)",
283 expected, seq_size_so_far);
284#endif
285 } else {
286 assert(HAS_ERROR_OCCURRED(tstate));
287 FETCH_ERROR_OCCURRED_STATE(tstate, exception_state);
288 }
289
290 return NULL;
291 }
292
293 CHECK_OBJECT(result);
294
295 return result;
296}
297
298#if PYTHON_VERSION >= 0x350
299// Different error message for starred unpacks
300NUITKA_MAY_BE_UNUSED static PyObject *UNPACK_NEXT_STARRED(PyThreadState *tstate,
301 struct Nuitka_ExceptionPreservationItem *exception_state,
302 PyObject *iterator, int seq_size_so_far, int expected) {
303 CHECK_OBJECT(iterator);
304 assert(HAS_ITERNEXT(iterator));
305
306 PyObject *result = (*Py_TYPE(iterator)->tp_iternext)(iterator);
307
308 if (unlikely(result == NULL)) {
309 FETCH_ERROR_OCCURRED_STATE(tstate, exception_state);
310
311 // TODO: Specialize more for StopIteration and maybe derived ones do not
312 // count or do they?
313 if (!HAS_EXCEPTION_STATE(exception_state) ||
314 EXCEPTION_STATE_MATCH_BOOL_SINGLE(tstate, exception_state, PyExc_StopIteration)) {
315 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_FORMAT2(
316 tstate, exception_state, PyExc_ValueError, "not enough values to unpack (expected at least %d, got %d)",
317 expected, seq_size_so_far);
318 }
319
320 return NULL;
321 }
322
323 CHECK_OBJECT(result);
324
325 return result;
326}
327#endif
328
329NUITKA_MAY_BE_UNUSED static bool UNPACK_ITERATOR_CHECK(PyThreadState *tstate,
330 struct Nuitka_ExceptionPreservationItem *exception_state,
331 PyObject *iterator, int expected) {
332 PyObject *attempt = (*(Py_TYPE(iterator)->tp_iternext))(iterator);
333
334 if (likely(attempt == NULL)) {
335 PyObject *error = GET_ERROR_OCCURRED(tstate);
336
337 if (error != NULL) {
338 if (EXCEPTION_MATCH_BOOL_SINGLE(tstate, error, PyExc_StopIteration)) {
339 CLEAR_ERROR_OCCURRED(tstate);
340 } else {
341 FETCH_ERROR_OCCURRED_STATE(tstate, exception_state);
342 return false;
343 }
344 }
345
346 return true;
347 } else {
348 Py_DECREF(attempt);
349
350#if PYTHON_VERSION < 0x300
351 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_STR(tstate, exception_state, PyExc_ValueError,
352 "too many values to unpack");
353#else
354 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_FORMAT1(tstate, exception_state, PyExc_ValueError,
355 "too many values to unpack (expected %d)", expected);
356#endif
357 return false;
358 }
359}
360
361#endif
362
363// Part of "Nuitka", an optimizing Python compiler that is compatible and
364// integrates with CPython, but also works on its own.
365//
366// Licensed under the Apache License, Version 2.0 (the "License");
367// you may not use this file except in compliance with the License.
368// You may obtain a copy of the License at
369//
370// http://www.apache.org/licenses/LICENSE-2.0
371//
372// Unless required by applicable law or agreed to in writing, software
373// distributed under the License is distributed on an "AS IS" BASIS,
374// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
375// See the License for the specific language governing permissions and
376// limitations under the License.
Definition exceptions.h:712
Definition iterators.h:36