mirror of
https://github.com/python/cpython.git
synced 2025-08-23 18:24:46 +00:00
GH-116090: Fix test and clarify behavior for exception events when exhausting a generator. (GH-120697)
This commit is contained in:
parent
95a73917cd
commit
2c42e13e80
2 changed files with 33 additions and 6 deletions
|
@ -226,6 +226,10 @@ To allow tools to monitor for real exceptions without slowing down generators
|
||||||
and coroutines, the :monitoring-event:`STOP_ITERATION` event is provided.
|
and coroutines, the :monitoring-event:`STOP_ITERATION` event is provided.
|
||||||
:monitoring-event:`STOP_ITERATION` can be locally disabled, unlike :monitoring-event:`RAISE`.
|
:monitoring-event:`STOP_ITERATION` can be locally disabled, unlike :monitoring-event:`RAISE`.
|
||||||
|
|
||||||
|
Note that the :monitoring-event:`STOP_ITERATION` event and the :monitoring-event:`RAISE`
|
||||||
|
event for a :exc:`StopIteration` exception are equivalent, and are treated as interchangeable
|
||||||
|
when generating events. Implementations will favor :monitoring-event:`STOP_ITERATION` for
|
||||||
|
performance reasons, but may generate a :monitoring-event:`RAISE` event with a :exc:`StopIteration`.
|
||||||
|
|
||||||
Turning events on and off
|
Turning events on and off
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
|
@ -832,20 +832,43 @@ class ExceptionMonitoringTest(CheckEvents):
|
||||||
|
|
||||||
self.check_events(func1, [("raise", KeyError)])
|
self.check_events(func1, [("raise", KeyError)])
|
||||||
|
|
||||||
# gh-116090: This test doesn't really require specialization, but running
|
|
||||||
# it without specialization exposes a monitoring bug.
|
|
||||||
@requires_specialization
|
|
||||||
def test_implicit_stop_iteration(self):
|
def test_implicit_stop_iteration(self):
|
||||||
|
"""Generators are documented as raising a StopIteration
|
||||||
|
when they terminate.
|
||||||
|
However, we don't do that if we can avoid it, for speed.
|
||||||
|
sys.monitoring handles that by injecting a STOP_ITERATION
|
||||||
|
event when we would otherwise have skip the RAISE event.
|
||||||
|
This test checks that both paths record an equivalent event.
|
||||||
|
"""
|
||||||
|
|
||||||
def gen():
|
def gen():
|
||||||
yield 1
|
yield 1
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
def implicit_stop_iteration():
|
def implicit_stop_iteration(iterator=None):
|
||||||
for _ in gen():
|
if iterator is None:
|
||||||
|
iterator = gen()
|
||||||
|
for _ in iterator:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.check_events(implicit_stop_iteration, [("raise", StopIteration)], recorders=(StopiterationRecorder,))
|
recorders=(ExceptionRecorder, StopiterationRecorder,)
|
||||||
|
expected = [("raise", StopIteration)]
|
||||||
|
|
||||||
|
# Make sure that the loop is unspecialized, and that it will not
|
||||||
|
# re-specialize immediately, so that we can we can test the
|
||||||
|
# unspecialized version of the loop first.
|
||||||
|
# Note: this assumes that we don't specialize loops over sets.
|
||||||
|
implicit_stop_iteration(set(range(100)))
|
||||||
|
|
||||||
|
# This will record a RAISE event for the StopIteration.
|
||||||
|
self.check_events(implicit_stop_iteration, expected, recorders=recorders)
|
||||||
|
|
||||||
|
# Now specialize, so that we see a STOP_ITERATION event.
|
||||||
|
for _ in range(100):
|
||||||
|
implicit_stop_iteration()
|
||||||
|
|
||||||
|
# This will record a STOP_ITERATION event for the StopIteration.
|
||||||
|
self.check_events(implicit_stop_iteration, expected, recorders=recorders)
|
||||||
|
|
||||||
initial = [
|
initial = [
|
||||||
("raise", ZeroDivisionError),
|
("raise", ZeroDivisionError),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue