8000 GH-116090: Fix test and clarify behavior for exception events when ex… · python/cpython@2c42e13 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2c42e13

Browse files
authored
GH-116090: Fix test and clarify behavior for exception events when exhausting a generator. (GH-120697)
1 parent 95a7391 commit 2c42e13

File tree

2 files changed

+33
-6
lines changed

2 files changed

+33
-6
lines changed

Doc/library/sys.monitoring.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ To allow tools to monitor for real exceptions without slowing down generators
226226
and coroutines, the :monitoring-event:`STOP_ITERATION` event is provided.
227227
:monitoring-event:`STOP_ITERATION` can be locally disabled, unlike :monitoring-event:`RAISE`.
228228

229+
Note that the :monitoring-event:`STOP_ITERATION` event and the :monitoring-event:`RAISE`
230+
event for a :exc:`StopIteration` exception are equivalent, and are treated as interchangeable
231+
when generating events. Implementations will favor :monitoring-event:`STOP_ITERATION` for
232+
performance reasons, but may generate a :monitoring-event:`RAISE` event with a :exc:`StopIteration`.
229233

230234
Turning events on and off
231235
-------------------------

Lib/test/test_monitoring.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -832,20 +832,43 @@ def func1():
832832

833833
self.check_events(func1, [("raise", KeyError)])
834834

835-
# gh-116090: This test doesn't really require specialization, but running
836-
# it without specialization exposes a monitoring bug.
837-
@requires_specialization
838835
def test_implicit_stop_iteration(self):
836+
"""Generators are documented as raising a StopIteration
837+
when they terminate.
838+
However, we don't do that if we can avoid it, for speed.
839+
sys.monitoring handles that by injecting a STOP_ITERATION
840+
event when we would otherwise have skip the RAISE event.
841+
This test checks that both paths record an equivalent event.
842+
"""
839843

840844
def gen():
841845
yield 1
842846
return 2
843847

844-
def implicit_stop_iteration():
845-
for _ in gen():
848+
def implicit_stop_iteration(iterator=None):
849+
if iterator is None:
850+
iterator = gen()
851+
for _ in iterator:
846852
pass
847853

848-
self.check_events(implicit_stop_iteration, [("raise", StopIteration)], recorders=(StopiterationRecorder,))
854+
recorders=(ExceptionRecorder, StopiterationRecorder,)
855+
expected = [("raise", StopIteration)]
856+
857+
# Make sure that the loop is unspecialized, and that it will not
858+
# re-specialize immediately, so that we can we can test the
859+
# unspecialized version of the loop first.
860+
# Note: this assumes that we don't specialize loops over sets.
861+
implicit_stop_iteration(set(range(100)))
862+
863+
# This will record a RAISE event for the StopIteration.
864+
self.check_events(implicit_stop_iteration, expected, recorders=recorders)
865+
866+
# Now specialize, so that we see a STOP_ITERATION event.
867+
for _ in range(100):
868+
implicit_stop_iteration()
869+
870+
# This will record a STOP_ITERATION event for the StopIteration.
871+
self.check_events(implicit_stop_iteration, expected, recorders=recorders)
849872

850873
initial = [
851874
("raise", ZeroDivisionError),

0 commit comments

Comments
 (0)
0