Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Lib/asyncio/base_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, loop, protocol, args, shell,
self._proc = None
self._pid = None
self._returncode = None
self._exit_waiters = []
self._exit_waiters = set()
self._pending_calls = collections.deque()
self._pipes = {}
self._finished = False
Expand Down Expand Up @@ -211,6 +211,14 @@ async def _connect_pipes(self, waiter):
except (SystemExit, KeyboardInterrupt):
raise
except BaseException as exc:
# Close any pipes that were already connected before the
# error/cancellation to avoid leaking file descriptors.
for proto in self._pipes.values():
if proto is not None:
proto.pipe.close()
for raw_pipe in (proc.stdin, proc.stdout, proc.stderr):
if raw_pipe is not None:
raw_pipe.close()
if waiter is not None and not waiter.cancelled():
waiter.set_exception(exc)
else:
Expand Down Expand Up @@ -260,8 +268,12 @@ async def _wait(self):
return self._returncode

waiter = self._loop.create_future()
self._exit_waiters.append(waiter)
return await waiter
self._exit_waiters.add(waiter)
try:
return await waiter
finally:
if self._exit_waiters is not None:
self._exit_waiters.discard(waiter)

def _try_finish(self):
assert not self._finished
Expand Down
2 changes: 1 addition & 1 deletion Lib/asyncio/unix_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async def _make_subprocess_transport(self, protocol, args, shell,
raise
except BaseException:
transp.close()
await transp._wait()
await tasks.shield(transp._wait())
raise

return transp
Expand Down
2 changes: 1 addition & 1 deletion Lib/asyncio/windows_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ async def _make_subprocess_transport(self, protocol, args, shell,
raise
except BaseException:
transp.close()
await transp._wait()
await tasks.shield(transp._wait())
raise

return transp
Expand Down
5 changes: 1 addition & 4 deletions Lib/test/test_asyncio/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from asyncio import subprocess
from test.test_asyncio import utils as test_utils
from test import support
from test.support import os_helper, warnings_helper, gc_collect
from test.support import os_helper, gc_collect

if not support.has_subprocess_support:
raise unittest.SkipTest("test module requires subprocess")
Expand Down Expand Up @@ -1028,7 +1028,6 @@ async def main():

self.loop.run_until_complete(main())

@warnings_helper.ignore_warnings(category=ResourceWarning)
def test_subprocess_read_pipe_cancelled(self):
async def main():
loop = asyncio.get_running_loop()
Expand All @@ -1039,7 +1038,6 @@ async def main():
asyncio.run(main())
gc_collect()

@warnings_helper.ignore_warnings(category=ResourceWarning)
def test_subprocess_write_pipe_cancelled(self):
async def main():
loop = asyncio.get_running_loop()
Expand All @@ -1050,7 +1048,6 @@ async def main():
asyncio.run(main())
gc_collect()

@warnings_helper.ignore_warnings(category=ResourceWarning)
def test_subprocess_read_write_pipe_cancelled(self):
async def main():
loop = asyncio.get_running_loop()
Expand Down
Loading