Repository navigation
bpo-40138: Fix Windows os.waitpid() for large exit code #19637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7972,8 +7972,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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any possibility that
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That would be a backward incompatible changes. If you look at changes that I made in https://bugs.python.org/issue40094 to use os.waitstatus_to_exitcode(), you can see that even in the Python stdlib, plently of code used directly "status >> 8" without even checking os.WIFEXITED(). I don't think that it's worth it to change how waitpid() encodes the exit code in the exit status. You should now use os.waitstatus_to_exitcode() to write portable code. |
||
| } | ||
| #endif | ||
|
|
||
|
|
@@ -13829,7 +13831,7 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) | |
| /*[clinic input] | ||
| os.waitstatus_to_exitcode | ||
|
|
||
| status: int | ||
| status as status_obj: object | ||
|
|
||
| Convert a wait status to an exit code. | ||
|
|
||
|
|
@@ -13847,10 +13849,20 @@ This function must not be called if WIFSTOPPED(status) is true. | |
| [clinic start generated code]*/ | ||
|
|
||
| static PyObject * | ||
| os_waitstatus_to_exitcode_impl(PyObject *module, int status) | ||
| /*[clinic end generated code: output=c7c2265731f79b7a input=edfa5ca5006276fb]*/ | ||
| os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) | ||
| /*[clinic end generated code: output=db50b1b0ba3c7153 input=7fe2d7fdaea3db42]*/ | ||
| { | ||
| if (PyFloat_Check(status_obj)) { | ||
| PyErr_SetString(PyExc_TypeError, | ||
| "integer argument expected, got float" ); | ||
| return NULL; | ||
| } | ||
| #ifndef MS_WINDOWS | ||
| int status = _PyLong_AsInt(status_obj); | ||
| if (status == -1 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
|
|
||
| WAIT_TYPE wait_status; | ||
| WAIT_STATUS_INT(wait_status) = status; | ||
| int exitcode; | ||
|
|
@@ -13889,8 +13901,19 @@ os_waitstatus_to_exitcode_impl(PyObject *module, int status) | |
| #else | ||
| /* Windows implementation: see os.waitpid() implementation | ||
| which uses _cwait(). */ | ||
| int exitcode = (status >> 8); | ||
| return PyLong_FromLong(exitcode); | ||
| unsigned long long status = PyLong_AsUnsignedLongLong(status_obj); | ||
| if (status == (unsigned long long)-1 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
|
|
||
| unsigned long long exitcode = (status >> 8); | ||
| /* ExitProcess() accepts an UINT type: | ||
| reject exit code which doesn't fit in an UINT */ | ||
| if (exitcode > UINT_MAX) { | ||
| PyErr_Format(PyExc_ValueError, "invalid exit code: %llu", exitcode); | ||
| return NULL; | ||
| } | ||
| return PyLong_FromUnsignedLong((unsigned long)exitcode); | ||
| #endif | ||
| } | ||
| #endif | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spawnvconcatenates the arguments into a command line that gets passed toCreateProcessWin thelpCommandLineparameter. No shell is involved here. Unlike Python'ssubprocess.list2cmdline, the CRT'sspawnfamily does not create a command line according to MSVC rules. It simply concatenates with spaces, which leaves it up to the caller to ensure that each argument is quoted and escaped according to the rules of the target application.You can avoid the need to escape quotes in an argument by using only single quotes, which have no special meaning in MSVC
argvparsing. For example:args = ['python', '-c', f'"{code}"'], wherecodecontains no double quote characters.There's no need for
args[0]to be a fully-qualified path withspawnv. Actually, what you're doing by settingargs[0] = sys.executableis wrong. It only works because the test build's path to "python.exe" contains no spaces. If the path has spaces and isn't quoted, then Cargv[0]will only include up to the first space in the path, and the rest will be inargv[1], and so on. If you want to use the full path, it has to be quoted. For example, wheresys.executableis "C:\Program Files\Python38\python.exe":Failing example without quoting:
Preferably, just use unqualified "python":
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I didn't think about quoting sys.executable. I fixed the test.