Changeset 1281 for python


Ignore:
Timestamp:
Dec 26, 2017, 10:43:44 PM (8 years ago)
Author:
dmik
Message:

python: Implement subprocess.Popen using LIBCx spawn2.

Closes #280.

Location:
python/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • python/trunk/Lib/subprocess.py

    r1254 r1281  
    659659                 preexec_fn=None, close_fds=False, shell=False,
    660660                 cwd=None, env=None, universal_newlines=False,
    661                  startupinfo=None, creationflags=0):
     661                 startupinfo=None, creationflags=0,
     662                 threadsafe=None):
    662663        """Create new Popen instance."""
    663664        _cleanup()
     
    683684                raise ValueError("creationflags is only supported on Windows "
    684685                                 "platforms")
     686        if os2:
     687            if threadsafe is None:
     688                threadsafe = False
     689        else:
     690            if threadsafe is not None:
     691                raise ValueError("threadsafe is only supported on OS/2 "
     692                                 "platforms")
    685693
    686694        self.stdin = None
     
    713721            self._execute_child(args, executable, preexec_fn, close_fds,
    714722                                cwd, env, universal_newlines,
    715                                 startupinfo, creationflags, shell, to_close,
     723                                startupinfo, creationflags, threadsafe, shell, to_close,
    716724                                p2cread, p2cwrite,
    717725                                c2pread, c2pwrite,
     
    912920        def _execute_child(self, args, executable, preexec_fn, close_fds,
    913921                           cwd, env, universal_newlines,
    914                            startupinfo, creationflags, shell, to_close,
     922                           startupinfo, creationflags, threadsafe, shell, to_close,
    915923                           p2cread, p2cwrite,
    916924                           c2pread, c2pwrite,
     
    11951203        def _execute_child(self, args, executable, preexec_fn, close_fds,
    11961204                           cwd, env, universal_newlines,
    1197                            startupinfo, creationflags, shell, to_close,
     1205                           startupinfo, creationflags, threadsafe, shell, to_close,
    11981206                           p2cread, p2cwrite,
    11991207                           c2pread, c2pwrite,
     
    12231231                # in the kernel).
    12241232                mode = os.P_NOWAIT
    1225                 oldcwd = None
    1226                 dups = [ None, None, None ]
    1227                 cloexec_fds = set()
    1228 
    1229                 try:
    1230                     # Change the directory if asked
    1231                     if cwd is not None:
    1232                         oldcwd = os.getcwd()
    1233                         os.chdir(cwd)
    1234 
    1235                     # Duplicate stdio if needed
    1236                     if p2cread is not None:
    1237                         dups[0] = os.dup(0)
    1238                         os.dup2(p2cread, 0)
    1239                         self._set_cloexec_flag(dups[0])
    1240                     if c2pwrite is not None:
    1241                         dups[1] = os.dup(1)
    1242                         os.dup2(c2pwrite, 1)
    1243                         self._set_cloexec_flag(dups[1])
    1244                     if errwrite is not None:
    1245                         dups[2] = os.dup(2)
    1246                         os.dup2(errwrite, 2)
    1247                         self._set_cloexec_flag(dups[2])
    1248 
    1249                     # Disable inheritance
    1250                     if close_fds:
    1251                         for i in xrange(3, MAXFD):
    1252                             try:
    1253                                 f = fcntl.fcntl(i, fcntl.F_GETFD)
    1254                                 if not (f & fcntl.FD_CLOEXEC):
    1255                                     cloexec_fds.add(i)
    1256                                     self._set_cloexec_flag(i)
    1257                             except:
    1258                                 pass
    1259 
    1260                     if env is None:
    1261                         pid = os.spawnvp(mode, executable, args)
    1262                     else:
    1263                         pid = os.spawnvpe(mode, executable, args, env)
    1264                 finally:
    1265                     # Restore inheritance
    1266                     for i in cloexec_fds:
    1267                         self._set_cloexec_flag(i, False)
    1268 
    1269                     # Restore the parent stdio
    1270                     for i, fd in enumerate(dups):
    1271                         if fd is not None:
    1272                             os.dup2(fd, i)
    1273                             os.close(fd)
    1274 
    1275                     # Restore the current directory
    1276                     if oldcwd is not None:
    1277                         os.chdir(oldcwd)
     1233
     1234                if close_fds:
     1235                  mode |= os.P_2_NOINHERIT
     1236                if threadsafe:
     1237                  mode |= os.P_2_THREADSAFE
     1238                stdfds = [ p2cread, c2pwrite, errwrite ]
     1239
     1240                pid = os.spawn2(mode, executable, args, cwd, env, stdfds)
    12781241
    12791242                # Child is launched. Close the parent's copy of those pipe
     
    14801443                        data = _eintr_retry_call(file.read)
    14811444                        if data == "":
    1482                             file.close()
     1445                            try:
     1446                                file.close()
     1447                            except IOError as e:
     1448                                # kLIBC close may fail with EBADF or even Error 0,
     1449                                # ignore for now
     1450                                pass
    14831451                            break
    14841452                        buffer.append(data)
     
    15041472                    if input is not None:
    15051473                        try:
    1506                             self.stdin.write(input)
     1474                            _eintr_retry_call(self.stdin.write, input)
    15071475                        except IOError as e:
    15081476                            if e.errno != errno.EPIPE:
    15091477                                raise
    1510                     self.stdin.close()
     1478                    try:
     1479                        self.stdin.close()
     1480                    except IOError as e:
     1481                        # kLIBC close may fail with EBADF or even Error 0,
     1482                        # ignore for now
     1483                        pass
    15111484
    15121485                if self.stdout:
  • python/trunk/Modules/posixmodule.c

    r1254 r1281  
    3838#if defined(__KLIBC__)
    3939#include <sys/socket.h>
     40#include <libcx/spawn2.h>
    4041#endif
    4142
     
    88048805}
    88058806#endif
     8807
     8808#if defined(PYOS_OS2)
     8809PyDoc_STRVAR(os2_spawn2__doc__,
     8810"spawn2(mode, file, args, cwd, env, stdfds)\n\n\
     8811Execute the program 'name' in a new process,\n\
     8812search path to find the file.\n\
     8813\n\
     8814   mode: mode of process creation\n\
     8815   file: executable file name\n\
     8816   args: tuple or list of strings\n\
     8817   cwd: initial working directory for the new process\n\
     8818   env: dictionary of strings mapping to strings\n\
     8819   stdfds: tuple or list of 3 file descriptors for standard I/O");
     8820
     8821static PyObject *
     8822os2_spawn2(PyObject *self, PyObject *args)
     8823{
     8824    char *path, *cwd = NULL;
     8825    PyObject *argv, *env, *stdfds;
     8826    char **argvlist;
     8827    char **envlist;
     8828    PyObject *key, *val, *keys=NULL, *vals=NULL, *res=NULL;
     8829    int mode, i, pos, argc, envc, stdfdc;
     8830    Py_intptr_t spawnval;
     8831    PyObject *(*argv_getitem)(PyObject *, Py_ssize_t);
     8832    int lastarg = 0;
     8833    PyObject *(*stdfd_getitem)(PyObject *, Py_ssize_t);
     8834    int stdfdlist[3] = {0};
     8835
     8836    /* Note: no need in et and Py_FileSystemDefaultEncoding for path arguments
     8837     * as on OS/2 this is always the same as the default encoding. */
     8838    if (!PyArg_ParseTuple(args, "isOzOO:spawn2", &mode,
     8839                          &path, &argv, &cwd, &env, &stdfds))
     8840        return NULL;
     8841    if (PyList_Check(argv)) {
     8842        argc = PyList_Size(argv);
     8843        argv_getitem = PyList_GetItem;
     8844    }
     8845    else if (PyTuple_Check(argv)) {
     8846        argc = PyTuple_Size(argv);
     8847        argv_getitem = PyTuple_GetItem;
     8848    }
     8849    else {
     8850        PyErr_SetString(PyExc_TypeError,
     8851                        "spawn2() arg 3 must be a tuple or list");
     8852        goto fail_0;
     8853    }
     8854
     8855    if (env != Py_None && !PyMapping_Check(env)) {
     8856        PyErr_SetString(PyExc_TypeError,
     8857                        "spawn2() arg 5 must be a mapping object");
     8858        goto fail_0;
     8859    }
     8860
     8861    if (PyList_Check(stdfds)) {
     8862        stdfdc = PyList_Size(stdfds);
     8863        stdfd_getitem = PyList_GetItem;
     8864    }
     8865    else if (PyTuple_Check(stdfds)) {
     8866        stdfdc = PyTuple_Size(stdfds);
     8867        stdfd_getitem = PyTuple_GetItem;
     8868    }
     8869    else if (stdfds == Py_None) {
     8870        stdfdc = -1;
     8871    }
     8872    else {
     8873        PyErr_SetString(PyExc_TypeError,
     8874                        "spawn2() arg 6 must be a tuple or list or None");
     8875        goto fail_0;
     8876    }
     8877    if (stdfdc != -1 && stdfdc != 3) {
     8878        PyErr_SetString(PyExc_TypeError,
     8879                        "spawn2() arg 6 must contain 3 elements");
     8880        goto fail_0;
     8881    }
     8882
     8883    argvlist = PyMem_NEW(char *, argc+1);
     8884    if (argvlist == NULL) {
     8885        PyErr_NoMemory();
     8886        goto fail_0;
     8887    }
     8888
     8889    for (i = 0; i < argc; i++) {
     8890        if (!PyArg_Parse((*argv_getitem)(argv, i), "et",
     8891                         Py_FileSystemDefaultEncoding, &argvlist[i]))
     8892        {
     8893            PyErr_SetString(PyExc_TypeError,
     8894                            "spawn2() arg 3 must contain only strings");
     8895            lastarg = i;
     8896            goto fail_1;
     8897        }
     8898    }
     8899    lastarg = argc;
     8900    argvlist[argc] = NULL;
     8901
     8902    if (env == Py_None) {
     8903        envlist = NULL;
     8904        envc = -1;
     8905    } else {
     8906        i = PyMapping_Size(env);
     8907        if (i < 0)
     8908            goto fail_1;
     8909        envlist = PyMem_NEW(char *, i + 1);
     8910        if (envlist == NULL) {
     8911            PyErr_NoMemory();
     8912            goto fail_1;
     8913        }
     8914        envc = 0;
     8915        keys = PyMapping_Keys(env);
     8916        vals = PyMapping_Values(env);
     8917        if (!keys || !vals)
     8918            goto fail_2;
     8919        if (!PyList_Check(keys) || !PyList_Check(vals)) {
     8920            PyErr_SetString(PyExc_TypeError,
     8921                            "spawn2(): env.keys() or env.values() is not a list");
     8922            goto fail_2;
     8923        }
     8924
     8925        for (pos = 0; pos < i; pos++) {
     8926            char *p, *k, *v;
     8927            size_t len;
     8928
     8929            key = PyList_GetItem(keys, pos);
     8930            val = PyList_GetItem(vals, pos);
     8931            if (!key || !val)
     8932                goto fail_2;
     8933
     8934            if (!PyArg_Parse(key, "s", &k)) {
     8935                PyErr_SetString(PyExc_TypeError,
     8936                                "spawn2() arg 5 contains a non-string key");
     8937                goto fail_2;
     8938            }
     8939            if (!PyArg_Parse(val, "s", &v)) {
     8940                PyErr_SetString(PyExc_TypeError,
     8941                                "spawn2() arg 5 contains a non-string key");
     8942                goto fail_2;
     8943            }
     8944            len = PyString_Size(key) + PyString_Size(val) + 2;
     8945            p = PyMem_NEW(char, len);
     8946            if (p == NULL) {
     8947                PyErr_NoMemory();
     8948                goto fail_2;
     8949            }
     8950            PyOS_snprintf(p, len, "%s=%s", k, v);
     8951            envlist[envc++] = p;
     8952        }
     8953        envlist[envc] = 0;
     8954    }
     8955
     8956    if (stdfdc != -1) {
     8957        for (i = 0; i < stdfdc; i++) {
     8958            PyObject *elem = (*stdfd_getitem)(stdfds, i);
     8959            if (elem == Py_None) {
     8960                stdfdlist[i] = 0;
     8961            } else {
     8962                if (!PyArg_Parse(elem, "i", &stdfdlist[i])) {
     8963                    PyErr_SetString(PyExc_TypeError,
     8964                                    "spawn2() arg 6 must contain only integers");
     8965                    goto fail_2;
     8966                }
     8967            }
     8968        }
     8969    }
     8970
     8971    Py_BEGIN_ALLOW_THREADS
     8972
     8973    spawnval = spawn2(mode, path, (const char * const *)argvlist, cwd,
     8974                      (const char * const *)envlist,
     8975                      stdfdc == -1 ? NULL : stdfdlist);
     8976
     8977    Py_END_ALLOW_THREADS
     8978
     8979    if (spawnval == -1)
     8980        (void) posix_error();
     8981    else
     8982        res = Py_BuildValue("l", (long) spawnval);
     8983
     8984  fail_2:
     8985    if (envlist) {
     8986        while (--envc >= 0)
     8987            PyMem_DEL(envlist[envc]);
     8988        PyMem_DEL(envlist);
     8989    }
     8990  fail_1:
     8991    free_string_array(argvlist, lastarg);
     8992    Py_XDECREF(vals);
     8993    Py_XDECREF(keys);
     8994  fail_0:
     8995    return res;
     8996}
     8997#endif /* PYOS_OS2 */
    88068998
    88078999static PyMethodDef posix_methods[] = {
     
    91279319#endif
    91289320    {"urandom",         posix_urandom,   METH_VARARGS, posix_urandom__doc__},
     9321#ifdef PYOS_OS2
     9322    {"spawn2",          os2_spawn2, METH_VARARGS, os2_spawn2__doc__},
     9323#endif
    91299324    {NULL,              NULL}            /* Sentinel */
    91309325};
     
    93959590    if (ins(d, "P_UNRELATED", (long)P_UNRELATED)) return -1;
    93969591    if (ins(d, "P_DEBUGDESC", (long)P_DEBUGDESC)) return -1;
     9592    if (ins(d, "P_2_NOINHERIT", (long)P_2_NOINHERIT)) return -1;
     9593    if (ins(d, "P_2_THREADSAFE", (long)P_2_THREADSAFE)) return -1;
     9594    if (ins(d, "P_2_MODE_MASK", (long)P_2_MODE_MASK)) return -1;
    93979595#else
    93989596    if (ins(d, "P_WAIT", (long)_P_WAIT)) return -1;
Note: See TracChangeset for help on using the changeset viewer.