summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-04-22 15:57:59 (GMT)
committerGitHub <noreply@github.com>2020-04-22 15:57:59 (GMT)
commitb07350901cac9197aef41855d8a4d56533636b91 (patch)
treeb9ab42158db0cea7b537f8dc3e23d9f8ec2bedba
parent3a5545025685040842420c85a4a9aab5f044aeb8 (diff)
downloadcpython-b07350901cac9197aef41855d8a4d56533636b91.zip
cpython-b07350901cac9197aef41855d8a4d56533636b91.tar.gz
cpython-b07350901cac9197aef41855d8a4d56533636b91.tar.bz2
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.
-rw-r--r--Lib/test/test_os.py35
-rw-r--r--Misc/NEWS.d/next/Library/2020-04-22-00-05-10.bpo-40138.i_oGqa.rst2
-rw-r--r--Modules/posixmodule.c4
3 files changed, 35 insertions, 6 deletions
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