summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-04-01 16:49:29 (GMT)
committerGitHub <noreply@github.com>2020-04-01 16:49:29 (GMT)
commit65a796e5272f61b42792d3a8c69686558c1872c5 (patch)
tree138d64a8dd04ab4d1cac2eb5c415aa10e0bbe00f /Modules
parent5dd836030e0e399b21ab0865ae0d93934bdb3930 (diff)
downloadcpython-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')
-rw-r--r--Modules/clinic/posixmodule.c.h62
-rw-r--r--Modules/posixmodule.c79
2 files changed, 140 insertions, 1 deletions
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 48dd7a7..8ff06fe 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -8274,6 +8274,62 @@ exit:
#endif /* defined(MS_WINDOWS) */
+#if (defined(WIFEXITED) || defined(MS_WINDOWS))
+
+PyDoc_STRVAR(os_waitstatus_to_exitcode__doc__,
+"waitstatus_to_exitcode($module, /, status)\n"
+"--\n"
+"\n"
+"Convert a wait status to an exit code.\n"
+"\n"
+"On Unix:\n"
+"\n"
+"* If WIFEXITED(status) is true, return WEXITSTATUS(status).\n"
+"* If WIFSIGNALED(status) is true, return -WTERMSIG(status).\n"
+"* Otherwise, raise a ValueError.\n"
+"\n"
+"On Windows, return status shifted right by 8 bits.\n"
+"\n"
+"On Unix, if the process is being traced or if waitpid() was called with\n"
+"WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true.\n"
+"This function must not be called if WIFSTOPPED(status) is true.");
+
+#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF \
+ {"waitstatus_to_exitcode", (PyCFunction)(void(*)(void))os_waitstatus_to_exitcode, METH_FASTCALL|METH_KEYWORDS, os_waitstatus_to_exitcode__doc__},
+
+static PyObject *
+os_waitstatus_to_exitcode_impl(PyObject *module, int status);
+
+static PyObject *
+os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"status", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "waitstatus_to_exitcode", 0};
+ PyObject *argsbuf[1];
+ int status;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (PyFloat_Check(args[0])) {
+ PyErr_SetString(PyExc_TypeError,
+ "integer argument expected, got float" );
+ goto exit;
+ }
+ status = _PyLong_AsInt(args[0]);
+ if (status == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = os_waitstatus_to_exitcode_impl(module, status);
+
+exit:
+ return return_value;
+}
+
+#endif /* (defined(WIFEXITED) || defined(MS_WINDOWS)) */
+
#ifndef OS_TTYNAME_METHODDEF
#define OS_TTYNAME_METHODDEF
#endif /* !defined(OS_TTYNAME_METHODDEF) */
@@ -8809,4 +8865,8 @@ exit:
#ifndef OS__REMOVE_DLL_DIRECTORY_METHODDEF
#define OS__REMOVE_DLL_DIRECTORY_METHODDEF
#endif /* !defined(OS__REMOVE_DLL_DIRECTORY_METHODDEF) */
-/*[clinic end generated code: output=5d99f90cead7c0e1 input=a9049054013a1b77]*/
+
+#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
+ #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
+#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
+/*[clinic end generated code: output=4e28994a729eddf9 input=a9049054013a1b77]*/
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 */
};