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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy