From b07350901cac9197aef41855d8a4d56533636b91 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 22 Apr 2020 17:57:59 +0200 Subject: bpo-40138: Fix Windows os.waitpid() for large exit code (GH-19654) Fix the Windows implementation of os.waitpid() for exit code larger than "INT_MAX >> 8". The exit status is now interpreted as an unsigned number. --- Lib/test/test_os.py | 35 ++++++++++++++++++---- .../2020-04-22-00-05-10.bpo-40138.i_oGqa.rst | 2 ++ Modules/posixmodule.c | 4 ++- 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-04-22-00-05-10.bpo-40138.i_oGqa.rst diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 11454b2..2a4ae15 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2675,12 +2675,37 @@ class PidTests(unittest.TestCase): # We are the parent of our subprocess self.assertEqual(int(stdout), os.getpid()) + def check_waitpid(self, code, exitcode): + if sys.platform == 'win32': + # On Windows, os.spawnv() simply joins arguments with spaces: + # arguments need to be quoted + args = [f'"{sys.executable}"', '-c', f'"{code}"'] + else: + args = [sys.executable, '-c', code] + pid = os.spawnv(os.P_NOWAIT, sys.executable, args) + + pid2, status = os.waitpid(pid, 0) + if sys.platform == 'win32': + self.assertEqual(status, exitcode << 8) + else: + self.assertTrue(os.WIFEXITED(status), status) + self.assertEqual(os.WEXITSTATUS(status), exitcode) + self.assertEqual(pid2, pid) + def test_waitpid(self): - args = [sys.executable, '-c', 'pass'] - # Add an implicit test for PyUnicode_FSConverter(). - pid = os.spawnv(os.P_NOWAIT, FakePath(args[0]), args) - status = os.waitpid(pid, 0) - self.assertEqual(status, (pid, 0)) + self.check_waitpid(code='pass', exitcode=0) + + def test_waitpid_exitcode(self): + exitcode = 23 + code = f'import sys; sys.exit({exitcode})' + self.check_waitpid(code, exitcode=exitcode) + + @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') + def test_waitpid_windows(self): + # bpo-40138: test os.waitpid() with exit code larger than INT_MAX. + STATUS_CONTROL_C_EXIT = 0xC000013A + code = f'import _winapi; _winapi.ExitProcess({STATUS_CONTROL_C_EXIT})' + self.check_waitpid(code, exitcode=STATUS_CONTROL_C_EXIT) class SpawnTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2020-04-22-00-05-10.bpo-40138.i_oGqa.rst b/Misc/NEWS.d/next/Library/2020-04-22-00-05-10.bpo-40138.i_oGqa.rst new file mode 100644 index 0000000..ad5faf3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-04-22-00-05-10.bpo-40138.i_oGqa.rst @@ -0,0 +1,2 @@ +Fix the Windows implementation of :func:`os.waitpid` for exit code larger than +``INT_MAX >> 8``. The exit status is now interpreted as an unsigned number. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d40827d..eb0b56a 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7867,8 +7867,10 @@ os_waitpid_impl(PyObject *module, intptr_t pid, int options) if (res < 0) return (!async_err) ? posix_error() : NULL; + unsigned long long ustatus = (unsigned int)status; + /* shift the status left a byte so this is more like the POSIX waitpid */ - return Py_BuildValue(_Py_PARSE_INTPTR "i", res, status << 8); + return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8); } #endif -- cgit v0.12