Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b69ef16

Browse files
committed
Issue #14252: Fix subprocess.Popen.terminate() to not raise an error under Windows when the child process has already exited.
2 parents 6c52c57 + 1f9a835 commit b69ef16

4 files changed

Lines changed: 80 additions & 1 deletion

File tree

Lib/subprocess.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,15 @@ def send_signal(self, sig):
11621162
def terminate(self):
11631163
"""Terminates the process
11641164
"""
1165-
_subprocess.TerminateProcess(self._handle, 1)
1165+
try:
1166+
_subprocess.TerminateProcess(self._handle, 1)
1167+
except PermissionError:
1168+
# ERROR_ACCESS_DENIED (winerror 5) is received when the
1169+
# process already died.
1170+
rc = _subprocess.GetExitCodeProcess(self._handle)
1171+
if rc == _subprocess.STILL_ACTIVE:
1172+
raise
1173+
self.returncode = rc
11661174

11671175
kill = terminate
11681176

Lib/test/test_subprocess.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,27 @@ def _kill_process(self, method, *args):
10781078
getattr(p, method)(*args)
10791079
return p
10801080

1081+
def _kill_dead_process(self, method, *args):
1082+
# Do not inherit file handles from the parent.
1083+
# It should fix failures on some platforms.
1084+
p = subprocess.Popen([sys.executable, "-c", """if 1:
1085+
import sys, time
1086+
sys.stdout.write('x\\n')
1087+
sys.stdout.flush()
1088+
"""],
1089+
close_fds=True,
1090+
stdin=subprocess.PIPE,
1091+
stdout=subprocess.PIPE,
1092+
stderr=subprocess.PIPE)
1093+
# Wait for the interpreter to be completely initialized before
1094+
# sending any signal.
1095+
p.stdout.read(1)
1096+
# The process should end after this
1097+
time.sleep(1)
1098+
# This shouldn't raise even though the child is now dead
1099+
getattr(p, method)(*args)
1100+
p.communicate()
1101+
10811102
def test_send_signal(self):
10821103
p = self._kill_process('send_signal', signal.SIGINT)
10831104
_, stderr = p.communicate()
@@ -1096,6 +1117,18 @@ def test_terminate(self):
10961117
self.assertStderrEqual(stderr, b'')
10971118
self.assertEqual(p.wait(), -signal.SIGTERM)
10981119

1120+
def test_send_signal_dead(self):
1121+
# Sending a signal to a dead process
1122+
self._kill_dead_process('send_signal', signal.SIGINT)
1123+
1124+
def test_kill_dead(self):
1125+
# Killing a dead process
1126+
self._kill_dead_process('kill')
1127+
1128+
def test_terminate_dead(self):
1129+
# Terminating a dead process
1130+
self._kill_dead_process('terminate')
1131+
10991132
def check_close_std_fds(self, fds):
11001133
# Issue #9905: test that subprocess pipes still work properly with
11011134
# some standard fds closed
@@ -1662,6 +1695,31 @@ def _kill_process(self, method, *args):
16621695
returncode = p.wait()
16631696
self.assertNotEqual(returncode, 0)
16641697

1698+
def _kill_dead_process(self, method, *args):
1699+
p = subprocess.Popen([sys.executable, "-c", """if 1:
1700+
import sys, time
1701+
sys.stdout.write('x\\n')
1702+
sys.stdout.flush()
1703+
sys.exit(42)
1704+
"""],
1705+
stdin=subprocess.PIPE,
1706+
stdout=subprocess.PIPE,
1707+
stderr=subprocess.PIPE)
1708+
self.addCleanup(p.stdout.close)
1709+
self.addCleanup(p.stderr.close)
1710+
self.addCleanup(p.stdin.close)
1711+
# Wait for the interpreter to be completely initialized before
1712+
# sending any signal.
1713+
p.stdout.read(1)
1714+
# The process should end after this
1715+
time.sleep(1)
1716+
# This shouldn't raise even though the child is now dead
1717+
getattr(p, method)(*args)
1718+
_, stderr = p.communicate()
1719+
self.assertStderrEqual(stderr, b'')
1720+
rc = p.wait()
1721+
self.assertEqual(rc, 42)
1722+
16651723
def test_send_signal(self):
16661724
self._kill_process('send_signal', signal.SIGTERM)
16671725

@@ -1671,6 +1729,15 @@ def test_kill(self):
16711729
def test_terminate(self):
16721730
self._kill_process('terminate')
16731731

1732+
def test_send_signal_dead(self):
1733+
self._kill_dead_process('send_signal', signal.SIGTERM)
1734+
1735+
def test_kill_dead(self):
1736+
self._kill_dead_process('kill')
1737+
1738+
def test_terminate_dead(self):
1739+
self._kill_dead_process('terminate')
1740+
16741741

16751742
# The module says:
16761743
# "NB This only works (and is only relevant) for UNIX."

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Core and Builtins
2222
Library
2323
-------
2424

25+
- Issue #14252: Fix subprocess.Popen.terminate() to not raise an error under
26+
Windows when the child process has already exited.
27+
2528
- Issue #14223: curses.addch() is no more limited to the range 0-255 when the
2629
Python curses is not linked to libncursesw. It was a regression introduced
2730
in Python 3.3a1.

PC/_subprocess.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ PyInit__subprocess()
691691
defint(d, "WAIT_TIMEOUT", WAIT_TIMEOUT);
692692
defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
693693
defint(d, "CREATE_NEW_PROCESS_GROUP", CREATE_NEW_PROCESS_GROUP);
694+
defint(d, "STILL_ACTIVE", STILL_ACTIVE);
694695

695696
return m;
696697
}

0 commit comments

Comments
 (0)