Skip to content

Commit 3887932

Browse files
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 (cherry picked from commit 9932fd9) Co-authored-by: Niklas Fiekas <[email protected]>
1 parent 2d94d4f commit 3887932

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
@@ -758,12 +758,18 @@ def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
758758
# other end). Notably this is needed on AIX, and works
759759
# just fine on other platforms.
760760
stdin, stdin_w = socket.socketpair()
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)
761+
try:
762+
self._proc = subprocess.Popen(
763+
args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
764+
universal_newlines=False, bufsize=bufsize, **kwargs)
765+
if stdin_w is not None:
766+
stdin.close()
767+
self._proc.stdin = open(stdin_w.detach(), 'wb', buffering=bufsize)
768+
stdin_w = None
769+
finally:
770+
if stdin_w is not None:
771+
stdin.close()
772+
stdin_w.close()
767773

768774

769775
class AbstractChildWatcher:

Lib/test/test_asyncio/test_subprocess.py

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

466-
def test_popen_error(self):
467-
# Issue #24763: check that the subprocess transport is closed
468-
# when BaseSubprocessTransport fails
466+
def _test_popen_error(self, stdin):
469467
if sys.platform == 'win32':
470468
target = 'asyncio.windows_utils.Popen'
471469
else:
@@ -475,12 +473,23 @@ def test_popen_error(self):
475473
popen.side_effect = exc
476474

477475
create = asyncio.create_subprocess_exec(sys.executable, '-c',
478-
'pass', loop=self.loop)
476+
'pass', stdin=stdin,
477+
loop=self.loop)
479478
with warnings.catch_warnings(record=True) as warns:
480479
with self.assertRaises(exc):
481480
self.loop.run_until_complete(create)
482481
self.assertEqual(warns, [])
483482

483+
def test_popen_error(self):
484+
# Issue #24763: check that the subprocess transport is closed
485+
# when BaseSubprocessTransport fails
486+
self._test_popen_error(stdin=None)
487+
488+
def test_popen_error_with_stdin_pipe(self):
489+
# Issue #35721: check that newly created socket pair is closed when
490+
# Popen fails
491+
self._test_popen_error(stdin=subprocess.PIPE)
492+
484493
def test_read_stdout_after_process_exit(self):
485494

486495
async def execute():

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ Florian Festi
479479
John Feuerstein
480480
Carl Feynman
481481
Vincent Fiack
482+
Niklas Fiekas
482483
Anastasia Filatova
483484
Tomer Filiba
484485
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)