py/objgenerator: Protect against reentering a generator.
Generators that are already executing cannot be reexecuted. This patch puts in a check for such a case. Thanks to @jepler for finding the bug.
This commit is contained in:
parent
771cb359af
commit
400273a799
|
@ -117,9 +117,19 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
|
|||
*self->code_state.sp = send_value;
|
||||
}
|
||||
}
|
||||
|
||||
// We set self->globals=NULL while executing, for a sentinel to ensure the generator
|
||||
// cannot be reentered during execution
|
||||
if (self->globals == NULL) {
|
||||
mp_raise_ValueError("generator already executing");
|
||||
}
|
||||
|
||||
// Set up the correct globals context for the generator and execute it
|
||||
self->code_state.old_globals = mp_globals_get();
|
||||
mp_globals_set(self->globals);
|
||||
self->globals = NULL;
|
||||
mp_vm_return_kind_t ret_kind = mp_execute_bytecode(&self->code_state, throw_value);
|
||||
self->globals = mp_globals_get();
|
||||
mp_globals_set(self->code_state.old_globals);
|
||||
|
||||
switch (ret_kind) {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# yielding from an already executing generator is not allowed
|
||||
|
||||
def f():
|
||||
yield 1
|
||||
# g here is already executing so this will raise an exception
|
||||
yield from g
|
||||
|
||||
g = f()
|
||||
|
||||
print(next(g))
|
||||
|
||||
try:
|
||||
next(g)
|
||||
except ValueError:
|
||||
print('ValueError')
|
|
@ -336,7 +336,7 @@ def run_tests(pyb, tests, args, base_path="."):
|
|||
# Some tests are known to fail with native emitter
|
||||
# Remove them from the below when they work
|
||||
if args.emit == 'native':
|
||||
skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_stopped gen_yield_from_throw gen_yield_from_throw2 gen_yield_from_throw3 generator1 generator2 generator_args generator_close generator_closure generator_exc generator_pend_throw generator_return generator_send'.split()}) # require yield
|
||||
skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_executing gen_yield_from_iter gen_yield_from_send gen_yield_from_stopped gen_yield_from_throw gen_yield_from_throw2 gen_yield_from_throw3 generator1 generator2 generator_args generator_close generator_closure generator_exc generator_pend_throw generator_return generator_send'.split()}) # require yield
|
||||
skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join'.split()}) # require yield
|
||||
skip_tests.update({'basics/async_%s.py' % t for t in 'def await await2 for for2 with with2'.split()}) # require yield
|
||||
skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs
|
||||
|
|
Loading…
Reference in New Issue