diff --git a/py/objgenerator.c b/py/objgenerator.c index c306bb0c5..92bafea6a 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -252,7 +252,22 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); STATIC mp_obj_t gen_instance_close(mp_obj_t self_in); STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) { - mp_obj_t exc = (n_args == 2) ? args[1] : args[2]; + // The signature of this function is: throw(type[, value[, traceback]]) + // CPython will pass all given arguments through the call chain and process them + // at the point they are used (native generators will handle them differently to + // user-defined generators with a throw() method). To save passing multiple + // values, MicroPython instead does partial processing here to reduce it down to + // one argument and passes that through: + // - if only args[1] is given, or args[2] is given but is None, args[1] is + // passed through (in the standard case it is an exception class or instance) + // - if args[2] is given and not None it is passed through (in the standard + // case it would be an exception instance and args[1] its corresponding class) + // - args[3] is always ignored + + mp_obj_t exc = args[1]; + if (n_args > 2 && args[2] != mp_const_none) { + exc = args[2]; + } mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, exc); if (ret == MP_OBJ_STOP_ITERATION) { diff --git a/tests/basics/gen_yield_from_throw.py b/tests/basics/gen_yield_from_throw.py index 703158f4d..804c53dda 100644 --- a/tests/basics/gen_yield_from_throw.py +++ b/tests/basics/gen_yield_from_throw.py @@ -1,8 +1,8 @@ def gen(): try: yield 1 - except ValueError: - print("got ValueError from upstream!") + except ValueError as e: + print("got ValueError from upstream!", repr(e.args)) yield "str1" raise TypeError @@ -16,3 +16,21 @@ try: print(next(g)) except TypeError: print("got TypeError from downstream!") + +# passing None as second argument to throw +g = gen2() +print(next(g)) +print(g.throw(ValueError, None)) +try: + print(next(g)) +except TypeError: + print("got TypeError from downstream!") + +# passing an exception instance as second argument to throw +g = gen2() +print(next(g)) +print(g.throw(ValueError, ValueError(123))) +try: + print(next(g)) +except TypeError: + print("got TypeError from downstream!") diff --git a/tests/basics/generator_throw.py b/tests/basics/generator_throw.py index bf5ff33a2..1b43c125e 100644 --- a/tests/basics/generator_throw.py +++ b/tests/basics/generator_throw.py @@ -28,8 +28,8 @@ except StopIteration: def gen(): try: yield 123 - except GeneratorExit: - print('GeneratorExit') + except GeneratorExit as e: + print('GeneratorExit', repr(e.args)) yield 456 # thrown a class @@ -41,3 +41,13 @@ print(g.throw(GeneratorExit)) g = gen() print(next(g)) print(g.throw(GeneratorExit())) + +# thrown an instance with None as second arg +g = gen() +print(next(g)) +print(g.throw(GeneratorExit(), None)) + +# thrown a class and instance +g = gen() +print(next(g)) +print(g.throw(GeneratorExit, GeneratorExit(123)))