Skip to content

Commit 3cfa52d

Browse files
committed
avoid recursion errors if weakrefs unresolve before close is called
Can occur when close is called via `__del__`, for instance when asyncio.run raises
1 parent 0a5525b commit 3cfa52d

File tree

2 files changed

+23
-2
lines changed

2 files changed

+23
-2
lines changed

asyncio_atexit.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121

2222
class _RegistryEntry:
2323
def __init__(self, loop):
24+
if not hasattr(loop, "_atexit_orig_close"):
25+
# avoid double-patching
26+
# weakrefs can get unresolved and then close called in __del__,
27+
# so this seems unavoidable
28+
loop._atexit_orig_close = loop.close
2429
try:
25-
self._close_ref = weakref.WeakMethod(loop.close)
30+
self._close_ref = weakref.WeakMethod(loop._atexit_orig_close)
2631
except TypeError:
2732
# not everything can be weakref'd (Extensions such as uvloop).
2833
# Hold a regular reference _on the object_, in those cases
29-
loop._atexit_orig_close = loop.close
3034
self._close_ref = lambda: loop._atexit_orig_close
3135
self.callbacks = []
3236

test_asyncio_atexit.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,20 @@ async def test():
7575

7676
asyncio_run(test())
7777
assert not sync_called
78+
79+
80+
def test_run_raises(policy):
81+
sync_called = False
82+
83+
def sync_cb():
84+
nonlocal sync_called
85+
sync_called = True
86+
87+
async def test():
88+
asyncio_atexit.register(sync_cb)
89+
1 / 0
90+
91+
with pytest.raises(ZeroDivisionError):
92+
asyncio_run(test())
93+
94+
assert sync_called

0 commit comments

Comments
 (0)