370 lines
12 KiB
Diff
370 lines
12 KiB
Diff
From a4faef5368c5a15a2a4bb83aee451708ed622aee Mon Sep 17 00:00:00 2001
|
|
From: Pablo Galindo <Pablogsal@gmail.com>
|
|
Date: Mon, 29 Jan 2018 01:56:10 +0000
|
|
Subject: [PATCH] bpo-20104: Expose `posix_spawn` in the os module (GH-5109)
|
|
|
|
Add os.posix_spawn to wrap the low level POSIX API of the same name.
|
|
|
|
Contributed by Pablo Galindo.
|
|
|
|
Conflict:NA
|
|
Reference:https://github.com/python/cpython/commit/6c6ddf97c402709713d668d0ed53836a7749ba99
|
|
|
|
Signed-off-by: hanxinke <hanxinke@huawei.com>
|
|
---
|
|
Lib/test/test_posix.py | 16 ++
|
|
.../2018-01-06-01-14-53.bpo-20104.9DkKb8.rst | 1 +
|
|
Modules/clinic/posixmodule.c.h | 52 +++++
|
|
Modules/posixmodule.c | 202 ++++++++++++++++++
|
|
4 files changed, 271 insertions(+)
|
|
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-01-06-01-14-53.bpo-20104.9DkKb8.rst
|
|
|
|
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
|
|
index 08306b2..c67087c 100644
|
|
--- a/Lib/test/test_posix.py
|
|
+++ b/Lib/test/test_posix.py
|
|
@@ -192,6 +192,22 @@ class PosixTester(unittest.TestCase):
|
|
os.close(fp)
|
|
|
|
|
|
+ @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn")
|
|
+ def test_posix_spawn(self):
|
|
+ pid = posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], os.environ,[])
|
|
+ self.assertEqual(os.waitpid(pid,0),(pid,0))
|
|
+
|
|
+
|
|
+ @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn")
|
|
+ def test_posix_spawn_file_actions(self):
|
|
+ file_actions = []
|
|
+ file_actions.append((0,3,os.path.realpath(__file__),0,0))
|
|
+ file_actions.append((os.POSIX_SPAWN_CLOSE,2))
|
|
+ file_actions.append((os.POSIX_SPAWN_DUP2,1,4))
|
|
+ pid = posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], os.environ, file_actions)
|
|
+ self.assertEqual(os.waitpid(pid,0),(pid,0))
|
|
+
|
|
+
|
|
@unittest.skipUnless(hasattr(posix, 'waitid'), "test needs posix.waitid()")
|
|
@unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
|
|
def test_waitid(self):
|
|
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-06-01-14-53.bpo-20104.9DkKb8.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-06-01-14-53.bpo-20104.9DkKb8.rst
|
|
new file mode 100644
|
|
index 0000000..cb69f32
|
|
--- /dev/null
|
|
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-06-01-14-53.bpo-20104.9DkKb8.rst
|
|
@@ -0,0 +1 @@
|
|
+Expose posix_spawn as a low level API in the os module.
|
|
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
|
|
index 22eef68..07be916 100644
|
|
--- a/Modules/clinic/posixmodule.c.h
|
|
+++ b/Modules/clinic/posixmodule.c.h
|
|
@@ -1727,6 +1727,54 @@ exit:
|
|
|
|
#endif /* defined(HAVE_EXECV) */
|
|
|
|
+#if defined(HAVE_POSIX_SPAWN)
|
|
+
|
|
+PyDoc_STRVAR(os_posix_spawn__doc__,
|
|
+"posix_spawn($module, path, argv, env, file_actions=None, /)\n"
|
|
+"--\n"
|
|
+"\n"
|
|
+"Execute the program specified by path in a new process.\n"
|
|
+"\n"
|
|
+" path\n"
|
|
+" Path of executable file.\n"
|
|
+" argv\n"
|
|
+" Tuple or list of strings.\n"
|
|
+" env\n"
|
|
+" Dictionary of strings mapping to strings.\n"
|
|
+" file_actions\n"
|
|
+" FileActions object.");
|
|
+
|
|
+#define OS_POSIX_SPAWN_METHODDEF \
|
|
+ {"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL, os_posix_spawn__doc__},
|
|
+
|
|
+static PyObject *
|
|
+os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
|
|
+ PyObject *env, PyObject *file_actions);
|
|
+
|
|
+static PyObject *
|
|
+os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
|
+{
|
|
+ PyObject *return_value = NULL;
|
|
+ path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0);
|
|
+ PyObject *argv;
|
|
+ PyObject *env;
|
|
+ PyObject *file_actions = Py_None;
|
|
+
|
|
+ if (!_PyArg_ParseStack(args, nargs, "O&OO|O:posix_spawn",
|
|
+ path_converter, &path, &argv, &env, &file_actions)) {
|
|
+ goto exit;
|
|
+ }
|
|
+ return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions);
|
|
+
|
|
+exit:
|
|
+ /* Cleanup for path */
|
|
+ path_cleanup(&path);
|
|
+
|
|
+ return return_value;
|
|
+}
|
|
+
|
|
+#endif /* defined(HAVE_POSIX_SPAWN) */
|
|
+
|
|
#if (defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV))
|
|
|
|
PyDoc_STRVAR(os_spawnv__doc__,
|
|
@@ -6147,6 +6195,10 @@ exit:
|
|
#define OS_EXECVE_METHODDEF
|
|
#endif /* !defined(OS_EXECVE_METHODDEF) */
|
|
|
|
+#ifndef OS_POSIX_SPAWN_METHODDEF
|
|
+ #define OS_POSIX_SPAWN_METHODDEF
|
|
+#endif /* !defined(OS_POSIX_SPAWN_METHODDEF) */
|
|
+
|
|
#ifndef OS_SPAWNV_METHODDEF
|
|
#define OS_SPAWNV_METHODDEF
|
|
#endif /* !defined(OS_SPAWNV_METHODDEF) */
|
|
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
|
|
index 43d4302..fcb22c2 100644
|
|
--- a/Modules/posixmodule.c
|
|
+++ b/Modules/posixmodule.c
|
|
@@ -176,6 +176,7 @@ corresponding Unix manual entries for more information on calls.");
|
|
#else
|
|
/* Unix functions that the configure script doesn't check for */
|
|
#define HAVE_EXECV 1
|
|
+#define HAVE_POSIX_SPAWN 1
|
|
#define HAVE_FORK 1
|
|
#if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */
|
|
#define HAVE_FORK1 1
|
|
@@ -246,6 +247,10 @@ extern int lstat(const char *, struct stat *);
|
|
|
|
#endif /* !_MSC_VER */
|
|
|
|
+#ifdef HAVE_POSIX_SPAWN
|
|
+#include <spawn.h>
|
|
+#endif
|
|
+
|
|
#ifdef HAVE_UTIME_H
|
|
#include <utime.h>
|
|
#endif /* HAVE_UTIME_H */
|
|
@@ -5126,6 +5131,195 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env)
|
|
|
|
#endif /* HAVE_EXECV */
|
|
|
|
+#ifdef HAVE_POSIX_SPAWN
|
|
+
|
|
+enum posix_spawn_file_actions_identifier {
|
|
+ POSIX_SPAWN_OPEN,
|
|
+ POSIX_SPAWN_CLOSE,
|
|
+ POSIX_SPAWN_DUP2
|
|
+};
|
|
+
|
|
+/*[clinic input]
|
|
+
|
|
+os.posix_spawn
|
|
+ path: path_t
|
|
+ Path of executable file.
|
|
+ argv: object
|
|
+ Tuple or list of strings.
|
|
+ env: object
|
|
+ Dictionary of strings mapping to strings.
|
|
+ file_actions: object = None
|
|
+ FileActions object.
|
|
+ /
|
|
+
|
|
+Execute the program specified by path in a new process.
|
|
+[clinic start generated code]*/
|
|
+
|
|
+static PyObject *
|
|
+os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
|
|
+ PyObject *env, PyObject *file_actions)
|
|
+/*[clinic end generated code: output=d023521f541c709c input=0ec9f1cfdc890be5]*/
|
|
+{
|
|
+ EXECV_CHAR **argvlist = NULL;
|
|
+ EXECV_CHAR **envlist;
|
|
+ Py_ssize_t argc, envc;
|
|
+
|
|
+ /* posix_spawn has three arguments: (path, argv, env), where
|
|
+ argv is a list or tuple of strings and env is a dictionary
|
|
+ like posix.environ. */
|
|
+
|
|
+ if (!PySequence_Check(argv)){
|
|
+ PyErr_SetString(PyExc_TypeError,
|
|
+ "posix_spawn: argv must be a tuple or list");
|
|
+ goto fail;
|
|
+ }
|
|
+ argc = PySequence_Size(argv);
|
|
+ if (argc < 1) {
|
|
+ PyErr_SetString(PyExc_ValueError, "posix_spawn: argv must not be empty");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!PyMapping_Check(env)) {
|
|
+ PyErr_SetString(PyExc_TypeError,
|
|
+ "posix_spawn: environment must be a mapping object");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ argvlist = parse_arglist(argv, &argc);
|
|
+ if (argvlist == NULL) {
|
|
+ goto fail;
|
|
+ }
|
|
+ if (!argvlist[0][0]) {
|
|
+ PyErr_SetString(PyExc_ValueError,
|
|
+ "posix_spawn: argv first element cannot be empty");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ envlist = parse_envlist(env, &envc);
|
|
+ if (envlist == NULL)
|
|
+ goto fail;
|
|
+
|
|
+ pid_t pid;
|
|
+ posix_spawn_file_actions_t *file_actionsp = NULL;
|
|
+ if (file_actions != NULL && file_actions != Py_None){
|
|
+ posix_spawn_file_actions_t _file_actions;
|
|
+ if(posix_spawn_file_actions_init(&_file_actions) != 0){
|
|
+ PyErr_SetString(PyExc_TypeError,
|
|
+ "Error initializing file actions");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+
|
|
+ file_actionsp = &_file_actions;
|
|
+
|
|
+
|
|
+ PyObject* seq = PySequence_Fast(file_actions, "file_actions must be a sequence");
|
|
+ if(seq == NULL){
|
|
+ goto fail;
|
|
+ }
|
|
+ PyObject* file_actions_obj;
|
|
+ PyObject* mode_obj;
|
|
+
|
|
+ for (int i = 0; i < PySequence_Fast_GET_SIZE(seq); ++i) {
|
|
+ file_actions_obj = PySequence_Fast_GET_ITEM(seq, i);
|
|
+
|
|
+ if(!PySequence_Check(file_actions_obj) | !PySequence_Size(file_actions_obj)){
|
|
+ PyErr_SetString(PyExc_TypeError,"Each file_action element must be a non empty sequence");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+
|
|
+ mode_obj = PySequence_Fast_GET_ITEM(file_actions_obj, 0);
|
|
+ int mode = PyLong_AsLong(mode_obj);
|
|
+
|
|
+ /* Populate the file_actions object */
|
|
+
|
|
+ switch(mode) {
|
|
+
|
|
+ case POSIX_SPAWN_OPEN:
|
|
+ if(PySequence_Size(file_actions_obj) != 5){
|
|
+ PyErr_SetString(PyExc_TypeError,"A open file_action object must have 5 elements");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ long open_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1));
|
|
+ if(PyErr_Occurred()) {
|
|
+ goto fail;
|
|
+ }
|
|
+ const char* open_path = PyUnicode_AsUTF8(PySequence_GetItem(file_actions_obj, 2));
|
|
+ if(open_path == NULL){
|
|
+ goto fail;
|
|
+ }
|
|
+ long open_oflag = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 3));
|
|
+ if(PyErr_Occurred()) {
|
|
+ goto fail;
|
|
+ }
|
|
+ long open_mode = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 4));
|
|
+ if(PyErr_Occurred()) {
|
|
+ goto fail;
|
|
+ }
|
|
+ posix_spawn_file_actions_addopen(file_actionsp, open_fd, open_path, open_oflag, open_mode);
|
|
+ break;
|
|
+
|
|
+ case POSIX_SPAWN_CLOSE:
|
|
+ if(PySequence_Size(file_actions_obj) != 2){
|
|
+ PyErr_SetString(PyExc_TypeError,"A close file_action object must have 2 elements");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ long close_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1));
|
|
+ if(PyErr_Occurred()) {
|
|
+ goto fail;
|
|
+ }
|
|
+ posix_spawn_file_actions_addclose(file_actionsp, close_fd);
|
|
+ break;
|
|
+
|
|
+ case POSIX_SPAWN_DUP2:
|
|
+ if(PySequence_Size(file_actions_obj) != 3){
|
|
+ PyErr_SetString(PyExc_TypeError,"A dup2 file_action object must have 3 elements");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ long fd1 = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1));
|
|
+ if(PyErr_Occurred()) {
|
|
+ goto fail;
|
|
+ }
|
|
+ long fd2 = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 2));
|
|
+ if(PyErr_Occurred()) {
|
|
+ goto fail;
|
|
+ }
|
|
+ posix_spawn_file_actions_adddup2(file_actionsp, fd1, fd2);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ PyErr_SetString(PyExc_TypeError,"Unknown file_actions identifier");
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
+ Py_DECREF(seq);
|
|
+}
|
|
+
|
|
+ _Py_BEGIN_SUPPRESS_IPH
|
|
+ posix_spawn(&pid, path->narrow, file_actionsp, NULL, argvlist, envlist);
|
|
+ return PyLong_FromPid(pid);
|
|
+ _Py_END_SUPPRESS_IPH
|
|
+
|
|
+ path_error(path);
|
|
+
|
|
+ free_string_array(envlist, envc);
|
|
+
|
|
+fail:
|
|
+
|
|
+ if (argvlist) {
|
|
+ free_string_array(argvlist, argc);
|
|
+ }
|
|
+ return NULL;
|
|
+
|
|
+
|
|
+}
|
|
+#endif /* HAVE_POSIX_SPAWN */
|
|
+
|
|
+
|
|
#if defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV)
|
|
/*[clinic input]
|
|
os.spawnv
|
|
@@ -12687,6 +12881,7 @@ static PyMethodDef posix_methods[] = {
|
|
OS_NICE_METHODDEF
|
|
OS_GETPRIORITY_METHODDEF
|
|
OS_SETPRIORITY_METHODDEF
|
|
+ OS_POSIX_SPAWN_METHODDEF
|
|
#ifdef HAVE_READLINK
|
|
{"readlink", (PyCFunction)posix_readlink,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
@@ -13241,6 +13436,13 @@ all_ins(PyObject *m)
|
|
if (PyModule_AddIntConstant(m, "RWF_NOWAIT", RWF_NOWAIT)) return -1;
|
|
#endif
|
|
|
|
+/* constants for posix_spawn */
|
|
+#ifdef HAVE_POSIX_SPAWN
|
|
+ if (PyModule_AddIntConstant(m, "POSIX_SPAWN_OPEN", POSIX_SPAWN_OPEN)) return -1;
|
|
+ if (PyModule_AddIntConstant(m, "POSIX_SPAWN_CLOSE", POSIX_SPAWN_CLOSE)) return -1;
|
|
+ if (PyModule_AddIntConstant(m, "POSIX_SPAWN_DUP2", POSIX_SPAWN_DUP2)) return -1;
|
|
+#endif
|
|
+
|
|
#ifdef HAVE_SPAWNV
|
|
if (PyModule_AddIntConstant(m, "P_WAIT", _P_WAIT)) return -1;
|
|
if (PyModule_AddIntConstant(m, "P_NOWAIT", _P_NOWAIT)) return -1;
|
|
--
|
|
2.23.0
|
|
|