diff options
author | Victor Stinner <vstinner@python.org> | 2020-04-01 16:49:29 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-01 16:49:29 (GMT) |
commit | 65a796e5272f61b42792d3a8c69686558c1872c5 (patch) | |
tree | 138d64a8dd04ab4d1cac2eb5c415aa10e0bbe00f /Modules/posixmodule.c | |
parent | 5dd836030e0e399b21ab0865ae0d93934bdb3930 (diff) | |
download | cpython-65a796e5272f61b42792d3a8c69686558c1872c5.zip cpython-65a796e5272f61b42792d3a8c69686558c1872c5.tar.gz cpython-65a796e5272f61b42792d3a8c69686558c1872c5.tar.bz2 |
bpo-40094: Add os.waitstatus_to_exitcode() (GH-19201)
Add os.waitstatus_to_exitcode() function to convert a wait status to an
exitcode.
Suggest waitstatus_to_exitcode() usage in the documentation when
appropriate.
Use waitstatus_to_exitcode() in:
* multiprocessing, os, subprocess and _bootsubprocess modules;
* test.support.wait_process();
* setup.py: run_command();
* and many tests.
Diffstat (limited to 'Modules/posixmodule.c')
-rw-r--r-- | Modules/posixmodule.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 9ab136b..1adca8e 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -13771,6 +13771,84 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) #endif + +/* Only check if WIFEXITED is available: expect that it comes + with WEXITSTATUS, WIFSIGNALED, etc. + + os.waitstatus_to_exitcode() is implemented in C and not in Python, so + subprocess can safely call it during late Python finalization without + risking that used os attributes were set to None by _PyImport_Cleanup(). */ +#if defined(WIFEXITED) || defined(MS_WINDOWS) +/*[clinic input] +os.waitstatus_to_exitcode + + status: int + +Convert a wait status to an exit code. + +On Unix: + +* If WIFEXITED(status) is true, return WEXITSTATUS(status). +* If WIFSIGNALED(status) is true, return -WTERMSIG(status). +* Otherwise, raise a ValueError. + +On Windows, return status shifted right by 8 bits. + +On Unix, if the process is being traced or if waitpid() was called with +WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true. +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]*/ +{ +#ifndef MS_WINDOWS + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + int exitcode; + if (WIFEXITED(wait_status)) { + exitcode = WEXITSTATUS(wait_status); + /* Sanity check to provide warranty on the function behavior. + It should not occur in practice */ + if (exitcode < 0) { + PyErr_Format(PyExc_ValueError, "invalid WEXITSTATUS: %i", exitcode); + return NULL; + } + } + else if (WIFSIGNALED(wait_status)) { + int signum = WTERMSIG(wait_status); + /* Sanity check to provide warranty on the function behavior. + It should not occurs in practice */ + if (signum <= 0) { + PyErr_Format(PyExc_ValueError, "invalid WTERMSIG: %i", signum); + return NULL; + } + exitcode = -signum; + } else if (WIFSTOPPED(wait_status)) { + /* Status only received if the process is being traced + or if waitpid() was called with WUNTRACED option. */ + int signum = WSTOPSIG(wait_status); + PyErr_Format(PyExc_ValueError, + "process stopped by delivery of signal %i", + signum); + return NULL; + } + else { + PyErr_Format(PyExc_ValueError, "invalid wait status: %i", status); + return NULL; + } + return PyLong_FromLong(exitcode); +#else + /* Windows implementation: see os.waitpid() implementation + which uses _cwait(). */ + int exitcode = (status >> 8); + return PyLong_FromLong(exitcode); +#endif +} +#endif + + static PyMethodDef posix_methods[] = { OS_STAT_METHODDEF @@ -13964,6 +14042,7 @@ static PyMethodDef posix_methods[] = { OS__ADD_DLL_DIRECTORY_METHODDEF OS__REMOVE_DLL_DIRECTORY_METHODDEF #endif + OS_WAITSTATUS_TO_EXITCODE_METHODDEF {NULL, NULL} /* Sentinel */ }; |