| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent bcc136b commit 9bee32b
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2789,40 +2789,66 @@ def test_getppid(self): | |||
| 2789 | 2789 | # We are the parent of our subprocess | |
| 2790 | 2790 | self.assertEqual(int(stdout), os.getpid()) | |
| 2791 | 2791 | ||
| 2792 | + def check_waitpid(self, code, exitcode, callback=None): | ||
| 2793 | + if sys.platform == 'win32': | ||
| 2794 | + # On Windows, os.spawnv() simply joins arguments with spaces: | ||
| 2795 | + # arguments need to be quoted | ||
| 2796 | + args = [f'"{sys.executable}"', '-c', f'"{code}"'] | ||
| 2797 | + else: | ||
| 2798 | + args = [sys.executable, '-c', code] | ||
| 2799 | + pid = os.spawnv(os.P_NOWAIT, sys.executable, args) | ||
| 2800 | + | ||
| 2801 | + if callback is not None: | ||
| 2802 | + callback(pid) | ||
| 2803 | + | ||
| 2804 | + # don't use support.wait_process() to test directly os.waitpid() | ||
| 2805 | + # and os.waitstatus_to_exitcode() | ||
| 2806 | + pid2, status = os.waitpid(pid, 0) | ||
| 2807 | + self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) | ||
| 2808 | + self.assertEqual(pid2, pid) | ||
| 2809 | + | ||
| 2792 | 2810 | def test_waitpid(self): | |
| 2793 | - args = [sys.executable, '-c', 'pass'] | ||
| 2794 | - # Add an implicit test for PyUnicode_FSConverter(). | ||
| 2795 | - pid = os.spawnv(os.P_NOWAIT, FakePath(args[0]), args) | ||
| 2796 | - support.wait_process(pid, exitcode=0) | ||
| 2811 | + self.check_waitpid(code='pass', exitcode=0) | ||
| 2797 | 2812 | ||
| 2798 | 2813 | def test_waitstatus_to_exitcode(self): | |
| 2799 | 2814 | exitcode = 23 | |
| 2800 | - filename = support.TESTFN | ||
| 2801 | - self.addCleanup(support.unlink, filename) | ||
| 2815 | + code = f'import sys; sys.exit({exitcode})' | ||
| 2816 | + self.check_waitpid(code, exitcode=exitcode) | ||
| 2802 | 2817 | ||
| 2803 | - with open(filename, "w") as fp: | ||
| 2804 | - print(f'import sys; sys.exit({exitcode})', file=fp) | ||
| 2805 | - fp.flush() | ||
| 2806 | - args = [sys.executable, filename] | ||
| 2807 | - pid = os.spawnv(os.P_NOWAIT, args[0], args) | ||
| 2818 | + with self.assertRaises(TypeError): | ||
| 2819 | + os.waitstatus_to_exitcode(0.0) | ||
| 2808 | 2820 | ||
| 2809 | - pid2, status = os.waitpid(pid, 0) | ||
| 2810 | - self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) | ||
| 2811 | - self.assertEqual(pid2, pid) | ||
| 2821 | + @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') | ||
| 2822 | + def test_waitpid_windows(self): | ||
| 2823 | + # bpo-40138: test os.waitpid() and os.waitstatus_to_exitcode() | ||
| 2824 | + # with exit code larger than INT_MAX. | ||
| 2825 | + STATUS_CONTROL_C_EXIT = 0xC000013A | ||
| 2826 | + code = f'import _winapi; _winapi.ExitProcess({STATUS_CONTROL_C_EXIT})' | ||
| 2827 | + self.check_waitpid(code, exitcode=STATUS_CONTROL_C_EXIT) | ||
| 2828 | + | ||
| 2829 | + @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') | ||
| 2830 | + def test_waitstatus_to_exitcode_windows(self): | ||
| 2831 | + max_exitcode = 2 ** 32 - 1 | ||
| 2832 | + for exitcode in (0, 1, 5, max_exitcode): | ||
| 2833 | + self.assertEqual(os.waitstatus_to_exitcode(exitcode << 8), | ||
| 2834 | + exitcode) | ||
| 2835 | + | ||
| 2836 | + # invalid values | ||
| 2837 | + with self.assertRaises(ValueError): | ||
| 2838 | + os.waitstatus_to_exitcode((max_exitcode + 1) << 8) | ||
| 2839 | + with self.assertRaises(OverflowError): | ||
| 2840 | + os.waitstatus_to_exitcode(-1) | ||
| 2812 | 2841 | ||
| 2813 | 2842 | # Skip the test on Windows | |
| 2814 | 2843 | @unittest.skipUnless(hasattr(signal, 'SIGKILL'), 'need signal.SIGKILL') | |
| 2815 | 2844 | def test_waitstatus_to_exitcode_kill(self): | |
| 2845 | + code = f'import time; time.sleep({support.LONG_TIMEOUT})' | ||
| 2816 | 2846 | signum = signal.SIGKILL | |
| 2817 | - args = [sys.executable, '-c', | ||
| 2818 | - f'import time; time.sleep({support.LONG_TIMEOUT})'] | ||
| 2819 | - pid = os.spawnv(os.P_NOWAIT, args[0], args) | ||
| 2820 | 2847 | ||
| 2821 | - os.kill(pid, signum) | ||
| 2848 | + def kill_process(pid): | ||
| 2849 | + os.kill(pid, signum) | ||
| 2822 | 2850 | ||
| 2823 | - pid2, status = os.waitpid(pid, 0) | ||
| 2824 | - self.assertEqual(os.waitstatus_to_exitcode(status), -signum) | ||
| 2825 | - self.assertEqual(pid2, pid) | ||
| 2851 | + self.check_waitpid(code, exitcode=-signum, callback=kill_process) | ||
| 2826 | 2852 | ||
| 2827 | 2853 | ||
| 2828 | 2854 | class SpawnTests(unittest.TestCase): | |
@@ -2884,6 +2910,10 @@ def test_spawnv(self): | |||
| 2884 | 2910 | exitcode = os.spawnv(os.P_WAIT, args[0], args) | |
| 2885 | 2911 | self.assertEqual(exitcode, self.exitcode) | |
| 2886 | 2912 | ||
| 2913 | + # Test for PyUnicode_FSConverter() | ||
| 2914 | + exitcode = os.spawnv(os.P_WAIT, FakePath(args[0]), args) | ||
| 2915 | + self.assertEqual(exitcode, self.exitcode) | ||
| 2916 | + | ||
| 2887 | 2917 | @requires_os_func('spawnve') | |
| 2888 | 2918 | def test_spawnve(self): | |
| 2889 | 2919 | args = self.create_args(with_env=True) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Fix the Windows implementation of :func:`os.waitpid` for exit code larger than | ||
| 2 | + ``INT_MAX >> 8``. The exit status is now interpreted as an unsigned number. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7972,8 +7972,10 @@ os_waitpid_impl(PyObject *module, intptr_t pid, int options) | |||
| 7972 | 7972 | if (res < 0) | |
| 7973 | 7973 | return (!async_err) ? posix_error() : NULL; | |
| 7974 | 7974 | ||
| 7975 | + unsigned long long ustatus = (unsigned int)status; | ||
| 7976 | + | ||
| 7975 | 7977 | /* shift the status left a byte so this is more like the POSIX waitpid */ | |
| 7976 | - return Py_BuildValue(_Py_PARSE_INTPTR "i", res, status << 8); | ||
| 7978 | + return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8); | ||
| 7977 | 7979 | } | |
| 7978 | 7980 | #endif | |
| 7979 | 7981 | ||
@@ -13829,7 +13831,7 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) | |||
| 13829 | 13831 | /*[clinic input] | |
| 13830 | 13832 | os.waitstatus_to_exitcode | |
| 13831 | 13833 | ||
| 13832 | - status: int | ||
| 13834 | + status as status_obj: object | ||
| 13833 | 13835 | ||
| 13834 | 13836 | Convert a wait status to an exit code. | |
| 13835 | 13837 | ||
@@ -13847,10 +13849,20 @@ This function must not be called if WIFSTOPPED(status) is true. | |||
| 13847 | 13849 | [clinic start generated code]*/ | |
| 13848 | 13850 | ||
| 13849 | 13851 | static PyObject * | |
| 13850 | - os_waitstatus_to_exitcode_impl(PyObject *module, int status) | ||
| 13851 | - /*[clinic end generated code: output=c7c2265731f79b7a input=edfa5ca5006276fb]*/ | ||
| 13852 | + os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) | ||
| 13853 | + /*[clinic end generated code: output=db50b1b0ba3c7153 input=7fe2d7fdaea3db42]*/ | ||
| 13852 | 13854 | { | |
| 13855 | + if (PyFloat_Check(status_obj)) { | ||
| 13856 | + PyErr_SetString(PyExc_TypeError, | ||
| 13857 | + "integer argument expected, got float" ); | ||
| 13858 | + return NULL; | ||
| 13859 | + } | ||
| 13853 | 13860 | #ifndef MS_WINDOWS | |
| 13861 | + int status = _PyLong_AsInt(status_obj); | ||
| 13862 | + if (status == -1 && PyErr_Occurred()) { | ||
| 13863 | + return NULL; | ||
| 13864 | + } | ||
| 13865 | + | ||
| 13854 | 13866 | WAIT_TYPE wait_status; | |
| 13855 | 13867 | WAIT_STATUS_INT(wait_status) = status; | |
| 13856 | 13868 | int exitcode; | |
@@ -13889,8 +13901,19 @@ os_waitstatus_to_exitcode_impl(PyObject *module, int status) | |||
| 13889 | 13901 | #else | |
| 13890 | 13902 | /* Windows implementation: see os.waitpid() implementation | |
| 13891 | 13903 | which uses _cwait(). */ | |
| 13892 | - int exitcode = (status >> 8); | ||
| 13893 | - return PyLong_FromLong(exitcode); | ||
| 13904 | + unsigned long long status = PyLong_AsUnsignedLongLong(status_obj); | ||
| 13905 | + if (status == (unsigned long long)-1 && PyErr_Occurred()) { | ||
| 13906 | + return NULL; | ||
| 13907 | + } | ||
| 13908 | + | ||
| 13909 | + unsigned long long exitcode = (status >> 8); | ||
| 13910 | + /* ExitProcess() accepts an UINT type: | ||
| 13911 | + reject exit code which doesn't fit in an UINT */ | ||
| 13912 | + if (exitcode > UINT_MAX) { | ||
| 13913 | + PyErr_Format(PyExc_ValueError, "invalid exit code: %llu", exitcode); | ||
| 13914 | + return NULL; | ||
| 13915 | + } | ||
| 13916 | + return PyLong_FromUnsignedLong((unsigned long)exitcode); | ||
| 13894 | 13917 | #endif | |
| 13895 | 13918 | } | |
| 13896 | 13919 | #endif | |
| Back | FazBrowse Home | New Git URL |
0 commit comments