]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-110036: multiprocessing Popen.terminate() catches PermissionError (#110037)
authorVictor Stinner <vstinner@python.org>
Fri, 29 Sep 2023 00:41:12 +0000 (02:41 +0200)
committerGitHub <noreply@github.com>
Fri, 29 Sep 2023 00:41:12 +0000 (02:41 +0200)
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.

Lib/multiprocessing/popen_spawn_win32.py
Lib/test/_test_multiprocessing.py
Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst [new file with mode: 0644]

index 4d60ffc030bea6a7999417a2bb08c2aadc278816..af044305709e56cf55e3df46c9ee1c2d31efddac 100644 (file)
@@ -14,6 +14,7 @@ __all__ = ['Popen']
 #
 #
 
+# Exit code used by Popen.terminate()
 TERMINATE = 0x10000
 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
@@ -122,9 +123,15 @@ class Popen(object):
         if self.returncode is None:
             try:
                 _winapi.TerminateProcess(int(self._handle), TERMINATE)
-            except OSError:
-                if self.wait(timeout=1.0) is None:
+            except PermissionError:
+                # ERROR_ACCESS_DENIED (winerror 5) is received when the
+                # process already died.
+                code = _winapi.GetExitCodeProcess(int(self._handle))
+                if code == _winapi.STILL_ACTIVE:
                     raise
+                self.returncode = code
+            else:
+                self.returncode = -signal.SIGTERM
 
     kill = terminate
 
index 756d6808518fc4469bd1f621374445916e23105f..39666dd331db0bfd603c571354a87eba8dc48d83 100644 (file)
@@ -557,13 +557,14 @@ class _TestProcess(BaseTestCase):
 
     def test_terminate(self):
         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):
         exitcode = self._kill_process(multiprocessing.Process.kill)
         if os.name != 'nt':
             self.assertEqual(exitcode, -signal.SIGKILL)
+        else:
+            self.assertEqual(exitcode, -signal.SIGTERM)
 
     def test_cpu_count(self):
         try:
diff --git a/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst b/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst
new file mode 100644 (file)
index 0000000..ddb11b5
--- /dev/null
@@ -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.