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
asyncio.create_subprocess_exec() only works with main event loop #79802
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2019-06-30.09:56:09.887>created_at=<Date2018-12-30.22:59:38.171>labels= ['type-bug', '3.8', '3.9', 'expert-asyncio']
title='asyncio.create_subprocess_exec() only works with main event loop'updated_at=<Date2020-05-30.02:49:41.494>user='https://github.com/sth'
asyncio.create_subprocess_exec() accepts a loop parameter, but doesn't use it to watch the child process. Instead uses get_event_loop_policy().get_child_watcher(), which doesn't doesn't know about loop but tries to use the current default event loop.
This fails if there is no current event loop or if that loop isn't running:
This crashes because the main event loop never was created:
Traceback (most recent call last):
File "sample.py", line 8, in <module>
loop.run_until_complete(action(loop))
File "/home/sth/devel/cpython.vanilla/Lib/asyncio/base_events.py", line 589, in run_until_completereturn future.result()
File "sample.py", line 4, in action
proc =await asyncio.create_subprocess_exec('echo', loop=loop)
File "/home/sth/devel/cpython.vanilla/Lib/asyncio/subprocess.py", line 213, in create_subprocess_exec
transport, protocol =await loop.subprocess_exec(
File "/home/sth/devel/cpython.vanilla/Lib/asyncio/base_events.py", line 1542, in subprocess_exec
transport =awaitself._make_subprocess_transport(
File "/home/sth/devel/cpython.vanilla/Lib/asyncio/unix_events.py", line 193, in _make_subprocess_transport
watcher.add_child_handler(transp.get_pid(),
File "/home/sth/devel/cpython.vanilla/Lib/asyncio/unix_events.py", line 924, in add_child_handlerraiseRuntimeError(
RuntimeError: Cannot add child handler, the child watcher does not have a loop attached
If we do have a current event loop, for example by calling asyncio.get_event_loop() before creating out own loop, then we don't get an error, but the program hangs indefinitely since that loop isn't running.
Expected behavior would be that the loop given to create_subprocess_exec() is used to watch the child process.
A fix I found is to add `watcher.attach_loop(self)` just after `with events.get_child_watcher() as watcher:` in `_make_subprocess_transport` in `asyncio/unix_events.py`.
New changeset 13ed079 by Andrew Svetlov in branch 'master': bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (bpo-13630) 13ed079
karthi@ubuntu-s-1vcpu-1gb-blr1-01:~/cpython$ ./python -m unittest test.test_asyncio.test_unix_events
...................Exception in thread Thread-1:
Traceback (most recent call last):
File "/home/karthi/cpython/Lib/threading.py", line 923, in _bootstrap_innerself.run()
File "/home/karthi/cpython/Lib/threading.py", line 865, in runself._target(*self._args, **self._kwargs)
File "/home/karthi/cpython/Lib/test/test_asyncio/test_unix_events.py", line 1833, in fself.assertIsInstance(watcher, asyncio.SafeChildWatcher)
File "/home/karthi/cpython/Lib/unittest/case.py", line 1330, in assertIsInstanceself.fail(self._formatMessage(msg, standardMsg))
File "/home/karthi/cpython/Lib/unittest/case.py", line 748, in failraiseself.failureException(msg)
AssertionError: <asyncio.unix_events.ThreadedChildWatcher object at 0x7f00b357d640> is not an instance of <class 'asyncio.unix_events.SafeChildWatcher'>
../home/karthi/cpython/Lib/asyncio/base_events.py:646: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=False>
_warn(f"unclosed event loop {self!r}", ResourceWarning, source=self)
ResourceWarning: Enable tracemalloc to get the object allocation traceback
...............................................................................................
It seems the assertion has to use ThreadedChildWatcher instead of SafeChildWatcher as the default seems to be changed at [0] . If changing assertion fixes the test I am curious why it didn't fail in the primary CI.
The failure depends on tests order execution (maybe you use -jN flag?).
Before default child watcher was SafeChildWatcher, not it is ThreaderChildWather.
The watcher is a global variable (a property of global event loop policy).
The failed test doesn't make sense for default ThreadChildWatcher but still valuable for old default SafeChildWatcher.
I've changed the test to use SafeChildWatcher explicitly.
New changeset c6789d6 by Miss Islington (bot) (Andrew Svetlov) in branch 'master': bpo-35621: Fix tests when SafeChildWatcher is expected instead of ThreadedChildWatcher (GH-13754) c6789d6
New changeset 9535aff by Miss Islington (bot) (Andrew Svetlov) in branch 'master':
Revert "bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (bpo-13630)" (GH-13793) 9535aff
New changeset 0d671c0 by Andrew Svetlov in branch 'master': bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (GH-14344) 0d671c0
New changeset bf8cb31 by Miss Islington (bot) in branch '3.8': bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (GH-14344) bf8cb31
is there a workaround for earlier Python versions that does not involve patching the standard library?
You could potentially try using the new default watcher, ThreadedChildWatcher, by implementing it locally and setting it as the child watcher to use (instead of SafeChildWatcher) with set_child_watcher(). AFAICT, the current implementation should work well for earlier versions of Python that don't have it, we just can't include it earlier than 3.8 since it's a new feature.
Activity
sth commentedon Dec 30, 2018
asyncio.create_subprocess_exec()accepts aloopparameter, but doesn't use it to watch the child process. Instead usesget_event_loop_policy().get_child_watcher(), which doesn't doesn't know aboutloopbut tries to use the current default event loop.This fails if there is no current event loop or if that loop isn't running:
This crashes because the main event loop never was created:
If we do have a current event loop, for example by calling
asyncio.get_event_loop()before creating out own loop, then we don't get an error, but the program hangs indefinitely since that loop isn't running.Expected behavior would be that the loop given to create_subprocess_exec() is used to watch the child process.
adefossez commentedon Jan 24, 2019
Also impacted.
A fix I found is to add `watcher.attach_loop(self)` just after `with events.get_child_watcher() as watcher:` in `_make_subprocess_transport` in `asyncio/unix_events.py`.
asvetlov commentedon Jun 2, 2019
New changeset 13ed079 by Andrew Svetlov in branch 'master':
bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (bpo-13630)
13ed079
koobs commentedon Jun 2, 2019
New buildbot failure on koobs-freebsd10 that appears related to (includes) this changeset
Full log attached
tirkarthi commentedon Jun 2, 2019
Also seems to occur on Ubuntu : https://buildbot.python.org/all/#/builders/141/builds/1912/steps/5/logs/stdio
Ran 116 tests in 1.391s
OK
tirkarthi commentedon Jun 2, 2019
It seems the assertion has to use ThreadedChildWatcher instead of SafeChildWatcher as the default seems to be changed at [0] . If changing assertion fixes the test I am curious why it didn't fail in the primary CI.
[0] 13ed079#diff-8441007d39090aec5e09dbfcfa9973b7R1299
pablogsal commentedon Jun 2, 2019
I cannot reproduce this issue locally :(
pablogsal commentedon Jun 2, 2019
When I ran the tests, the watcher I get is a SafeChildWatcher
asvetlov commentedon Jun 2, 2019
Thanks for the report!
The failure depends on tests order execution (maybe you use -jN flag?).
Before default child watcher was SafeChildWatcher, not it is ThreaderChildWather.
The watcher is a global variable (a property of global event loop policy).
The failed test doesn't make sense for default ThreadChildWatcher but still valuable for old default SafeChildWatcher.
I've changed the test to use SafeChildWatcher explicitly.
See #13754 for the fix
pablogsal commentedon Jun 2, 2019
Oh, I understand now!
Thanks for the fix, Andrew! :)
miss-islington commentedon Jun 2, 2019
New changeset c6789d6 by Miss Islington (bot) (Andrew Svetlov) in branch 'master':
bpo-35621: Fix tests when SafeChildWatcher is expected instead of ThreadedChildWatcher (GH-13754)
c6789d6
miss-islington commentedon Jun 4, 2019
New changeset 9535aff by Miss Islington (bot) (Andrew Svetlov) in branch 'master':
Revert "bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (bpo-13630)" (GH-13793)
9535aff
asvetlov commentedon Jun 30, 2019
New changeset 0d671c0 by Andrew Svetlov in branch 'master':
bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (GH-14344)
0d671c0
miss-islington commentedon Jun 30, 2019
New changeset bf8cb31 by Miss Islington (bot) in branch '3.8':
bpo-35621: Support running subprocesses in asyncio when loop is executed in non-main thread (GH-14344)
bf8cb31
TimFroehlich commentedon Jul 18, 2019
Is it possible to amend the documentation (https://docs.python.org/3.5/library/asyncio-subprocess.html, etc) to include a note that the loop parameter doesn't work? I'm impacted by this bug and lost a few hours to it.
rojer commentedon May 29, 2020
is there a workaround for earlier Python versions that does not involve patching the standard library?
aeros commentedon May 30, 2020
You could potentially try using the new default watcher,
ThreadedChildWatcher, by implementing it locally and setting it as the child watcher to use (instead ofSafeChildWatcher) withset_child_watcher(). AFAICT, the current implementation should work well for earlier versions of Python that don't have it, we just can't include it earlier than 3.8 since it's a new feature.See
cpython/Lib/asyncio/unix_events.py
Line 1326 in 4649202