You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
IsolatedAsyncioTestCase does not call asyncio.set_event_loop before setUp anymore, asyncio.Runner+PidFdChildWatcher leaves zombie processes #95736
graingert@conscientious testing310 ~/projects python3.10 foo.py
hello!
.hello!
.
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
graingert@conscientious testing310 ~/projects python3.11 foo.py
hello!
.E
======================================================================
ERROR: test_demo (__main__.DemoTestCase2.test_demo)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.11/unittest/async_case.py", line 82, in _callSetUp
self._asyncioTestContext.run(self.setUp)
File "/home/graingert/projects/foo.py", line 14, in setUp
self.loop = asyncio.get_event_loop_policy().get_event_loop()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/events.py", line 677, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'MainThread'.
----------------------------------------------------------------------
Ran 2 tests in 0.002s
FAILED (errors=1)
it looks like this is because asyncio.Runner calls asyncio.set_event_loop( in Runner.run
changed the title [-]IsolatedAsyncioTestCase does not call asyncio.set_event_loop in setUp anymore in 3.11[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` in before `setUp` anymore in 3.11[/+]on Aug 6, 2022
changed the title [-]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` in before `setUp` anymore in 3.11[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore in 3.11[/+]on Aug 6, 2022
it looks like asyncio.set_event_loop is also being called an additional time per call to asyncio.Runner.run which will call get_child_watcher().attach_loop(loop) every call which causes the PidfdChildWatcher to blow away its callbacks
changed the title [-]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore in 3.11[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore, asyncio.Runner+PidFdChildWatcher leaves zombie processes[/+]on Aug 6, 2022
changed the title [-]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore, asyncio.Runner+PidFdChildWatcher leaves zombie processes[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore, `asyncio.Runner`+`PidFdChildWatcher` leaves zombie processes[/+]on Aug 6, 2022
importasyncioimportsysimportunittestimportasyncioasyncio.set_child_watcher(asyncio.PidfdChildWatcher())
defcreate_free_port():
return4# chosen by a fair dice rollclassTestProc(unittest.IsolatedAsyncioTestCase):
asyncdefasyncSetUp(self):
self.port=create_free_port()
self.proc=awaitasyncio.create_subprocess_exec(
sys.executable,
"-c", # more realistically this might be an http server or databasef"import time; print('listening on {self.port}'); import time; time.sleep(2); print('goodbye')",
)
asyncdeftestProc(self):
print(f"connecting to {self.port}")
asyncdefasyncTearDown(self):
awaitself.proc.communicate() # hangs forever on 3.11if__name__=="__main__":
unittest.main()
on python3.10 this produces:
connecting to 4
listening on 4
goodbye
.
----------------------------------------------------------------------
Ran 1 test in 2.022s
OK
on python3.11 it hangs forever in await self.proc.communicate()
@graingert I hope you will make aiohttp compatible with 3.11 regardless of whether this fix gets merged into 3.11. As a base dependency of many other packages it would block 3.11 support for those other packages.
@gvanrossum your question has, as always, helped me crystallize the trade off I chose by accident, and some other options that are available to aiohttp and I'm going to try to enumerate them so that aiohttp can make the right choice here
Activity
[-]IsolatedAsyncioTestCase does not call asyncio.set_event_loop in setUp anymore in 3.11[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` in before `setUp` anymore in 3.11[/+][-]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` in before `setUp` anymore in 3.11[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore in 3.11[/+]graingert commentedon Aug 6, 2022
it looks like
asyncio.set_event_loopis also being called an additional time per call toasyncio.Runner.runwhich will callget_child_watcher().attach_loop(loop)every call which causes thePidfdChildWatcherto blow away its callbacksgraingert commentedon Aug 6, 2022
yep that's exactly what's happening here:
output
[-]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore in 3.11[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore, asyncio.Runner+PidFdChildWatcher leaves zombie processes[/+][-]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore, asyncio.Runner+PidFdChildWatcher leaves zombie processes[/-][+]`IsolatedAsyncioTestCase` does not call `asyncio.set_event_loop` before `setUp` anymore, `asyncio.Runner`+`PidFdChildWatcher` leaves zombie processes[/+]graingert commentedon Aug 6, 2022
#95584 is vaguely related
graingert commentedon Aug 8, 2022
The issue here is that
asyncio.Runnercallsset_event_looptoo late and too oftenIn addition child watchers that use attach_loop are prone to unusual behaviour when the main thread switches loop
graingert commentedon Aug 10, 2022
It looks like IsolatedAsyncioTestCase when the Pidfdchildwatcher is used will break a subprocess started in asyncSetUp
graingert commentedon Aug 11, 2022
on python3.10 this produces:
on python3.11 it hangs forever in
await self.proc.communicate()18 remaining items
pythongh-95736: Fix event loop creation in IsolatedAsyncioTestCase (p…
[3.11] pythonGH-95736: fix IsolatedAsyncioTestCase to initialize Runn…
[3.11] pythonGH-95736: fix IsolatedAsyncioTestCase to initialize Runn…
[3.11] pythonGH-95736: fix IsolatedAsyncioTestCase to initialize Runn…
[3.11] pythonGH-95736: fix IsolatedAsyncioTestCase to initialize Runn…
[3.11] pythonGH-95736: fix IsolatedAsyncioTestCase to initialize Runn…
gvanrossum commentedon Aug 17, 2022
@graingert I hope you will make aiohttp compatible with 3.11 regardless of whether this fix gets merged into 3.11. As a base dependency of many other packages it would block 3.11 support for those other packages.
graingert commentedon Aug 17, 2022
@gvanrossum your question has, as always, helped me crystallize the trade off I chose by accident, and some other options that are available to aiohttp and I'm going to try to enumerate them so that aiohttp can make the right choice here
gvanrossum commentedon Aug 18, 2022
Whoops, reopening since the 3.11 backport didn’t get merged yet.
[3.11] GH-95736: fix IsolatedAsyncioTestCase to initialize Runner bef… (