diff --git a/docs/api-reference/function-index.rst b/docs/api-reference/function-index.rst index 9c4014bb..d267f1e3 100644 --- a/docs/api-reference/function-index.rst +++ b/docs/api-reference/function-index.rst @@ -48,6 +48,8 @@ HPy Core API Function Index * :c:func:`HPyGlobal_Load` * :c:func:`HPyGlobal_Store` * :c:func:`HPyImport_ImportModule` +* :c:func:`HPyIter_Check` +* :c:func:`HPyIter_Next` * :c:func:`HPyListBuilder_Build` * :c:func:`HPyListBuilder_Cancel` * :c:func:`HPyListBuilder_New` @@ -131,6 +133,7 @@ HPy Core API Function Index * :c:func:`HPy_GetItem` * :c:func:`HPy_GetItem_i` * :c:func:`HPy_GetItem_s` +* :c:func:`HPy_GetIter` * :c:func:`HPy_GetSlice` * :c:func:`HPy_HasAttr` * :c:func:`HPy_HasAttr_s` diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index 70d3e8f6..6f076bba 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -190,6 +190,8 @@ with the code for the :term:`CPython ABI` mode, so it is guaranteed to be correc `PyFloat_AsDouble `_ :c:func:`HPyFloat_AsDouble` `PyFloat_FromDouble `_ :c:func:`HPyFloat_FromDouble` `PyImport_ImportModule `_ :c:func:`HPyImport_ImportModule` + `PyIter_Check `_ :c:func:`HPyIter_Check` + `PyIter_Next `_ :c:func:`HPyIter_Next` `PyList_Append `_ :c:func:`HPyList_Append` `PyList_Check `_ :c:func:`HPyList_Check` `PyList_Insert `_ :c:func:`HPyList_Insert` @@ -252,6 +254,7 @@ with the code for the :term:`CPython ABI` mode, so it is guaranteed to be correc `PyObject_GetAttr `_ :c:func:`HPy_GetAttr` `PyObject_GetAttrString `_ :c:func:`HPy_GetAttr_s` `PyObject_GetItem `_ :c:func:`HPy_GetItem` + `PyObject_GetIter `_ :c:func:`HPy_GetIter` `PyObject_HasAttr `_ :c:func:`HPy_HasAttr` `PyObject_HasAttrString `_ :c:func:`HPy_HasAttr_s` `PyObject_Hash `_ :c:func:`HPy_Hash` diff --git a/hpy/debug/src/autogen_debug_ctx_init.h b/hpy/debug/src/autogen_debug_ctx_init.h index 598e7a3f..609a5d19 100644 --- a/hpy/debug/src/autogen_debug_ctx_init.h +++ b/hpy/debug/src/autogen_debug_ctx_init.h @@ -71,6 +71,9 @@ int debug_ctx_Callable_Check(HPyContext *dctx, DHPy h); DHPy debug_ctx_CallTupleDict(HPyContext *dctx, DHPy callable, DHPy args, DHPy kw); DHPy debug_ctx_Call(HPyContext *dctx, DHPy callable, const DHPy *args, size_t nargs, DHPy kwnames); DHPy debug_ctx_CallMethod(HPyContext *dctx, DHPy name, const DHPy *args, size_t nargs, DHPy kwnames); +DHPy debug_ctx_GetIter(HPyContext *dctx, DHPy obj); +DHPy debug_ctx_Iter_Next(HPyContext *dctx, DHPy obj); +int debug_ctx_Iter_Check(HPyContext *dctx, DHPy obj); void debug_ctx_FatalError(HPyContext *dctx, const char *message); void debug_ctx_Err_SetString(HPyContext *dctx, DHPy h_type, const char *utf8_message); void debug_ctx_Err_SetObject(HPyContext *dctx, DHPy h_type, DHPy h_value); @@ -343,6 +346,9 @@ static inline void debug_ctx_init_fields(HPyContext *dctx, HPyContext *uctx) dctx->ctx_CallTupleDict = &debug_ctx_CallTupleDict; dctx->ctx_Call = &debug_ctx_Call; dctx->ctx_CallMethod = &debug_ctx_CallMethod; + dctx->ctx_GetIter = &debug_ctx_GetIter; + dctx->ctx_Iter_Next = &debug_ctx_Iter_Next; + dctx->ctx_Iter_Check = &debug_ctx_Iter_Check; dctx->ctx_FatalError = &debug_ctx_FatalError; dctx->ctx_Err_SetString = &debug_ctx_Err_SetString; dctx->ctx_Err_SetObject = &debug_ctx_Err_SetObject; diff --git a/hpy/debug/src/autogen_debug_wrappers.c b/hpy/debug/src/autogen_debug_wrappers.c index 87b73384..c0fda457 100644 --- a/hpy/debug/src/autogen_debug_wrappers.c +++ b/hpy/debug/src/autogen_debug_wrappers.c @@ -731,6 +731,42 @@ DHPy debug_ctx_CallTupleDict(HPyContext *dctx, DHPy callable, DHPy args, DHPy kw return DHPy_open(dctx, universal_result); } +DHPy debug_ctx_GetIter(HPyContext *dctx, DHPy obj) +{ + if (!get_ctx_info(dctx)->is_valid) { + report_invalid_debug_context(); + } + HPy dh_obj = DHPy_unwrap(dctx, obj); + get_ctx_info(dctx)->is_valid = false; + HPy universal_result = HPy_GetIter(get_info(dctx)->uctx, dh_obj); + get_ctx_info(dctx)->is_valid = true; + return DHPy_open(dctx, universal_result); +} + +DHPy debug_ctx_Iter_Next(HPyContext *dctx, DHPy obj) +{ + if (!get_ctx_info(dctx)->is_valid) { + report_invalid_debug_context(); + } + HPy dh_obj = DHPy_unwrap(dctx, obj); + get_ctx_info(dctx)->is_valid = false; + HPy universal_result = HPyIter_Next(get_info(dctx)->uctx, dh_obj); + get_ctx_info(dctx)->is_valid = true; + return DHPy_open(dctx, universal_result); +} + +int debug_ctx_Iter_Check(HPyContext *dctx, DHPy obj) +{ + if (!get_ctx_info(dctx)->is_valid) { + report_invalid_debug_context(); + } + HPy dh_obj = DHPy_unwrap(dctx, obj); + get_ctx_info(dctx)->is_valid = false; + int universal_result = HPyIter_Check(get_info(dctx)->uctx, dh_obj); + get_ctx_info(dctx)->is_valid = true; + return universal_result; +} + void debug_ctx_FatalError(HPyContext *dctx, const char *message) { if (!get_ctx_info(dctx)->is_valid) { diff --git a/hpy/devel/include/hpy/cpython/autogen_api_impl.h b/hpy/devel/include/hpy/cpython/autogen_api_impl.h index 77c837c9..53cc8e43 100644 --- a/hpy/devel/include/hpy/cpython/autogen_api_impl.h +++ b/hpy/devel/include/hpy/cpython/autogen_api_impl.h @@ -240,6 +240,21 @@ HPyAPI_FUNC int HPyCallable_Check(HPyContext *ctx, HPy h) return PyCallable_Check(_h2py(h)); } +HPyAPI_FUNC HPy HPy_GetIter(HPyContext *ctx, HPy obj) +{ + return _py2h(PyObject_GetIter(_h2py(obj))); +} + +HPyAPI_FUNC HPy HPyIter_Next(HPyContext *ctx, HPy obj) +{ + return _py2h(PyIter_Next(_h2py(obj))); +} + +HPyAPI_FUNC int HPyIter_Check(HPyContext *ctx, HPy obj) +{ + return PyIter_Check(_h2py(obj)); +} + HPyAPI_FUNC HPy HPyErr_SetString(HPyContext *ctx, HPy h_type, const char *utf8_message) { PyErr_SetString(_h2py(h_type), utf8_message); diff --git a/hpy/devel/include/hpy/universal/autogen_ctx.h b/hpy/devel/include/hpy/universal/autogen_ctx.h index 60ae238d..ecf357d8 100644 --- a/hpy/devel/include/hpy/universal/autogen_ctx.h +++ b/hpy/devel/include/hpy/universal/autogen_ctx.h @@ -283,4 +283,7 @@ struct _HPyContext_s { HPy (*ctx_GetSlice)(HPyContext *ctx, HPy obj, HPy_ssize_t start, HPy_ssize_t end); int (*ctx_SetSlice)(HPyContext *ctx, HPy obj, HPy_ssize_t start, HPy_ssize_t end, HPy value); int (*ctx_DelSlice)(HPyContext *ctx, HPy obj, HPy_ssize_t start, HPy_ssize_t end); + HPy (*ctx_GetIter)(HPyContext *ctx, HPy obj); + HPy (*ctx_Iter_Next)(HPyContext *ctx, HPy obj); + int (*ctx_Iter_Check)(HPyContext *ctx, HPy obj); }; diff --git a/hpy/devel/include/hpy/universal/autogen_trampolines.h b/hpy/devel/include/hpy/universal/autogen_trampolines.h index 4d10e132..c4dc36ca 100644 --- a/hpy/devel/include/hpy/universal/autogen_trampolines.h +++ b/hpy/devel/include/hpy/universal/autogen_trampolines.h @@ -254,6 +254,18 @@ HPyAPI_FUNC HPy HPy_CallMethod(HPyContext *ctx, HPy name, const HPy *args, size_ return ctx->ctx_CallMethod ( ctx, name, args, nargs, kwnames ); } +HPyAPI_FUNC HPy HPy_GetIter(HPyContext *ctx, HPy obj) { + return ctx->ctx_GetIter ( ctx, obj ); +} + +HPyAPI_FUNC HPy HPyIter_Next(HPyContext *ctx, HPy obj) { + return ctx->ctx_Iter_Next ( ctx, obj ); +} + +HPyAPI_FUNC int HPyIter_Check(HPyContext *ctx, HPy obj) { + return ctx->ctx_Iter_Check ( ctx, obj ); +} + HPyAPI_FUNC HPy HPyErr_SetString(HPyContext *ctx, HPy h_type, const char *utf8_message) { ctx->ctx_Err_SetString ( ctx, h_type, utf8_message ); return HPy_NULL; } diff --git a/hpy/tools/autogen/conf.py b/hpy/tools/autogen/conf.py index 80ee2bc8..5ebf3457 100644 --- a/hpy/tools/autogen/conf.py +++ b/hpy/tools/autogen/conf.py @@ -29,6 +29,7 @@ 'HPy_HasAttr_s': 'PyObject_HasAttrString', 'HPy_SetAttr': 'PyObject_SetAttr', 'HPy_SetAttr_s': 'PyObject_SetAttrString', + 'HPy_GetIter': 'PyObject_GetIter', 'HPy_GetItem': 'PyObject_GetItem', 'HPy_GetItem_i': None, 'HPy_GetItem_s': None, @@ -107,6 +108,8 @@ 'HPy_RichCompare': 'PyObject_RichCompare', 'HPy_RichCompareBool': 'PyObject_RichCompareBool', 'HPy_Hash': 'PyObject_Hash', + 'HPyIter_Next': 'PyIter_Next', + 'HPyIter_Check': 'PyIter_Check', 'HPyListBuilder_New': None, 'HPyListBuilder_Set': None, 'HPyListBuilder_Build': None, diff --git a/hpy/tools/autogen/public_api.h b/hpy/tools/autogen/public_api.h index 78286fcf..ae2c18a6 100644 --- a/hpy/tools/autogen/public_api.h +++ b/hpy/tools/autogen/public_api.h @@ -307,6 +307,55 @@ HPy HPy_Call(HPyContext *ctx, HPy callable, const HPy *args, size_t nargs, HPy k HPy_ID(262) HPy HPy_CallMethod(HPyContext *ctx, HPy name, const HPy *args, size_t nargs, HPy kwnames); +/** + * Return a new iterator for iterable object ``obj``. This is the equivalent + * of the Python expression ``iter(obj)``. + * + * :param ctx: + * The execution context. + * :param obj: + * An iterable Python object (must not be ``HPy_NULL``). If the object is + * not iterable, a ``TypeError`` will be raised. + * + * :returns: + * The new iterator, ``obj`` itself if it is already an iterator, or + * ``HPy_NULL`` on failure. + */ +HPy_ID(269) +HPy HPy_GetIter(HPyContext *ctx, HPy obj); + +/** + * Return the next value from iterator ``obj``. + * + * :param ctx: + * The execution context. + * :param obj: + * An iterator Python object (must not be ``HPy_NULL``). This can be + * verified with ``HPy_IterCheck``. Otherwise, the behavior is undefined. + * + * :returns: + * The new value in iterator ``obj``, or ``HPy_NULL`` on failure. If the + * iterator was exhausted normally, an exception will not be set. In + * case of some other error, one will be set. + */ +HPy_ID(270) +HPy HPyIter_Next(HPyContext *ctx, HPy obj); + +/** + * Tests if an object is an instance of a Python iterator. + * + * :param ctx: + * The execution context. + * :param obj: + * A handle to an arbitrary object (must not be ``HPy_NULL``). + * + * :returns: + * Non-zero if object ``obj`` provides the ``Iterator`` protocol, and ``0`` + * otherwise. + */ +HPy_ID(271) +int HPyIter_Check(HPyContext *ctx, HPy obj); + /* pyerrors.h */ HPy_ID(136) void HPy_FatalError(HPyContext *ctx, const char *message); diff --git a/hpy/trace/src/autogen_trace_ctx_init.h b/hpy/trace/src/autogen_trace_ctx_init.h index c6453373..9d5eae4b 100644 --- a/hpy/trace/src/autogen_trace_ctx_init.h +++ b/hpy/trace/src/autogen_trace_ctx_init.h @@ -71,6 +71,9 @@ int trace_ctx_Callable_Check(HPyContext *tctx, HPy h); HPy trace_ctx_CallTupleDict(HPyContext *tctx, HPy callable, HPy args, HPy kw); HPy trace_ctx_Call(HPyContext *tctx, HPy callable, const HPy *args, size_t nargs, HPy kwnames); HPy trace_ctx_CallMethod(HPyContext *tctx, HPy name, const HPy *args, size_t nargs, HPy kwnames); +HPy trace_ctx_GetIter(HPyContext *tctx, HPy obj); +HPy trace_ctx_Iter_Next(HPyContext *tctx, HPy obj); +int trace_ctx_Iter_Check(HPyContext *tctx, HPy obj); void trace_ctx_Err_SetString(HPyContext *tctx, HPy h_type, const char *utf8_message); void trace_ctx_Err_SetObject(HPyContext *tctx, HPy h_type, HPy h_value); HPy trace_ctx_Err_SetFromErrnoWithFilename(HPyContext *tctx, HPy h_type, const char *filename_fsencoded); @@ -198,8 +201,8 @@ static inline void trace_ctx_init_info(HPyTraceInfo *info, HPyContext *uctx) { info->magic_number = HPY_TRACE_MAGIC; info->uctx = uctx; - info->call_counts = (uint64_t *)calloc(269, sizeof(uint64_t)); - info->durations = (_HPyTime_t *)calloc(269, sizeof(_HPyTime_t)); + info->call_counts = (uint64_t *)calloc(272, sizeof(uint64_t)); + info->durations = (_HPyTime_t *)calloc(272, sizeof(_HPyTime_t)); info->on_enter_func = HPy_NULL; info->on_exit_func = HPy_NULL; } @@ -360,6 +363,9 @@ static inline void trace_ctx_init_fields(HPyContext *tctx, HPyContext *uctx) tctx->ctx_CallTupleDict = &trace_ctx_CallTupleDict; tctx->ctx_Call = &trace_ctx_Call; tctx->ctx_CallMethod = &trace_ctx_CallMethod; + tctx->ctx_GetIter = &trace_ctx_GetIter; + tctx->ctx_Iter_Next = &trace_ctx_Iter_Next; + tctx->ctx_Iter_Check = &trace_ctx_Iter_Check; tctx->ctx_FatalError = uctx->ctx_FatalError; tctx->ctx_Err_SetString = &trace_ctx_Err_SetString; tctx->ctx_Err_SetObject = &trace_ctx_Err_SetObject; diff --git a/hpy/trace/src/autogen_trace_func_table.c b/hpy/trace/src/autogen_trace_func_table.c index 4a239e03..1f45abcd 100644 --- a/hpy/trace/src/autogen_trace_func_table.c +++ b/hpy/trace/src/autogen_trace_func_table.c @@ -12,7 +12,7 @@ #include "trace_internal.h" -#define TRACE_NFUNC 185 +#define TRACE_NFUNC 188 #define NO_FUNC "" static const char *trace_func_table[] = { @@ -285,6 +285,9 @@ static const char *trace_func_table[] = { "ctx_GetSlice", "ctx_SetSlice", "ctx_DelSlice", + "ctx_GetIter", + "ctx_Iter_Next", + "ctx_Iter_Check", NULL /* sentinel */ }; @@ -295,7 +298,7 @@ int hpy_trace_get_nfunc(void) const char * hpy_trace_get_func_name(int idx) { - if (idx >= 0 && idx < 269) + if (idx >= 0 && idx < 272) return trace_func_table[idx]; return NULL; } diff --git a/hpy/trace/src/autogen_trace_wrappers.c b/hpy/trace/src/autogen_trace_wrappers.c index 6f53bcdb..da2bfe95 100644 --- a/hpy/trace/src/autogen_trace_wrappers.c +++ b/hpy/trace/src/autogen_trace_wrappers.c @@ -804,6 +804,45 @@ HPy trace_ctx_CallMethod(HPyContext *tctx, HPy name, const HPy *args, size_t nar return res; } +HPy trace_ctx_GetIter(HPyContext *tctx, HPy obj) +{ + HPyTraceInfo *info = hpy_trace_on_enter(tctx, 269); + HPyContext *uctx = info->uctx; + _HPyTime_t _ts_start, _ts_end; + _HPyClockStatus_t r0, r1; + r0 = get_monotonic_clock(&_ts_start); + HPy res = HPy_GetIter(uctx, obj); + r1 = get_monotonic_clock(&_ts_end); + hpy_trace_on_exit(info, 269, r0, r1, &_ts_start, &_ts_end); + return res; +} + +HPy trace_ctx_Iter_Next(HPyContext *tctx, HPy obj) +{ + HPyTraceInfo *info = hpy_trace_on_enter(tctx, 270); + HPyContext *uctx = info->uctx; + _HPyTime_t _ts_start, _ts_end; + _HPyClockStatus_t r0, r1; + r0 = get_monotonic_clock(&_ts_start); + HPy res = HPyIter_Next(uctx, obj); + r1 = get_monotonic_clock(&_ts_end); + hpy_trace_on_exit(info, 270, r0, r1, &_ts_start, &_ts_end); + return res; +} + +int trace_ctx_Iter_Check(HPyContext *tctx, HPy obj) +{ + HPyTraceInfo *info = hpy_trace_on_enter(tctx, 271); + HPyContext *uctx = info->uctx; + _HPyTime_t _ts_start, _ts_end; + _HPyClockStatus_t r0, r1; + r0 = get_monotonic_clock(&_ts_start); + int res = HPyIter_Check(uctx, obj); + r1 = get_monotonic_clock(&_ts_end); + hpy_trace_on_exit(info, 271, r0, r1, &_ts_start, &_ts_end); + return res; +} + void trace_ctx_Err_SetString(HPyContext *tctx, HPy h_type, const char *utf8_message) { HPyTraceInfo *info = hpy_trace_on_enter(tctx, 137); diff --git a/hpy/universal/src/autogen_ctx_def.h b/hpy/universal/src/autogen_ctx_def.h index e49c62e7..ec33d46f 100644 --- a/hpy/universal/src/autogen_ctx_def.h +++ b/hpy/universal/src/autogen_ctx_def.h @@ -76,6 +76,9 @@ struct _HPyContext_s g_universal_ctx = { .ctx_CallTupleDict = &ctx_CallTupleDict, .ctx_Call = &ctx_Call, .ctx_CallMethod = &ctx_CallMethod, + .ctx_GetIter = &ctx_GetIter, + .ctx_Iter_Next = &ctx_Iter_Next, + .ctx_Iter_Check = &ctx_Iter_Check, .ctx_FatalError = &ctx_FatalError, .ctx_Err_SetString = &ctx_Err_SetString, .ctx_Err_SetObject = &ctx_Err_SetObject, diff --git a/hpy/universal/src/autogen_ctx_impl.h b/hpy/universal/src/autogen_ctx_impl.h index 68ffafbe..540888e4 100644 --- a/hpy/universal/src/autogen_ctx_impl.h +++ b/hpy/universal/src/autogen_ctx_impl.h @@ -240,6 +240,21 @@ HPyAPI_IMPL int ctx_Callable_Check(HPyContext *ctx, HPy h) return PyCallable_Check(_h2py(h)); } +HPyAPI_IMPL HPy ctx_GetIter(HPyContext *ctx, HPy obj) +{ + return _py2h(PyObject_GetIter(_h2py(obj))); +} + +HPyAPI_IMPL HPy ctx_Iter_Next(HPyContext *ctx, HPy obj) +{ + return _py2h(PyIter_Next(_h2py(obj))); +} + +HPyAPI_IMPL int ctx_Iter_Check(HPyContext *ctx, HPy obj) +{ + return PyIter_Check(_h2py(obj)); +} + HPyAPI_IMPL void ctx_Err_SetString(HPyContext *ctx, HPy h_type, const char *utf8_message) { PyErr_SetString(_h2py(h_type), utf8_message); diff --git a/test/test_hpyiter.py b/test/test_hpyiter.py new file mode 100644 index 00000000..58387253 --- /dev/null +++ b/test/test_hpyiter.py @@ -0,0 +1,90 @@ +from .support import HPyTest + +class TestIter(HPyTest): + + def test_Check(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", HPyFunc_O) + static HPy f_impl(HPyContext *ctx, HPy self, HPy arg) + { + if (HPyIter_Check(ctx, arg)) + return HPy_Dup(ctx, ctx->h_True); + return HPy_Dup(ctx, ctx->h_False); + } + @EXPORT(f) + @INIT + """) + + class CustomIterable: + def __init__(self): + self._iter = iter([1, 2, 3]) + + def __iter__(self): + return self._iter + + class CustomIterator: + def __init__(self): + self._iter = iter([1, 2, 3]) + + def __iter__(self): + return self._iter + + def __next__(self): + return next(self._iter) + + assert mod.f(object()) is False + assert mod.f(10) is False + + assert mod.f((1, 2)) is False + assert mod.f(iter((1, 2))) is True + + assert mod.f([]) is False + assert mod.f(iter([])) is True + + assert mod.f('hello') is False + assert mod.f(iter('hello')) is True + + assert mod.f(map(int, ("1", "2"))) is True + assert mod.f(range(1, 10)) is False + + assert mod.f(CustomIterable()) is False + assert mod.f(iter(CustomIterable())) is True + assert mod.f(CustomIterator()) is True + + def test_Next(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", HPyFunc_O) + static HPy f_impl(HPyContext *ctx, HPy self, HPy arg) + { + HPy result = HPyIter_Next(ctx, arg); + int is_null = HPy_IsNull(result); + + if (is_null && HPyErr_Occurred(ctx)) + return HPy_NULL; + if (is_null) + return HPyErr_SetObject(ctx, ctx->h_StopIteration, ctx->h_None); + return result; + } + @EXPORT(f) + @INIT + """) + + class CustomIterator: + def __init__(self): + self._iter = iter(["a", "b", "c"]) + + def __iter__(self): + return self._iter + + def __next__(self): + return next(self._iter) + + assert mod.f(iter([3, 2, 1])) == 3 + assert mod.f((i for i in range(1, 10))) == 1 + assert mod.f(CustomIterator()) == "a" + + import pytest + with pytest.raises(StopIteration): + assert mod.f(iter([])) + + diff --git a/test/test_object.py b/test/test_object.py index 10ef4f84..8e504504 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -905,6 +905,64 @@ class Dummy: import pytest with pytest.raises(TypeError): mod.f(Dummy(), 42) + + def test_getiter(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", HPyFunc_O) + static HPy f_impl(HPyContext *ctx, HPy self, HPy arg) + { + HPy iterator; + iterator = HPy_GetIter(ctx, arg); + if HPy_IsNull(iterator) + return HPy_NULL; + return iterator; + } + @EXPORT(f) + @INIT + """) + + def test_for_loop(iterator): + results = [] + for obj in iterator: + results.append(obj) + return results + + class WithIter: + def __iter__(self): + return (1, 2, 3).__iter__() + + class WithoutIter: + pass + + case = [1, 2, 3] + result = mod.f(case) + assert result + assert test_for_loop(result) == [1, 2, 3] + + case = iter([1, 2, 3]) + result = mod.f(case) + assert result + assert test_for_loop(result) == [1, 2, 3] + + case = zip((1, 2, 3), [4, 5, 6]) + result = mod.f(case) + assert result + assert test_for_loop(result) == [(1, 4), (2, 5), (3, 6)] + + case = range(10) + result = mod.f(case) + assert result + assert test_for_loop(result) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + case = WithIter() + result = mod.f(case) + assert result + assert test_for_loop(result) == [1, 2, 3] + + import pytest + with pytest.raises(TypeError): + assert mod.f(WithoutIter()) + def test_dump(self): # _HPy_Dump is supposed to be used e.g. inside a gdb session: it