From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Wed, 22 Apr 2020 16:16:42 +0000 (-0700) Subject: bpo-40138: Fix Windows os.waitpid() for large exit code (GH-19654) X-Git-Tag: v3.7.8rc1~81 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=de5dcfa3bcabf52e43468a2b088ed71b5e5c4503;p=thirdparty%2FPython%2Fcpython.git 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. (cherry picked from commit b07350901cac9197aef41855d8a4d56533636b91) Co-authored-by: Victor Stinner --- diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 411e5aa50738..3a76940a0894 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2417,12 +2417,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 000000000000..ad5faf386575 --- /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 87ef2a9e0d22..977e49f432a3 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7127,8 +7127,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