aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-04-01 18:49:29 +0200
committerGitHub <noreply@github.com>2020-04-01 18:49:29 +0200
commit65a796e5272f61b42792d3a8c69686558c1872c5 (patch)
tree138d64a8dd04ab4d1cac2eb5c415aa10e0bbe00f /Modules/posixmodule.c
parentbpo-38527: fix configure script for Solaris (GH-16845) (diff)
downloadcpython-65a796e5272f61b42792d3a8c69686558c1872c5.tar.gz
cpython-65a796e5272f61b42792d3a8c69686558c1872c5.tar.bz2
cpython-65a796e5272f61b42792d3a8c69686558c1872c5.zip
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.c79
1 files changed, 79 insertions, 0 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 9ab136b2525..1adca8ec29d 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 */
};