[3.12] gh-110036: multiprocessing Popen.terminate() catches PermissionError (GH-110037) (#110064)

gh-110036: multiprocessing Popen.terminate() catches PermissionError (GH-110037)

On Windows, multiprocessing Popen.terminate() now catchs
PermissionError and get the process exit code. If the process is
still running, raise again the PermissionError. Otherwise, the
process terminated as expected: store its exit code.
(cherry picked from commit bd4518c60c)

Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
Miss Islington (bot) 2023-10-02 08:44:26 -07:00 committed by GitHub
parent aaa8bf18c6
commit a2074911ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 17 additions and 4 deletions

View file

@ -14,6 +14,7 @@ __all__ = ['Popen']
# #
# #
# Exit code used by Popen.terminate()
TERMINATE = 0x10000 TERMINATE = 0x10000
WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
@ -122,9 +123,15 @@ class Popen(object):
if self.returncode is None: if self.returncode is None:
try: try:
_winapi.TerminateProcess(int(self._handle), TERMINATE) _winapi.TerminateProcess(int(self._handle), TERMINATE)
except OSError: except PermissionError:
if self.wait(timeout=1.0) is None: # ERROR_ACCESS_DENIED (winerror 5) is received when the
# process already died.
code = _winapi.GetExitCodeProcess(int(self._handle))
if code == _winapi.STILL_ACTIVE:
raise raise
self.returncode = code
else:
self.returncode = -signal.SIGTERM
kill = terminate kill = terminate

View file

@ -557,13 +557,14 @@ class _TestProcess(BaseTestCase):
def test_terminate(self): def test_terminate(self):
exitcode = self._kill_process(multiprocessing.Process.terminate) exitcode = self._kill_process(multiprocessing.Process.terminate)
if os.name != 'nt': self.assertEqual(exitcode, -signal.SIGTERM)
self.assertEqual(exitcode, -signal.SIGTERM)
def test_kill(self): def test_kill(self):
exitcode = self._kill_process(multiprocessing.Process.kill) exitcode = self._kill_process(multiprocessing.Process.kill)
if os.name != 'nt': if os.name != 'nt':
self.assertEqual(exitcode, -signal.SIGKILL) self.assertEqual(exitcode, -signal.SIGKILL)
else:
self.assertEqual(exitcode, -signal.SIGTERM)
def test_cpu_count(self): def test_cpu_count(self):
try: try:

View file

@ -0,0 +1,5 @@
On Windows, multiprocessing ``Popen.terminate()`` now catchs
:exc:`PermissionError` and get the process exit code. If the process is
still running, raise again the :exc:`PermissionError`. Otherwise, the
process terminated as expected: store its exit code. Patch by Victor
Stinner.