py/runtime: Fix PEP479 behaviour throwing StopIteration into yield from.

Commit 3f6ffe059f implemented PEP479 but did
not catch the case fixed in this commit.  Found by coverage analysis, that
the VM had uncovered code.
pull/5161/head
Damien George 2019-09-30 16:06:20 +10:00
rodzic 82c494a97e
commit 809d89c794
4 zmienionych plików z 21 dodań i 10 usunięć

Wyświetl plik

@ -1319,7 +1319,12 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th
// will be propagated up. This behavior is approved by test_pep380.py
// test_delegation_of_close_to_non_generator(),
// test_delegating_throw_to_non_generator()
*ret_val = mp_make_raise_obj(throw_value);
if (mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
// PEP479: if StopIteration is raised inside a generator it is replaced with RuntimeError
*ret_val = mp_obj_new_exception_msg(&mp_type_RuntimeError, "generator raised StopIteration");
} else {
*ret_val = mp_make_raise_obj(throw_value);
}
return MP_VM_RETURN_EXCEPTION;
}
}

11
py/vm.c
Wyświetl plik

@ -1266,17 +1266,10 @@ yield:
DISPATCH();
} else {
assert(ret_kind == MP_VM_RETURN_EXCEPTION);
assert(!EXC_MATCH(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration)));
// Pop exhausted gen
sp--;
if (EXC_MATCH(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
PUSH(mp_obj_exception_get_value(ret_value));
// If we injected GeneratorExit downstream, then even
// if it was swallowed, we re-raise GeneratorExit
GENERATOR_EXIT_IF_NEEDED(t_exc);
DISPATCH();
} else {
RAISE(ret_value);
}
RAISE(ret_value);
}
}

Wyświetl plik

@ -27,3 +27,14 @@ try:
g.throw(StopIteration)
except RuntimeError:
print('RuntimeError')
# throwing a StopIteration through yield from, will be converted to a RuntimeError
def gen():
yield from range(2)
print('should not get here')
g = gen()
print(next(g))
try:
g.throw(StopIteration)
except RuntimeError:
print('RuntimeError')

Wyświetl plik

@ -3,3 +3,5 @@ RuntimeError
StopIteration
1
RuntimeError
0
RuntimeError