-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IsolatedAsyncioTestCase
does not call asyncio.set_event_loop
before setUp
anymore, asyncio.Runner
+PidFdChildWatcher
leaves zombie processes
#95736
Comments
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.11IsolatedAsyncioTestCase
does not call asyncio.set_event_loop
before setUp
anymore in 3.11
it looks like |
yep that's exactly what's happening here: import asyncio
import sys
asyncio.set_child_watcher(asyncio.PidfdChildWatcher())
tasks = set()
pids = set()
async def subprocess():
proc = await asyncio.create_subprocess_exec(
sys.executable,
"-c",
"import time; print('hello!'); import time; time.sleep(2); print('goodbye')",
)
pids.add(proc.pid)
print(await proc.communicate())
async def main():
task = asyncio.create_task(subprocess())
tasks.add(task)
await asyncio.sleep(1)
async def main2():
try:
async with asyncio.timeout(3):
await asyncio.gather(*tasks)
except TimeoutError:
import psutil
for pid in pids:
print(psutil.Process(pid))
else:
print("success!")
if __name__ == "__main__":
with asyncio.Runner() as runner:
runner.run(main())
runner.run(main2()) output
|
IsolatedAsyncioTestCase
does not call asyncio.set_event_loop
before setUp
anymore in 3.11IsolatedAsyncioTestCase
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 processesIsolatedAsyncioTestCase
does not call asyncio.set_event_loop
before setUp
anymore, asyncio.Runner
+PidFdChildWatcher
leaves zombie processes
#95584 is vaguely related |
The issue here is that In addition child watchers that use attach_loop are prone to unusual behaviour when the main thread switches loop |
It looks like IsolatedAsyncioTestCase when the Pidfdchildwatcher is used will break a subprocess started in asyncSetUp |
import asyncio
import sys
import unittest
import asyncio
asyncio.set_child_watcher(asyncio.PidfdChildWatcher())
def create_free_port():
return 4 # chosen by a fair dice roll
class TestProc(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self):
self.port = create_free_port()
self.proc = await asyncio.create_subprocess_exec(
sys.executable,
"-c", # more realistically this might be an http server or database
f"import time; print('listening on {self.port}'); import time; time.sleep(2); print('goodbye')",
)
async def testProc(self):
print(f"connecting to {self.port}")
async def asyncTearDown(self):
await self.proc.communicate() # hangs forever on 3.11
if __name__ == "__main__":
unittest.main() on python3.10 this produces:
on python3.11 it hangs forever in |
I think IsolatedAsyncioTestCase can be fixed by backing out the changes to use this would also allow people to use |
Please create a separate issue for the |
I'm not sure I follow. Are you saying that
What does "yield-safe dependent" mean? I'm guessing that ATM |
There's more to yield safety than just being in the same task and propagating exceptions, @njsmith has a draft PEP |
If this was something you wanted me to work on, I'd implement the same testing strategy as AsyncExitStack where a SyncExitStack is built out of AsyncExitStack and run through the same test suite as the real ExitStack see #95902 for a plan on how to fix that same issue with asynccontextmanager and contextmanager |
Okay let's back out of the speculative projects and focus on this PR. ATM I have no idea how the one line Kumar added fixes the test, and it feels too much like "programming by random modification" to me to just approve because it makes the test pass. :-) I will get back to this. |
Speculative projects moved here |
…re calling setup functions (pythonGH-95898) (cherry picked from commit 9d51599) Co-authored-by: Kumar Aditya <[email protected]>
The changeset breaks wasm32-emscripten tests. The issue is
Despite the class level skip, the test runner runs some setup routine. The recent change caused the async test runner to set up an event loop. This does not work on wasm32-emscripten. There is no socketpair() implementation and you cannot create a listening socket. Here is a traceback of a wasm32-emscripten run with an extra ``return NULL;
|
I propose to roll back the PR (#95898) since I have no idea how to deal with this. |
FWIW the problem is that I suppose a fix would be to test for the skip flags (see |
It should be created before calling the setUp() method, but after checking for skipping a test.
While the demo no longer fails, we need to keep this open until #96033 lands. Given all the complexity this issue has already experienced I don't think we can expect to backport this to 3.11. I guess we'll have to recommend that affected user tests in 3.11 can call |
The fix I'm thinking of for aiohttp is to move to asyncio.get_running_loop() in the asyncSetUp |
It looks like you're also moving from |
Yes, I chose to do this because it's in an asyncio async function so it's guaranteed the loop is available. Here aiohttp is effectively passing on the bug to it's downstream users. Therefore maybe what aiohttp should do is:
I think it was also discovered that this whole aiohttp TestCase class was possibly supposed to have been deprecated years ago so maybe aiohttp should just do that |
It should be created before calling the setUp() method, but after checking for skipping a test. Automerge-Triggered-By: GH:tiran
…ythonGH-96033) It should be created before calling the setUp() method, but after checking for skipping a test. Automerge-Triggered-By: GH:tiran (cherry picked from commit 3651710) Co-authored-by: Serhiy Storchaka <[email protected]>
…er before calling setup functions (pythonGH-95898) (cherry picked from commit 9d51599) Co-authored-by: Kumar Aditya <[email protected]>
…er before calling setup functions (pythonGH-95898) (cherry picked from commit 9d51599) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Serhiy Storchaka [email protected]
…er before calling setup functions (pythonGH-95898) (cherry picked from commit 9d51599) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Serhiy Storchaka [email protected]
…er before calling setup functions (pythonGH-95898) (cherry picked from commit 9d51599) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
…er before calling setup functions (pythonGH-95898) (cherry picked from commit 9d51599) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
@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 |
Whoops, reopening since the 3.11 backport didn’t get merged yet. |
#96042) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
demo reproducer:
see also #93896
it looks like this is because
asyncio.Runner
callsasyncio.set_event_loop(
inRunner.run
The text was updated successfully, but these errors were encountered: