Skip to content

Commit 9932fd9

Browse files
niklasfmiss-islington
authored andcommitted
bpo-35721: Close socket pair if Popen in _UnixSubprocessTransport fails (GH-11553)
This slightly expands an existing test case `test_popen_error` to trigger a `ResourceWarning` and fixes it. https://bugs.python.org/issue35721
1 parent 6d1c467 commit 9932fd9

File tree

4 files changed

+29
-10
lines changed

4 files changed

+29
-10
lines changed

Lib/asyncio/unix_events.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -757,12 +757,18 @@ def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
757757
# other end). Notably this is needed on AIX, and works
758758
# just fine on other platforms.
759759
stdin, stdin_w = socket.socketpair()
760-
self._proc = subprocess.Popen(
761-
args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
762-
universal_newlines=False, bufsize=bufsize, **kwargs)
763-
if stdin_w is not None:
764-
stdin.close()
765-
self._proc.stdin = open(stdin_w.detach(), 'wb', buffering=bufsize)
760+
try:
761+
self._proc = subprocess.Popen(
762+
args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
763+
universal_newlines=False, bufsize=bufsize, **kwargs)
764+
if stdin_w is not None:
765+
stdin.close()
766+
self._proc.stdin = open(stdin_w.detach(), 'wb', buffering=bufsize)
767+
stdin_w = None
768+
finally:
769+
if stdin_w is not None:
770+
stdin.close()
771+
stdin_w.close()
766772

767773

768774
class AbstractChildWatcher:

Lib/test/test_asyncio/test_subprocess.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,7 @@ async def kill_running():
468468
isinstance(self, SubprocessFastWatcherTests)):
469469
asyncio.get_child_watcher()._callbacks.clear()
470470

471-
def test_popen_error(self):
472-
# Issue #24763: check that the subprocess transport is closed
473-
# when BaseSubprocessTransport fails
471+
def _test_popen_error(self, stdin):
474472
if sys.platform == 'win32':
475473
target = 'asyncio.windows_utils.Popen'
476474
else:
@@ -480,12 +478,23 @@ def test_popen_error(self):
480478
popen.side_effect = exc
481479

482480
create = asyncio.create_subprocess_exec(sys.executable, '-c',
483-
'pass', loop=self.loop)
481+
'pass', stdin=stdin,
482+
loop=self.loop)
484483
with warnings.catch_warnings(record=True) as warns:
485484
with self.assertRaises(exc):
486485
self.loop.run_until_complete(create)
487486
self.assertEqual(warns, [])
488487

488+
def test_popen_error(self):
489+
# Issue #24763: check that the subprocess transport is closed
490+
# when BaseSubprocessTransport fails
491+
self._test_popen_error(stdin=None)
492+
493+
def test_popen_error_with_stdin_pipe(self):
494+
# Issue #35721: check that newly created socket pair is closed when
495+
# Popen fails
496+
self._test_popen_error(stdin=subprocess.PIPE)
497+
489498
def test_read_stdout_after_process_exit(self):
490499

491500
async def execute():

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ Florian Festi
486486
John Feuerstein
487487
Carl Feynman
488488
Vincent Fiack
489+
Niklas Fiekas
489490
Anastasia Filatova
490491
Tomer Filiba
491492
Segev Finer
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :meth:`asyncio.SelectorEventLoop.subprocess_exec()` leaks file descriptors
2+
if ``Popen`` fails and called with ``stdin=subprocess.PIPE``.
3+
Patch by Niklas Fiekas.

0 commit comments

Comments
 (0)