From 66eaf84b8c2db6afad7ec49eb296a019a2f377df Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 26 Mar 2014 19:27:58 +0000 Subject: [PATCH] py: Replace mp_const_stop_iteration object with MP_OBJ_NULL. --- py/builtin.c | 14 ++++++------ py/emitnative.c | 2 +- py/obj.h | 3 +-- py/objarray.c | 4 ++-- py/objdict.c | 16 ++++++------- py/objenumerate.c | 5 +++-- py/objfilter.c | 5 +++-- py/objgenerator.c | 8 +++---- py/objgetitemiter.c | 5 +++-- py/objlist.c | 4 ++-- py/objmap.c | 5 +++-- py/objnone.c | 5 ----- py/objrange.c | 3 ++- py/objset.c | 16 ++++++------- py/objstr.c | 6 ++--- py/objtuple.c | 4 ++-- py/objzip.c | 6 ++--- py/runtime.c | 40 +++++++++++++++++++++++++++++---- py/runtime.h | 3 ++- py/stream.c | 2 +- py/vm.c | 4 ++-- tests/basics/iter1.py | 52 +++++++++++++++++++++++++++++++++++++++++++ tests/basics/iter2.py | 23 +++++++++++++++++++ 23 files changed, 171 insertions(+), 64 deletions(-) create mode 100644 tests/basics/iter1.py create mode 100644 tests/basics/iter2.py diff --git a/py/builtin.c b/py/builtin.c index 42bb980f1f..281c57dd43 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -103,7 +103,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_abs_obj, mp_builtin_abs); STATIC mp_obj_t mp_builtin_all(mp_obj_t o_in) { mp_obj_t iterable = rt_getiter(o_in); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (!rt_is_true(item)) { return mp_const_false; } @@ -116,7 +116,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_all_obj, mp_builtin_all); STATIC mp_obj_t mp_builtin_any(mp_obj_t o_in) { mp_obj_t iterable = rt_getiter(o_in); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (rt_is_true(item)) { return mp_const_true; } @@ -232,7 +232,7 @@ STATIC mp_obj_t mp_builtin_max(uint n_args, const mp_obj_t *args) { mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t max_obj = NULL; mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (max_obj == NULL || mp_obj_less(max_obj, item)) { max_obj = item; } @@ -261,7 +261,7 @@ STATIC mp_obj_t mp_builtin_min(uint n_args, const mp_obj_t *args) { mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t min_obj = NULL; mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (min_obj == NULL || mp_obj_less(item, min_obj)) { min_obj = item; } @@ -285,8 +285,8 @@ STATIC mp_obj_t mp_builtin_min(uint n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR(mp_builtin_min_obj, 1, mp_builtin_min); STATIC mp_obj_t mp_builtin_next(mp_obj_t o) { - mp_obj_t ret = rt_iternext(o); - if (ret == mp_const_stop_iteration) { + mp_obj_t ret = rt_iternext_allow_raise(o); + if (ret == MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception(&mp_type_StopIteration)); } else { return ret; @@ -362,7 +362,7 @@ STATIC mp_obj_t mp_builtin_sum(uint n_args, const mp_obj_t *args) { } mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { value = rt_binary_op(RT_BINARY_OP_ADD, value, item); } return value; diff --git a/py/emitnative.c b/py/emitnative.c index 3ed415b04c..75086de155 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -987,7 +987,7 @@ STATIC void emit_native_for_iter(emit_t *emit, int label) { emit_access_stack(emit, 1, &vtype, REG_ARG_1); assert(vtype == VTYPE_PYOBJ); emit_call(emit, RT_F_ITERNEXT, rt_iternext); - ASM_MOV_IMM_TO_REG((machine_uint_t)mp_const_stop_iteration, REG_TEMP1); + ASM_MOV_IMM_TO_REG((machine_uint_t)MP_OBJ_NULL, REG_TEMP1); #if N_X64 asm_x64_cmp_r64_with_r64(emit->as, REG_RET, REG_TEMP1); asm_x64_jcc_label(emit->as, JCC_JE, label); diff --git a/py/obj.h b/py/obj.h index 19d32198b1..7c3ef8c55b 100644 --- a/py/obj.h +++ b/py/obj.h @@ -152,7 +152,7 @@ struct _mp_obj_type_t { mp_store_item_fun_t store_item; mp_fun_1_t getiter; - mp_fun_1_t iternext; + mp_fun_1_t iternext; // may return MP_OBJ_NULL as an optimisation instead of raising StopIteration() (with no args) // Alternatively, pointer(s) to interfaces to save space // in mp_obj_type_t at the expense of extra pointer and extra dereference @@ -221,7 +221,6 @@ extern const mp_obj_t mp_const_false; extern const mp_obj_t mp_const_true; extern const mp_obj_t mp_const_empty_tuple; extern const mp_obj_t mp_const_ellipsis; -extern const mp_obj_t mp_const_stop_iteration; // special object indicating end of iteration (not StopIteration exception!) // General API for objects diff --git a/py/objarray.c b/py/objarray.c index 69f60e000a..d77a101075 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -64,7 +64,7 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) { mp_obj_t iterable = rt_getiter(initializer); mp_obj_t item; int i = 0; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (len == 0) { array_append(array, item); } else { @@ -212,7 +212,7 @@ STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) { if (self->cur < self->array->len) { return mp_binary_get_val(self->array->typecode, self->array->items, self->cur++); } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } diff --git a/py/objdict.c b/py/objdict.c index 17cc499c5e..ead2d7442f 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -106,7 +106,7 @@ mp_obj_t dict_it_iternext(mp_obj_t self_in) { if (next != NULL) { return next->key; } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } @@ -171,7 +171,7 @@ STATIC mp_obj_t dict_fromkeys(uint n_args, const mp_obj_t *args) { self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len)); } - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; } @@ -266,14 +266,14 @@ STATIC mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) { /* TODO: check for the "keys" method */ mp_obj_t iter = rt_getiter(iterable); mp_obj_t next = NULL; - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { mp_obj_t inneriter = rt_getiter(next); mp_obj_t key = rt_iternext(inneriter); mp_obj_t value = rt_iternext(inneriter); mp_obj_t stop = rt_iternext(inneriter); - if (key == mp_const_stop_iteration - || value == mp_const_stop_iteration - || stop != mp_const_stop_iteration) { + if (key == MP_OBJ_NULL + || value == MP_OBJ_NULL + || stop != MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception_msg( &mp_type_ValueError, "dictionary update sequence has the wrong length")); @@ -335,7 +335,7 @@ STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) { return mp_const_none; } } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } @@ -364,7 +364,7 @@ STATIC void dict_view_print(void (*print)(void *env, const char *fmt, ...), void print(env, "(["); mp_obj_t *self_iter = dict_view_getiter(self); mp_obj_t *next = NULL; - while ((next = dict_view_it_iternext(self_iter)) != mp_const_stop_iteration) { + while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_NULL) { if (!first) { print(env, ", "); } diff --git a/py/objenumerate.c b/py/objenumerate.c index 1862eb74e1..8b33cafd7c 100644 --- a/py/objenumerate.c +++ b/py/objenumerate.c @@ -1,3 +1,4 @@ +#include #include #include "misc.h" @@ -37,8 +38,8 @@ STATIC mp_obj_t enumerate_iternext(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &enumerate_type)); mp_obj_enumerate_t *self = self_in; mp_obj_t next = rt_iternext(self->iter); - if (next == mp_const_stop_iteration) { - return mp_const_stop_iteration; + if (next == MP_OBJ_NULL) { + return MP_OBJ_NULL; } else { mp_obj_t items[] = {MP_OBJ_NEW_SMALL_INT(self->cur++), next}; return mp_obj_new_tuple(2, items); diff --git a/py/objfilter.c b/py/objfilter.c index ea76b99070..6ba1303a3d 100644 --- a/py/objfilter.c +++ b/py/objfilter.c @@ -1,3 +1,4 @@ +#include #include #include "nlr.h" @@ -29,7 +30,7 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &filter_type)); mp_obj_filter_t *self = self_in; mp_obj_t next; - while ((next = rt_iternext(self->iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(self->iter)) != MP_OBJ_NULL) { mp_obj_t val; if (self->fun != mp_const_none) { val = rt_call_function_n_kw(self->fun, 1, 0, &next); @@ -40,7 +41,7 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) { return next; } } - return mp_const_stop_iteration; + return MP_OBJ_NULL; } const mp_obj_type_t filter_type = { diff --git a/py/objgenerator.c b/py/objgenerator.c index eed5a55111..0a4dcd7b17 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -78,7 +78,7 @@ mp_obj_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw mp_obj_gen_instance_t *self = self_in; if (self->ip == 0) { *ret_kind = MP_VM_RETURN_NORMAL; - return mp_const_stop_iteration; + return MP_OBJ_NULL; } if (self->sp == self->state - 1) { if (send_value != mp_const_none) { @@ -122,7 +122,7 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o case MP_VM_RETURN_NORMAL: // Optimize return w/o value in case generator is used in for loop if (ret == mp_const_none) { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } else { nlr_jump(mp_obj_new_exception_args(&mp_type_StopIteration, 1, &ret)); } @@ -145,7 +145,7 @@ mp_obj_t gen_instance_iternext(mp_obj_t self_in) { STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { mp_obj_t ret = gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL); - if (ret == mp_const_stop_iteration) { + if (ret == MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception(&mp_type_StopIteration)); } else { return ret; @@ -156,7 +156,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); STATIC mp_obj_t gen_instance_throw(uint n_args, const mp_obj_t *args) { mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, n_args == 2 ? args[1] : args[2]); - if (ret == mp_const_stop_iteration) { + if (ret == MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception(&mp_type_StopIteration)); } else { return ret; diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c index 08ef66f29f..fff3f3807f 100644 --- a/py/objgetitemiter.c +++ b/py/objgetitemiter.c @@ -1,3 +1,4 @@ +#include #include "nlr.h" #include "misc.h" @@ -25,8 +26,8 @@ STATIC mp_obj_t it_iternext(mp_obj_t self_in) { } else { // an exception was raised if (mp_obj_get_type(nlr.ret_val) == &mp_type_StopIteration) { - // return mp_const_stop_iteration instead of raising StopIteration - return mp_const_stop_iteration; + // return MP_OBJ_NULL instead of raising StopIteration + return MP_OBJ_NULL; } else { // re-raise exception nlr_jump(nlr.ret_val); diff --git a/py/objlist.c b/py/objlist.c index f43670d603..3083e23bb1 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -53,7 +53,7 @@ STATIC mp_obj_t list_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t list = mp_obj_new_list(0, NULL); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { mp_obj_list_append(list, item); } return list; @@ -401,7 +401,7 @@ mp_obj_t list_it_iternext(mp_obj_t self_in) { self->cur += 1; return o_out; } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } diff --git a/py/objmap.c b/py/objmap.c index 2b356ec4ea..cc7eae235b 100644 --- a/py/objmap.c +++ b/py/objmap.c @@ -1,3 +1,4 @@ +#include #include #include "nlr.h" @@ -40,9 +41,9 @@ STATIC mp_obj_t map_iternext(mp_obj_t self_in) { for (int i = 0; i < self->n_iters; i++) { mp_obj_t next = rt_iternext(self->iters[i]); - if (next == mp_const_stop_iteration) { + if (next == MP_OBJ_NULL) { m_del(mp_obj_t, nextses, self->n_iters); - return mp_const_stop_iteration; + return MP_OBJ_NULL; } nextses[i] = next; } diff --git a/py/objnone.c b/py/objnone.c index a4370efebc..1ec3d46fd4 100644 --- a/py/objnone.c +++ b/py/objnone.c @@ -31,8 +31,3 @@ const mp_obj_type_t none_type = { STATIC const mp_obj_none_t none_obj = {{&none_type}}; const mp_obj_t mp_const_none = (mp_obj_t)&none_obj; - -// the stop-iteration object just needs to be something unique -// it's not the StopIteration exception -STATIC const mp_obj_none_t stop_it_obj = {{&none_type}}; -const mp_obj_t mp_const_stop_iteration = (mp_obj_t)&stop_it_obj; diff --git a/py/objrange.c b/py/objrange.c index f80bc08602..c527dfa89e 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -1,3 +1,4 @@ +#include #include "nlr.h" #include "misc.h" @@ -55,7 +56,7 @@ STATIC mp_obj_t range_it_iternext(mp_obj_t o_in) { o->cur += o->step; return o_out; } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } diff --git a/py/objset.c b/py/objset.c index 437bae9bf8..4f0776f893 100644 --- a/py/objset.c +++ b/py/objset.c @@ -59,7 +59,7 @@ STATIC mp_obj_t set_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_ mp_obj_t set = mp_obj_new_set(0, NULL); mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { mp_obj_set_store(set, item); } return set; @@ -89,7 +89,7 @@ STATIC mp_obj_t set_it_iternext(mp_obj_t self_in) { } } - return mp_const_stop_iteration; + return MP_OBJ_NULL; } STATIC mp_obj_t set_getiter(mp_obj_t set_in) { @@ -162,7 +162,7 @@ STATIC mp_obj_t set_diff_int(int n_args, const mp_obj_t *args, bool update) { } else { mp_obj_t iter = rt_getiter(other); mp_obj_t next; - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { set_discard(self, next); } } @@ -193,7 +193,7 @@ STATIC mp_obj_t set_intersect_int(mp_obj_t self_in, mp_obj_t other, bool update) mp_obj_t iter = rt_getiter(other); mp_obj_t next; - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) { set_add(out, next); } @@ -225,7 +225,7 @@ STATIC mp_obj_t set_isdisjoint(mp_obj_t self_in, mp_obj_t other) { mp_obj_t iter = rt_getiter(other); mp_obj_t next; - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) { return mp_const_false; } @@ -258,7 +258,7 @@ STATIC mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool } else { mp_obj_t iter = set_getiter(self); mp_obj_t next; - while ((next = set_it_iternext(iter)) != mp_const_stop_iteration) { + while ((next = set_it_iternext(iter)) != MP_OBJ_NULL) { if (!mp_set_lookup(&other->set, next, MP_MAP_LOOKUP)) { out = false; break; @@ -332,7 +332,7 @@ STATIC mp_obj_t set_symmetric_difference_update(mp_obj_t self_in, mp_obj_t other mp_obj_set_t *self = self_in; mp_obj_t iter = rt_getiter(other_in); mp_obj_t next; - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_REMOVE_IF_FOUND | MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); } return mp_const_none; @@ -350,7 +350,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_obj, set_symmetric_dif STATIC void set_update_int(mp_obj_set_t *self, mp_obj_t other_in) { mp_obj_t iter = rt_getiter(other_in); mp_obj_t next; - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); } } diff --git a/py/objstr.c b/py/objstr.c index 00586a3b3b..3ace7b051a 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -161,7 +161,7 @@ STATIC mp_obj_t bytes_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (len == -1) { vstr_add_char(vstr, MP_OBJ_SMALL_INT_VALUE(item)); } else { @@ -877,7 +877,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) { self->cur += 1; return o_out; } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } @@ -895,7 +895,7 @@ STATIC mp_obj_t bytes_it_iternext(mp_obj_t self_in) { self->cur += 1; return o_out; } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } diff --git a/py/objtuple.c b/py/objtuple.c index 3378b4ec1c..68f5abefdf 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -53,7 +53,7 @@ STATIC mp_obj_t tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m mp_obj_t iterable = rt_getiter(args[0]); mp_obj_t item; - while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) { + while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) { if (len >= alloc) { items = m_renew(mp_obj_t, items, alloc, alloc * 2); alloc *= 2; @@ -245,7 +245,7 @@ STATIC mp_obj_t tuple_it_iternext(mp_obj_t self_in) { self->cur += 1; return o_out; } else { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } } diff --git a/py/objzip.c b/py/objzip.c index b939ff6cb9..553dabba74 100644 --- a/py/objzip.c +++ b/py/objzip.c @@ -34,16 +34,16 @@ STATIC mp_obj_t zip_iternext(mp_obj_t self_in) { mp_obj_zip_t *self = self_in; mp_obj_t *items; if (self->n_iters == 0) { - return mp_const_stop_iteration; + return MP_OBJ_NULL; } mp_obj_t o = mp_obj_new_tuple(self->n_iters, NULL); mp_obj_tuple_get(o, NULL, &items); for (int i = 0; i < self->n_iters; i++) { mp_obj_t next = rt_iternext(self->iters[i]); - if (next == mp_const_stop_iteration) { + if (next == MP_OBJ_NULL) { mp_obj_tuple_del(o); - return mp_const_stop_iteration; + return MP_OBJ_NULL; } items[i] = next; } diff --git a/py/runtime.c b/py/runtime.c index 3e0ef98d8b..cc5398552e 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -616,7 +616,7 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) { /* second attempt, walk the iterator */ mp_obj_t next = NULL; mp_obj_t iter = rt_getiter(rhs); - while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + while ((next = rt_iternext(iter)) != MP_OBJ_NULL) { if (mp_obj_equal(next, lhs)) { return mp_const_true; } @@ -778,12 +778,12 @@ void rt_unpack_sequence(mp_obj_t seq_in, uint num, mp_obj_t *items) { for (seq_len = 0; seq_len < num; seq_len++) { mp_obj_t el = rt_iternext(iterable); - if (el == mp_const_stop_iteration) { + if (el == MP_OBJ_NULL) { goto too_short; } items[num - 1 - seq_len] = el; } - if (rt_iternext(iterable) != mp_const_stop_iteration) { + if (rt_iternext(iterable) != MP_OBJ_NULL) { goto too_long; } } @@ -944,7 +944,9 @@ mp_obj_t rt_getiter(mp_obj_t o_in) { } } -mp_obj_t rt_iternext(mp_obj_t o_in) { +// may return MP_OBJ_NULL as an optimisation instead of raise StopIteration() +// may also raise StopIteration() +mp_obj_t rt_iternext_allow_raise(mp_obj_t o_in) { mp_obj_type_t *type = mp_obj_get_type(o_in); if (type->iternext != NULL) { return type->iternext(o_in); @@ -961,6 +963,36 @@ mp_obj_t rt_iternext(mp_obj_t o_in) { } } +// will always return MP_OBJ_NULL instead of raising StopIteration() (or any subclass thereof) +// may raise other exceptions +mp_obj_t rt_iternext(mp_obj_t o_in) { + mp_obj_type_t *type = mp_obj_get_type(o_in); + if (type->iternext != NULL) { + return type->iternext(o_in); + } else { + // check for __next__ method + mp_obj_t dest[2]; + rt_load_method_maybe(o_in, MP_QSTR___next__, dest); + if (dest[0] != MP_OBJ_NULL) { + // __next__ exists, call it and return its result + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t ret = rt_call_method_n_kw(0, 0, dest); + nlr_pop(); + return ret; + } else { + if (mp_obj_is_subclass_fast(mp_obj_get_type(nlr.ret_val), &mp_type_StopIteration)) { + return MP_OBJ_NULL; + } else { + nlr_jump(nlr.ret_val); + } + } + } else { + nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "'%s' object is not an iterator", mp_obj_get_type_str(o_in))); + } + } +} + mp_obj_t rt_make_raise_obj(mp_obj_t o) { DEBUG_printf("raise %p\n", o); if (mp_obj_is_exception_type(o)) { diff --git a/py/runtime.h b/py/runtime.h index 3980e50cc0..f5e4a49cf8 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -39,7 +39,8 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest); void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t val); void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val); mp_obj_t rt_getiter(mp_obj_t o); -mp_obj_t rt_iternext(mp_obj_t o); +mp_obj_t rt_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_NULL instead of raising StopIteration() +mp_obj_t rt_iternext(mp_obj_t o); // will always return MP_OBJ_NULL instead of raising StopIteration(...) mp_obj_t rt_make_raise_obj(mp_obj_t o); mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); mp_obj_t rt_import_from(mp_obj_t module, qstr name); diff --git a/py/stream.c b/py/stream.c index 7c97ee10c3..a0a2c68f80 100644 --- a/py/stream.c +++ b/py/stream.c @@ -154,7 +154,7 @@ mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) { if (sz != 0) { return l_in; } - return mp_const_stop_iteration; + return MP_OBJ_NULL; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read); diff --git a/py/vm.c b/py/vm.c index 8a0262a841..e34acb488b 100644 --- a/py/vm.c +++ b/py/vm.c @@ -430,8 +430,8 @@ unwind_jump: case MP_BC_FOR_ITER: DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward - obj1 = rt_iternext(TOP()); - if (obj1 == mp_const_stop_iteration) { + obj1 = rt_iternext_allow_raise(TOP()); + if (obj1 == MP_OBJ_NULL) { --sp; // pop the exhausted iterator ip += unum; // jump to after for-block } else { diff --git a/tests/basics/iter1.py b/tests/basics/iter1.py new file mode 100644 index 0000000000..c2ef86a635 --- /dev/null +++ b/tests/basics/iter1.py @@ -0,0 +1,52 @@ +# test user defined iterators + +class MyStopIteration(StopIteration): + pass + +class myiter: + def __init__(self, i): + self.i = i + + def __iter__(self): + return self + + def __next__(self): + if self.i <= 0: + # stop in the usual way + raise StopIteration + elif self.i == 10: + # stop with an argument + raise StopIteration(42) + elif self.i == 20: + # raise a non-stop exception + raise TypeError + elif self.i == 30: + # raise a user-defined stop iteration + print('raising MyStopIteration') + raise MyStopIteration + else: + # return the next value + self.i -= 1 + return self.i + +for i in myiter(5): + print(i) + +for i in myiter(12): + print(i) + +try: + for i in myiter(22): + print(i) +except TypeError: + print('raised TypeError') + +try: + for i in myiter(5): + print(i) + raise StopIteration +except StopIteration: + print('raised StopIteration') + +for i in myiter(32): + print(i) diff --git a/tests/basics/iter2.py b/tests/basics/iter2.py new file mode 100644 index 0000000000..763f59cb6f --- /dev/null +++ b/tests/basics/iter2.py @@ -0,0 +1,23 @@ +# user defined iterator used in something other than a for loop + +class MyStopIteration(StopIteration): + pass + +class myiter: + def __init__(self, i): + self.i = i + + def __iter__(self): + return self + + def __next__(self): + if self.i == 0: + raise StopIteration + elif self.i == 1: + raise StopIteration(1) + elif self.i == 2: + raise MyStopIteration + +print(list(myiter(0))) +print(list(myiter(1))) +print(list(myiter(2)))