@@ -36,6 +36,11 @@ class AsyncGenerators:
36
36
# regular set so we don't have to deal with GC firing at
37
37
# unexpected times.
38
38
alive : _WEAK_ASYNC_GEN_SET | _ASYNC_GEN_SET = attrs .Factory (_WEAK_ASYNC_GEN_SET )
39
+ # The ids of foreign async generators are added to this set when first
40
+ # iterated. Usually it is not safe to refer to ids like this, but because
41
+ # we're using a finalizer we can ensure ids in this set do not outlive
42
+ # their async generator.
43
+ foreign : set [int ] = attrs .Factory (set )
39
44
40
45
# This collects async generators that get garbage collected during
41
46
# the one-tick window between the system nursery closing and the
@@ -52,10 +57,7 @@ def firstiter(agen: AsyncGeneratorType[object, NoReturn]) -> None:
52
57
# An async generator first iterated outside of a Trio
53
58
# task doesn't belong to Trio. Probably we're in guest
54
59
# mode and the async generator belongs to our host.
55
- # The locals dictionary is the only good place to
56
- # remember this fact, at least until
57
- # https://bugs.python.org/issue40916 is implemented.
58
- agen .ag_frame .f_locals ["@trio_foreign_asyncgen" ] = True
60
+ self .foreign .add (id (agen ))
59
61
if self .prev_hooks .firstiter is not None :
60
62
self .prev_hooks .firstiter (agen )
61
63
@@ -80,8 +82,9 @@ def finalize_in_trio_context(
80
82
def finalizer (agen : AsyncGeneratorType [object , NoReturn ]) -> None :
81
83
agen_name = name_asyncgen (agen )
82
84
try :
83
- is_ours = not agen .ag_frame .f_locals .get ("@trio_foreign_asyncgen" )
84
- except AttributeError : # pragma: no cover
85
+ self .foreign .remove (id (agen ))
86
+ is_ours = False
87
+ except KeyError :
85
88
is_ours = True
86
89
87
90
if is_ours :
0 commit comments