From fddc45e6b0d94be91926795396e9d82ef7e9130d Mon Sep 17 00:00:00 2001 From: nanjekyejoannah Date: Wed, 16 Jan 2019 11:10:53 +0300 Subject: [PATCH 1/3] Expose posix_spawnp --- Doc/library/os.rst | 18 + Lib/test/test_posix.py | 188 +++++---- .../2019-01-14-14-13-08.bpo-35674.kamWqz.rst | 2 + Modules/clinic/posixmodule.c.h | 75 +++- Modules/posixmodule.c | 123 ++++-- aclocal.m4 | 8 +- configure | 2 +- configure.ac | 2 +- patch.patch | 377 ++++++++++++++++++ pyconfig.h.in | 3 + 10 files changed, 692 insertions(+), 106 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-01-14-14-13-08.bpo-35674.kamWqz.rst create mode 100644 patch.patch diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 16250e29774f20..11e373c8cb99ae 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3396,6 +3396,10 @@ written in Python, such as a mail server's external command delivery program. The positional-only arguments *path*, *args*, and *env* are similar to :func:`execve`. + The *path* parameter is the path to the executable file.The *path* should + contain a directory.Use :func:`posix_spawnp` to pass an executable file + without directory. + The *file_actions* argument may be a sequence of tuples describing actions to take on specific file descriptors in the child process between the C library implementation's :c:func:`fork` and :c:func:`exec` steps. @@ -3459,6 +3463,20 @@ written in Python, such as a mail server's external command delivery program. .. versionadded:: 3.7 +.. function:: posix_spawnp(path, argv, env, *, file_actions=None, \ + setpgroup=None, resetids=False, setsigmask=(), \ + setsigdef=(), scheduler=None) + + Wraps the :c:func:`posix_spawnp` C library API for use from Python. + + Similar to :func:`posix_spawn` except that the system searches + for the *executable* file in the list of directories specified by the + :envvar:`PATH` environment variable (in the same way as for ``execvp(3)``) + if the *path* argument contains no directory. + + .. versionadded:: 3.8 + + .. function:: register_at_fork(*, before=None, after_in_parent=None, \ after_in_child=None) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index d7e512c99f03d3..51f215a34f2567 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1489,10 +1489,10 @@ def test_setgroups(self): self.assertListEqual(groups, posix.getgroups()) -@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") -class TestPosixSpawn(unittest.TestCase): +class _PosixSpawnMixin: # Program which does nothing and exit with status 0 (success) NOOP_PROGRAM = (sys.executable, '-I', '-S', '-c', 'pass') + spawn_func = None def python_args(self, *args): # Disable site module to avoid side effects. For example, @@ -1511,7 +1511,7 @@ def test_returns_pid(self): pidfile.write(str(os.getpid())) """ args = self.python_args('-c', script) - pid = posix.posix_spawn(args[0], args, os.environ) + pid = self.spawn_func(args[0], args, os.environ) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) with open(pidfile) as f: self.assertEqual(f.read(), str(pid)) @@ -1519,9 +1519,9 @@ def test_returns_pid(self): def test_no_such_executable(self): no_such_executable = 'no_such_executable' try: - pid = posix.posix_spawn(no_such_executable, - [no_such_executable], - os.environ) + pid = self.spawn_func(no_such_executable, + [no_such_executable], + os.environ) except FileNotFoundError as exc: self.assertEqual(exc.filename, no_such_executable) else: @@ -1538,14 +1538,14 @@ def test_specify_environment(self): envfile.write(os.environ['foo']) """ args = self.python_args('-c', script) - pid = posix.posix_spawn(args[0], args, - {**os.environ, 'foo': 'bar'}) + pid = self.spawn_func(args[0], args, + {**os.environ, 'foo': 'bar'}) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) with open(envfile) as f: self.assertEqual(f.read(), 'bar') def test_empty_file_actions(self): - pid = posix.posix_spawn( + pid = self.spawn_func( self.NOOP_PROGRAM[0], self.NOOP_PROGRAM, os.environ, @@ -1554,7 +1554,7 @@ def test_empty_file_actions(self): self.assertEqual(os.waitpid(pid, 0), (pid, 0)) def test_resetids_explicit_default(self): - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', 'pass'], os.environ, @@ -1563,7 +1563,7 @@ def test_resetids_explicit_default(self): self.assertEqual(os.waitpid(pid, 0), (pid, 0)) def test_resetids(self): - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', 'pass'], os.environ, @@ -1573,12 +1573,12 @@ def test_resetids(self): def test_resetids_wrong_type(self): with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, resetids=None) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, resetids=None) def test_setpgroup(self): - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', 'pass'], os.environ, @@ -1588,9 +1588,9 @@ def test_setpgroup(self): def test_setpgroup_wrong_type(self): with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setpgroup="023") + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setpgroup="023") @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), 'need signal.pthread_sigmask()') @@ -1599,7 +1599,7 @@ def test_setsigmask(self): import signal signal.raise_signal(signal.SIGUSR1)""") - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', code], os.environ, @@ -1609,18 +1609,18 @@ def test_setsigmask(self): def test_setsigmask_wrong_type(self): with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setsigmask=34) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigmask=34) with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setsigmask=["j"]) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigmask=["j"]) with self.assertRaises(ValueError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setsigmask=[signal.NSIG, - signal.NSIG+1]) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigmask=[signal.NSIG, + signal.NSIG+1]) @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), 'need signal.pthread_sigmask()') @@ -1630,7 +1630,7 @@ def test_setsigdef(self): import signal signal.raise_signal(signal.SIGUSR1)""") try: - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', code], os.environ, @@ -1646,17 +1646,17 @@ def test_setsigdef(self): def test_setsigdef_wrong_type(self): with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setsigdef=34) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigdef=34) with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setsigdef=["j"]) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigdef=["j"]) with self.assertRaises(ValueError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) + self.spawn_func(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) @requires_sched @unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')), @@ -1670,7 +1670,7 @@ def test_setscheduler_only_param(self): sys.exit(101) if os.sched_getparam(0).sched_priority != {priority}: sys.exit(102)""") - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', code], os.environ, @@ -1690,7 +1690,7 @@ def test_setscheduler_with_policy(self): sys.exit(101) if os.sched_getparam(0).sched_priority != {priority}: sys.exit(102)""") - pid = posix.posix_spawn( + pid = self.spawn_func( sys.executable, [sys.executable, '-c', code], os.environ, @@ -1704,40 +1704,40 @@ def test_multiple_file_actions(self): (os.POSIX_SPAWN_CLOSE, 0), (os.POSIX_SPAWN_DUP2, 1, 4), ] - pid = posix.posix_spawn(self.NOOP_PROGRAM[0], - self.NOOP_PROGRAM, - os.environ, - file_actions=file_actions) + pid = self.spawn_func(self.NOOP_PROGRAM[0], + self.NOOP_PROGRAM, + os.environ, + file_actions=file_actions) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) def test_bad_file_actions(self): args = self.NOOP_PROGRAM with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[None]) + self.spawn_func(args[0], args, os.environ, + file_actions=[None]) with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[()]) + self.spawn_func(args[0], args, os.environ, + file_actions=[()]) with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[(None,)]) + self.spawn_func(args[0], args, os.environ, + file_actions=[(None,)]) with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[(12345,)]) + self.spawn_func(args[0], args, os.environ, + file_actions=[(12345,)]) with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[(os.POSIX_SPAWN_CLOSE,)]) + self.spawn_func(args[0], args, os.environ, + file_actions=[(os.POSIX_SPAWN_CLOSE,)]) with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)]) + self.spawn_func(args[0], args, os.environ, + file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)]) with self.assertRaises(TypeError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[(os.POSIX_SPAWN_CLOSE, None)]) + self.spawn_func(args[0], args, os.environ, + file_actions=[(os.POSIX_SPAWN_CLOSE, None)]) with self.assertRaises(ValueError): - posix.posix_spawn(args[0], args, os.environ, - file_actions=[(os.POSIX_SPAWN_OPEN, - 3, __file__ + '\0', - os.O_RDONLY, 0)]) + self.spawn_func(args[0], args, os.environ, + file_actions=[(os.POSIX_SPAWN_OPEN, + 3, __file__ + '\0', + os.O_RDONLY, 0)]) def test_open_file(self): outfile = support.TESTFN @@ -1752,8 +1752,8 @@ def test_open_file(self): stat.S_IRUSR | stat.S_IWUSR), ] args = self.python_args('-c', script) - pid = posix.posix_spawn(args[0], args, os.environ, - file_actions=file_actions) + pid = self.spawn_func(args[0], args, os.environ, + file_actions=file_actions) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) with open(outfile) as f: self.assertEqual(f.read(), 'hello') @@ -1770,8 +1770,8 @@ def test_close_file(self): closefile.write('is closed %d' % e.errno) """ args = self.python_args('-c', script) - pid = posix.posix_spawn(args[0], args, os.environ, - file_actions=[(os.POSIX_SPAWN_CLOSE, 0),]) + pid = self.spawn_func(args[0], args, os.environ, + file_actions=[(os.POSIX_SPAWN_CLOSE, 0)]) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) with open(closefile) as f: self.assertEqual(f.read(), 'is closed %d' % errno.EBADF) @@ -1788,16 +1788,64 @@ def test_dup2(self): (os.POSIX_SPAWN_DUP2, childfile.fileno(), 1), ] args = self.python_args('-c', script) - pid = posix.posix_spawn(args[0], args, os.environ, - file_actions=file_actions) + pid = self.spawn_func(args[0], args, os.environ, + file_actions=file_actions) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) with open(dupfile) as f: self.assertEqual(f.read(), 'hello') +@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") +class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): + spawn_func = getattr(posix, 'posix_spawn', None) + + +@unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") +class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): + spawn_func = getattr(posix, 'posix_spawnp', None) + + @support.skip_unless_symlink + def test_posix_spawnp(self): + # Use a symlink to create a program in its own temporary directory + temp_dir = tempfile.mkdtemp() + self.addCleanup(support.rmtree, temp_dir) + + program = 'posix_spawnp_test_program.exe' + program_fullpath = os.path.join(temp_dir, program) + os.symlink(sys.executable, program_fullpath) + + try: + path = os.pathsep.join((temp_dir, os.environ['PATH'])) + except KeyError: + path = temp_dir # PATH is not set + + spawn_args = (program, '-I', '-S', '-c', 'pass') + code = textwrap.dedent(""" + import os + args = %a + pid = os.posix_spawnp(args[0], args, os.environ) + pid2, status = os.waitpid(pid, 0) + if pid2 != pid: + raise Exception(f"pid {pid2} != {pid}") + if status != 0: + raise Exception(f"status {status} != 0") + """ % (spawn_args,)) + + # Use a subprocess to test os.posix_spawnp() with a modified PATH + # environment variable: posix_spawnp() uses the current environment + # to locate the program, not its environment argument. + args = ('-c', code) + #assert_python_ok(*args, PATH=path) + + def test_main(): try: - support.run_unittest(PosixTester, PosixGroupsTester, TestPosixSpawn) + support.run_unittest( + PosixTester, + PosixGroupsTester, + TestPosixSpawn, + TestPosixSpawnP, + ) finally: support.reap_children() diff --git a/Misc/NEWS.d/next/Library/2019-01-14-14-13-08.bpo-35674.kamWqz.rst b/Misc/NEWS.d/next/Library/2019-01-14-14-13-08.bpo-35674.kamWqz.rst new file mode 100644 index 00000000000000..02d170ecac6e45 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-01-14-14-13-08.bpo-35674.kamWqz.rst @@ -0,0 +1,2 @@ +Add a new :func:`os.posix_spawnp` function. +Patch by Joannah Nanjekye. \ No newline at end of file diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 2c1ee97faf711d..ce17709c38ba2c 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1791,6 +1791,75 @@ os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #endif /* defined(HAVE_POSIX_SPAWN) */ +#if defined(HAVE_POSIX_SPAWNP) + +PyDoc_STRVAR(os_posix_spawnp__doc__, +"posix_spawnp($module, path, argv, env, /, *, file_actions=(),\n" +" setpgroup=None, resetids=False, setsigmask=(),\n" +" setsigdef=(), scheduler=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" +" A sequence of file action tuples.\n" +" setpgroup\n" +" The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\n" +" resetids\n" +" If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.\n" +" setsigmask\n" +" The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n" +" setsigdef\n" +" The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.\n" +" scheduler\n" +" A tuple with the scheduler policy (optional) and parameters."); + +#define OS_POSIX_SPAWNP_METHODDEF \ + {"posix_spawnp", (PyCFunction)(void(*)(void))os_posix_spawnp, METH_FASTCALL|METH_KEYWORDS, os_posix_spawnp__doc__}, + +static PyObject * +os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, int resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler); + +static PyObject * +os_posix_spawnp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL}; + static _PyArg_Parser _parser = {"O&OO|$OOiOOO:posix_spawnp", _keywords, 0}; + path_t path = PATH_T_INITIALIZE("posix_spawnp", "path", 0, 0); + PyObject *argv; + PyObject *env; + PyObject *file_actions = NULL; + PyObject *setpgroup = NULL; + int resetids = 0; + PyObject *setsigmask = NULL; + PyObject *setsigdef = NULL; + PyObject *scheduler = NULL; + + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + path_converter, &path, &argv, &env, &file_actions, &setpgroup, &resetids, &setsigmask, &setsigdef, &scheduler)) { + goto exit; + } + return_value = os_posix_spawnp_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsigmask, setsigdef, scheduler); + +exit: + /* Cleanup for path */ + path_cleanup(&path); + + return return_value; +} + +#endif /* defined(HAVE_POSIX_SPAWNP) */ + #if (defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV)) PyDoc_STRVAR(os_spawnv__doc__, @@ -6851,6 +6920,10 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #define OS_POSIX_SPAWN_METHODDEF #endif /* !defined(OS_POSIX_SPAWN_METHODDEF) */ +#ifndef OS_POSIX_SPAWNP_METHODDEF + #define OS_POSIX_SPAWNP_METHODDEF +#endif /* !defined(OS_POSIX_SPAWNP_METHODDEF) */ + #ifndef OS_SPAWNV_METHODDEF #define OS_SPAWNV_METHODDEF #endif /* !defined(OS_SPAWNV_METHODDEF) */ @@ -7258,4 +7331,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ -/*[clinic end generated code: output=febc1e16c9024e40 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=dabd0fa27bf87044 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e5c2a9cfc1eccb..3f138512b55463 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5381,35 +5381,9 @@ parse_file_actions(PyObject *file_actions, return -1; } -/*[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(c_default='NULL') = () - A sequence of file action tuples. - setpgroup: object = NULL - The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. - resetids: bool(accept={int}) = False - If the value is `True` the POSIX_SPAWN_RESETIDS will be activated. - setsigmask: object(c_default='NULL') = () - The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. - setsigdef: object(c_default='NULL') = () - The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. - scheduler: object = NULL - A tuple with the scheduler policy (optional) and parameters. - -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, +py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv, PyObject *env, PyObject *file_actions, PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setsigdef, PyObject *scheduler) @@ -5489,8 +5463,15 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, attrp = &attr; _Py_BEGIN_SUPPRESS_IPH - err_code = posix_spawn(&pid, path->narrow, + if (use_posix_spawnp) { + err_code = posix_spawnp(&pid, path->narrow, + file_actionsp, attrp, argvlist, envlist); + } + else { + err_code = posix_spawn(&pid, path->narrow, file_actionsp, attrp, argvlist, envlist); + } + _Py_END_SUPPRESS_IPH if (err_code) { errno = err_code; @@ -5518,7 +5499,90 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, Py_XDECREF(temp_buffer); return result; } -#endif /* HAVE_POSIX_SPAWN */ + + +/*[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(c_default='NULL') = () + A sequence of file action tuples. + setpgroup: object = NULL + The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. + resetids: bool(accept={int}) = False + If the value is `True` the POSIX_SPAWN_RESETIDS will be activated. + setsigmask: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. + setsigdef: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. + scheduler: object = NULL + A tuple with the scheduler policy (optional) and parameters. + +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, + PyObject *setpgroup, int resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler) +/*[clinic end generated code: output=45dfa4c515d09f2c input=2891c2f1d457e39b]*/ +{ + return py_posix_spawn(0, module, path, argv, env, file_actions, + setpgroup, resetids, setsigmask, setsigdef, + scheduler); +} + #endif /* HAVE_POSIX_SPAWN */ + + + +#ifdef HAVE_POSIX_SPAWNP +/*[clinic input] + +os.posix_spawnp + 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(c_default='NULL') = () + A sequence of file action tuples. + setpgroup: object = NULL + The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. + resetids: bool(accept={int}) = False + If the value is `True` the POSIX_SPAWN_RESETIDS will be activated. + setsigmask: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. + setsigdef: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. + scheduler: object = NULL + A tuple with the scheduler policy (optional) and parameters. + +Execute the program specified by path in a new process. +[clinic start generated code]*/ + +static PyObject * +os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, int resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler) +/*[clinic end generated code: output=7955dc0edc82b8c3 input=b7576eb25b1ed9eb]*/ +{ + return py_posix_spawn(1, module, path, argv, env, file_actions, + setpgroup, resetids, setsigmask, setsigdef, + scheduler); +} +#endif /* HAVE_POSIX_SPAWNP */ #if defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV) @@ -13084,6 +13148,7 @@ static PyMethodDef posix_methods[] = { OS_GETPRIORITY_METHODDEF OS_SETPRIORITY_METHODDEF OS_POSIX_SPAWN_METHODDEF + OS_POSIX_SPAWNP_METHODDEF OS_READLINK_METHODDEF OS_RENAME_METHODDEF OS_REPLACE_METHODDEF diff --git a/aclocal.m4 b/aclocal.m4 index 94a2dd6d3ebd31..f98db73656d305 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15.1 -*- Autoconf -*- +# generated automatically by aclocal 1.15 -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -13,7 +13,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -dnl serial 11 (pkg-config-0.29) +dnl serial 11 (pkg-config-0.29.1) dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson @@ -55,7 +55,7 @@ dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], -[m4_define([PKG_MACROS_VERSION], [0.29]) +[m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ diff --git a/configure b/configure index 13677b9d96b687..b32481dca03fff 100755 --- a/configure +++ b/configure @@ -11447,7 +11447,7 @@ for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ initgroups kill killpg lchown lockf linkat lstat lutimes mmap \ memrchr mbrtowc mkdirat mkfifo \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \ - posix_fallocate posix_fadvise posix_spawn pread preadv preadv2 \ + posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \ pthread_init pthread_kill putenv pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ setgid sethostname \ diff --git a/configure.ac b/configure.ac index bd09a9c9e1e094..262c72668a956e 100644 --- a/configure.ac +++ b/configure.ac @@ -3505,7 +3505,7 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ initgroups kill killpg lchown lockf linkat lstat lutimes mmap \ memrchr mbrtowc mkdirat mkfifo \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \ - posix_fallocate posix_fadvise posix_spawn pread preadv preadv2 \ + posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \ pthread_init pthread_kill putenv pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ setgid sethostname \ diff --git a/patch.patch b/patch.patch new file mode 100644 index 00000000000000..ac2cfa824231a2 --- /dev/null +++ b/patch.patch @@ -0,0 +1,377 @@ +diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py +index c68ae6acd4..0be79c60d7 100644 +--- a/Lib/test/test_posix.py ++++ b/Lib/test/test_posix.py +@@ -1489,9 +1489,10 @@ class PosixGroupsTester(unittest.TestCase): + self.assertListEqual(groups, posix.getgroups()) + + +-class _PosixSpawnMixin(): ++class _PosixSpawnMixin: + # Program which does nothing and exit with status 0 (success) + NOOP_PROGRAM = (sys.executable, '-I', '-S', '-c', 'pass') ++ spawn_func = None + + def python_args(self, *args): + # Disable site module to avoid side effects. For example, +@@ -1510,7 +1511,7 @@ class _PosixSpawnMixin(): + pidfile.write(str(os.getpid())) + """ + args = self.python_args('-c', script) +- pid = spawn_func(args[0], args, os.environ) ++ pid = self.spawn_func(args[0], args, os.environ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + with open(pidfile) as f: + self.assertEqual(f.read(), str(pid)) +@@ -1518,9 +1519,9 @@ class _PosixSpawnMixin(): + def test_no_such_executable(self): + no_such_executable = 'no_such_executable' + try: +- pid = spawn_func(no_such_executable, +- [no_such_executable], +- os.environ) ++ pid = self.spawn_func(no_such_executable, ++ [no_such_executable], ++ os.environ) + except FileNotFoundError as exc: + self.assertEqual(exc.filename, no_such_executable) + else: +@@ -1537,14 +1538,14 @@ class _PosixSpawnMixin(): + envfile.write(os.environ['foo']) + """ + args = self.python_args('-c', script) +- pid = spawn_func(args[0], args, +- {**os.environ, 'foo': 'bar'}) ++ pid = self.spawn_func(args[0], args, ++ {**os.environ, 'foo': 'bar'}) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + with open(envfile) as f: + self.assertEqual(f.read(), 'bar') + + def test_empty_file_actions(self): +- pid = spawn_func( ++ pid = self.spawn_func( + self.NOOP_PROGRAM[0], + self.NOOP_PROGRAM, + os.environ, +@@ -1553,7 +1554,7 @@ class _PosixSpawnMixin(): + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_resetids_explicit_default(self): +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', 'pass'], + os.environ, +@@ -1562,7 +1563,7 @@ class _PosixSpawnMixin(): + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_resetids(self): +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', 'pass'], + os.environ, +@@ -1572,12 +1573,12 @@ class _PosixSpawnMixin(): + + def test_resetids_wrong_type(self): + with self.assertRaises(TypeError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, resetids=None) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, resetids=None) + + def test_setpgroup(self): +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', 'pass'], + os.environ, +@@ -1587,9 +1588,9 @@ class _PosixSpawnMixin(): + + def test_setpgroup_wrong_type(self): + with self.assertRaises(TypeError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setpgroup="023") ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setpgroup="023") + + @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), + 'need signal.pthread_sigmask()') +@@ -1598,7 +1599,7 @@ class _PosixSpawnMixin(): + import signal + signal.raise_signal(signal.SIGUSR1)""") + +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', code], + os.environ, +@@ -1608,18 +1609,18 @@ class _PosixSpawnMixin(): + + def test_setsigmask_wrong_type(self): + with self.assertRaises(TypeError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setsigmask=34) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setsigmask=34) + with self.assertRaises(TypeError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setsigmask=["j"]) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setsigmask=["j"]) + with self.assertRaises(ValueError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setsigmask=[signal.NSIG, +- signal.NSIG+1]) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setsigmask=[signal.NSIG, ++ signal.NSIG+1]) + + @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), + 'need signal.pthread_sigmask()') +@@ -1629,7 +1630,7 @@ class _PosixSpawnMixin(): + import signal + signal.raise_signal(signal.SIGUSR1)""") + try: +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', code], + os.environ, +@@ -1645,17 +1646,17 @@ class _PosixSpawnMixin(): + + def test_setsigdef_wrong_type(self): + with self.assertRaises(TypeError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setsigdef=34) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setsigdef=34) + with self.assertRaises(TypeError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setsigdef=["j"]) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setsigdef=["j"]) + with self.assertRaises(ValueError): +- spawn_func(sys.executable, +- [sys.executable, "-c", "pass"], +- os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) ++ self.spawn_func(sys.executable, ++ [sys.executable, "-c", "pass"], ++ os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) + + @requires_sched + @unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')), +@@ -1669,7 +1670,7 @@ class _PosixSpawnMixin(): + sys.exit(101) + if os.sched_getparam(0).sched_priority != {priority}: + sys.exit(102)""") +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', code], + os.environ, +@@ -1689,7 +1690,7 @@ class _PosixSpawnMixin(): + sys.exit(101) + if os.sched_getparam(0).sched_priority != {priority}: + sys.exit(102)""") +- pid = spawn_func( ++ pid = self.spawn_func( + sys.executable, + [sys.executable, '-c', code], + os.environ, +@@ -1703,40 +1704,40 @@ class _PosixSpawnMixin(): + (os.POSIX_SPAWN_CLOSE, 0), + (os.POSIX_SPAWN_DUP2, 1, 4), + ] +- pid = spawn_func(self.NOOP_PROGRAM[0], +- self.NOOP_PROGRAM, +- os.environ, +- file_actions=file_actions) ++ pid = self.spawn_func(self.NOOP_PROGRAM[0], ++ self.NOOP_PROGRAM, ++ os.environ, ++ file_actions=file_actions) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_bad_file_actions(self): + args = self.NOOP_PROGRAM + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[None]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[None]) + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[()]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[()]) + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[(None,)]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[(None,)]) + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[(12345,)]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[(12345,)]) + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[(os.POSIX_SPAWN_CLOSE,)]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[(os.POSIX_SPAWN_CLOSE,)]) + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)]) + with self.assertRaises(TypeError): +- spawn_func(args[0], args, os.environ, +- file_actions=[(os.POSIX_SPAWN_CLOSE, None)]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[(os.POSIX_SPAWN_CLOSE, None)]) + with self.assertRaises(ValueError): +- spawn_func(args[0], args, os.environ, +- file_actions=[(os.POSIX_SPAWN_OPEN, +- 3, __file__ + '\0', +- os.O_RDONLY, 0)]) ++ self.spawn_func(args[0], args, os.environ, ++ file_actions=[(os.POSIX_SPAWN_OPEN, ++ 3, __file__ + '\0', ++ os.O_RDONLY, 0)]) + + def test_open_file(self): + outfile = support.TESTFN +@@ -1751,8 +1752,8 @@ class _PosixSpawnMixin(): + stat.S_IRUSR | stat.S_IWUSR), + ] + args = self.python_args('-c', script) +- pid = spawn_func(args[0], args, os.environ, +- file_actions=file_actions) ++ pid = self.spawn_func(args[0], args, os.environ, ++ file_actions=file_actions) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + with open(outfile) as f: + self.assertEqual(f.read(), 'hello') +@@ -1769,8 +1770,8 @@ class _PosixSpawnMixin(): + closefile.write('is closed %d' % e.errno) + """ + args = self.python_args('-c', script) +- pid = spawn_func(args[0], args, os.environ, +- file_actions=[(os.POSIX_SPAWN_CLOSE, 0),]) ++ pid = self.spawn_func(args[0], args, os.environ, ++ file_actions=[(os.POSIX_SPAWN_CLOSE, 0)]) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + with open(closefile) as f: + self.assertEqual(f.read(), 'is closed %d' % errno.EBADF) +@@ -1787,12 +1788,22 @@ class _PosixSpawnMixin(): + (os.POSIX_SPAWN_DUP2, childfile.fileno(), 1), + ] + args = self.python_args('-c', script) +- pid = spawn_func(args[0], args, os.environ, +- file_actions=file_actions) ++ pid = self.spawn_func(args[0], args, os.environ, ++ file_actions=file_actions) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + with open(dupfile) as f: + self.assertEqual(f.read(), 'hello') + ++ ++@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") ++class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): ++ spawn_func = getattr(posix, 'posix_spawn', None) ++ ++ ++@unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") ++class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): ++ spawn_func = getattr(posix, 'posix_spawnp', None) ++ + @support.skip_unless_symlink + def test_posix_spawnp(self): + # Use a symlink to create a program in its own temporary directory +@@ -1812,14 +1823,13 @@ class _PosixSpawnMixin(): + code = textwrap.dedent(""" + import os + args = %a +- env = %a +- pid = os.posix_spawnp(args[0], args, env) ++ pid = os.posix_spawnp(args[0], args, os.environ) + pid2, status = os.waitpid(pid, 0) + if pid2 != pid: + raise Exception(f"pid {pid2} != {pid}") +- if f"status {status} != 0": +- raise Exception(f"status = {status}") +- """ % (spawn_args, env)) ++ if status != 0: ++ raise Exception(f"status {status} != 0") ++ """ % (spawn_args,)) + + # Use a subprocess to test os.posix_spawnp() with a modified PATH + # environment variable: posix_spawnp() uses the current environment +@@ -1828,23 +1838,14 @@ class _PosixSpawnMixin(): + assert_python_ok(*args, PATH=path) + + +-@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") +-class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): +- posix_func = getattr(posix, 'posix_spawn', None) +- if posix_func is not None: +- spawn_func = posix.posix_spawn +- +- +-@unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") +-class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): +- posix_func = getattr(posix, 'posix_spawnp', None) +- if posix_func is not None: +- spawn_func = posix.posix_spawnp +- +- + def test_main(): + try: +- support.run_unittest(PosixTester, PosixGroupsTester, TestPosixSpawn, TestPosixSpawn) ++ support.run_unittest( ++ PosixTester, ++ PosixGroupsTester, ++ TestPosixSpawn, ++ TestPosixSpawnP, ++ ) + finally: + support.reap_children() + +diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c +index a50d69114a..7bd1fa7cf6 100644 +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -5462,14 +5462,14 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a + + _Py_BEGIN_SUPPRESS_IPH + if (use_posix_spawnp) { +- err_code = posix_spawn(&pid, path->narrow, ++ err_code = posix_spawnp(&pid, path->narrow, + file_actionsp, attrp, argvlist, envlist); + } + else { +- err_code = posix_spawnp(&pid, path->narrow, ++ err_code = posix_spawn(&pid, path->narrow, + file_actionsp, attrp, argvlist, envlist); + } +- ++ + _Py_END_SUPPRESS_IPH + if (err_code) { + errno = err_code; diff --git a/pyconfig.h.in b/pyconfig.h.in index f37ca3615025be..a2a56230fc13be 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -732,6 +732,9 @@ /* Define to 1 if you have the `posix_spawn' function. */ #undef HAVE_POSIX_SPAWN +/* Define to 1 if you have the `posix_spawnp' function. */ +#undef HAVE_POSIX_SPAWNP + /* Define to 1 if you have the `pread' function. */ #undef HAVE_PREAD From 37902ca3f57fa95a441eb616fe72d87c7f250f3d Mon Sep 17 00:00:00 2001 From: nanjekyejoannah Date: Wed, 16 Jan 2019 15:18:47 +0300 Subject: [PATCH 2/3] deleted unwanted patch file and uncomment test --- Lib/test/test_posix.py | 4 +- patch.patch | 377 ----------------------------------------- 2 files changed, 2 insertions(+), 379 deletions(-) delete mode 100644 patch.patch diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 51f215a34f2567..79fc3c26441867 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1490,7 +1490,7 @@ def test_setgroups(self): class _PosixSpawnMixin: - # Program which does nothing and exit with status 0 (success) + # Program which does nothing and exits with status 0 (success) NOOP_PROGRAM = (sys.executable, '-I', '-S', '-c', 'pass') spawn_func = None @@ -1835,7 +1835,7 @@ def test_posix_spawnp(self): # environment variable: posix_spawnp() uses the current environment # to locate the program, not its environment argument. args = ('-c', code) - #assert_python_ok(*args, PATH=path) + assert_python_ok(*args, PATH=path) def test_main(): diff --git a/patch.patch b/patch.patch deleted file mode 100644 index ac2cfa824231a2..00000000000000 --- a/patch.patch +++ /dev/null @@ -1,377 +0,0 @@ -diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index c68ae6acd4..0be79c60d7 100644 ---- a/Lib/test/test_posix.py -+++ b/Lib/test/test_posix.py -@@ -1489,9 +1489,10 @@ class PosixGroupsTester(unittest.TestCase): - self.assertListEqual(groups, posix.getgroups()) - - --class _PosixSpawnMixin(): -+class _PosixSpawnMixin: - # Program which does nothing and exit with status 0 (success) - NOOP_PROGRAM = (sys.executable, '-I', '-S', '-c', 'pass') -+ spawn_func = None - - def python_args(self, *args): - # Disable site module to avoid side effects. For example, -@@ -1510,7 +1511,7 @@ class _PosixSpawnMixin(): - pidfile.write(str(os.getpid())) - """ - args = self.python_args('-c', script) -- pid = spawn_func(args[0], args, os.environ) -+ pid = self.spawn_func(args[0], args, os.environ) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - with open(pidfile) as f: - self.assertEqual(f.read(), str(pid)) -@@ -1518,9 +1519,9 @@ class _PosixSpawnMixin(): - def test_no_such_executable(self): - no_such_executable = 'no_such_executable' - try: -- pid = spawn_func(no_such_executable, -- [no_such_executable], -- os.environ) -+ pid = self.spawn_func(no_such_executable, -+ [no_such_executable], -+ os.environ) - except FileNotFoundError as exc: - self.assertEqual(exc.filename, no_such_executable) - else: -@@ -1537,14 +1538,14 @@ class _PosixSpawnMixin(): - envfile.write(os.environ['foo']) - """ - args = self.python_args('-c', script) -- pid = spawn_func(args[0], args, -- {**os.environ, 'foo': 'bar'}) -+ pid = self.spawn_func(args[0], args, -+ {**os.environ, 'foo': 'bar'}) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - with open(envfile) as f: - self.assertEqual(f.read(), 'bar') - - def test_empty_file_actions(self): -- pid = spawn_func( -+ pid = self.spawn_func( - self.NOOP_PROGRAM[0], - self.NOOP_PROGRAM, - os.environ, -@@ -1553,7 +1554,7 @@ class _PosixSpawnMixin(): - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - - def test_resetids_explicit_default(self): -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', 'pass'], - os.environ, -@@ -1562,7 +1563,7 @@ class _PosixSpawnMixin(): - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - - def test_resetids(self): -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', 'pass'], - os.environ, -@@ -1572,12 +1573,12 @@ class _PosixSpawnMixin(): - - def test_resetids_wrong_type(self): - with self.assertRaises(TypeError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, resetids=None) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, resetids=None) - - def test_setpgroup(self): -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', 'pass'], - os.environ, -@@ -1587,9 +1588,9 @@ class _PosixSpawnMixin(): - - def test_setpgroup_wrong_type(self): - with self.assertRaises(TypeError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setpgroup="023") -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setpgroup="023") - - @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), - 'need signal.pthread_sigmask()') -@@ -1598,7 +1599,7 @@ class _PosixSpawnMixin(): - import signal - signal.raise_signal(signal.SIGUSR1)""") - -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', code], - os.environ, -@@ -1608,18 +1609,18 @@ class _PosixSpawnMixin(): - - def test_setsigmask_wrong_type(self): - with self.assertRaises(TypeError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setsigmask=34) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setsigmask=34) - with self.assertRaises(TypeError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setsigmask=["j"]) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setsigmask=["j"]) - with self.assertRaises(ValueError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setsigmask=[signal.NSIG, -- signal.NSIG+1]) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setsigmask=[signal.NSIG, -+ signal.NSIG+1]) - - @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), - 'need signal.pthread_sigmask()') -@@ -1629,7 +1630,7 @@ class _PosixSpawnMixin(): - import signal - signal.raise_signal(signal.SIGUSR1)""") - try: -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', code], - os.environ, -@@ -1645,17 +1646,17 @@ class _PosixSpawnMixin(): - - def test_setsigdef_wrong_type(self): - with self.assertRaises(TypeError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setsigdef=34) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setsigdef=34) - with self.assertRaises(TypeError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setsigdef=["j"]) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setsigdef=["j"]) - with self.assertRaises(ValueError): -- spawn_func(sys.executable, -- [sys.executable, "-c", "pass"], -- os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) -+ self.spawn_func(sys.executable, -+ [sys.executable, "-c", "pass"], -+ os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) - - @requires_sched - @unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')), -@@ -1669,7 +1670,7 @@ class _PosixSpawnMixin(): - sys.exit(101) - if os.sched_getparam(0).sched_priority != {priority}: - sys.exit(102)""") -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', code], - os.environ, -@@ -1689,7 +1690,7 @@ class _PosixSpawnMixin(): - sys.exit(101) - if os.sched_getparam(0).sched_priority != {priority}: - sys.exit(102)""") -- pid = spawn_func( -+ pid = self.spawn_func( - sys.executable, - [sys.executable, '-c', code], - os.environ, -@@ -1703,40 +1704,40 @@ class _PosixSpawnMixin(): - (os.POSIX_SPAWN_CLOSE, 0), - (os.POSIX_SPAWN_DUP2, 1, 4), - ] -- pid = spawn_func(self.NOOP_PROGRAM[0], -- self.NOOP_PROGRAM, -- os.environ, -- file_actions=file_actions) -+ pid = self.spawn_func(self.NOOP_PROGRAM[0], -+ self.NOOP_PROGRAM, -+ os.environ, -+ file_actions=file_actions) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - - def test_bad_file_actions(self): - args = self.NOOP_PROGRAM - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[None]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[None]) - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[()]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[()]) - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[(None,)]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[(None,)]) - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[(12345,)]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[(12345,)]) - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[(os.POSIX_SPAWN_CLOSE,)]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[(os.POSIX_SPAWN_CLOSE,)]) - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)]) - with self.assertRaises(TypeError): -- spawn_func(args[0], args, os.environ, -- file_actions=[(os.POSIX_SPAWN_CLOSE, None)]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[(os.POSIX_SPAWN_CLOSE, None)]) - with self.assertRaises(ValueError): -- spawn_func(args[0], args, os.environ, -- file_actions=[(os.POSIX_SPAWN_OPEN, -- 3, __file__ + '\0', -- os.O_RDONLY, 0)]) -+ self.spawn_func(args[0], args, os.environ, -+ file_actions=[(os.POSIX_SPAWN_OPEN, -+ 3, __file__ + '\0', -+ os.O_RDONLY, 0)]) - - def test_open_file(self): - outfile = support.TESTFN -@@ -1751,8 +1752,8 @@ class _PosixSpawnMixin(): - stat.S_IRUSR | stat.S_IWUSR), - ] - args = self.python_args('-c', script) -- pid = spawn_func(args[0], args, os.environ, -- file_actions=file_actions) -+ pid = self.spawn_func(args[0], args, os.environ, -+ file_actions=file_actions) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - with open(outfile) as f: - self.assertEqual(f.read(), 'hello') -@@ -1769,8 +1770,8 @@ class _PosixSpawnMixin(): - closefile.write('is closed %d' % e.errno) - """ - args = self.python_args('-c', script) -- pid = spawn_func(args[0], args, os.environ, -- file_actions=[(os.POSIX_SPAWN_CLOSE, 0),]) -+ pid = self.spawn_func(args[0], args, os.environ, -+ file_actions=[(os.POSIX_SPAWN_CLOSE, 0)]) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - with open(closefile) as f: - self.assertEqual(f.read(), 'is closed %d' % errno.EBADF) -@@ -1787,12 +1788,22 @@ class _PosixSpawnMixin(): - (os.POSIX_SPAWN_DUP2, childfile.fileno(), 1), - ] - args = self.python_args('-c', script) -- pid = spawn_func(args[0], args, os.environ, -- file_actions=file_actions) -+ pid = self.spawn_func(args[0], args, os.environ, -+ file_actions=file_actions) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - with open(dupfile) as f: - self.assertEqual(f.read(), 'hello') - -+ -+@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") -+class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): -+ spawn_func = getattr(posix, 'posix_spawn', None) -+ -+ -+@unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") -+class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): -+ spawn_func = getattr(posix, 'posix_spawnp', None) -+ - @support.skip_unless_symlink - def test_posix_spawnp(self): - # Use a symlink to create a program in its own temporary directory -@@ -1812,14 +1823,13 @@ class _PosixSpawnMixin(): - code = textwrap.dedent(""" - import os - args = %a -- env = %a -- pid = os.posix_spawnp(args[0], args, env) -+ pid = os.posix_spawnp(args[0], args, os.environ) - pid2, status = os.waitpid(pid, 0) - if pid2 != pid: - raise Exception(f"pid {pid2} != {pid}") -- if f"status {status} != 0": -- raise Exception(f"status = {status}") -- """ % (spawn_args, env)) -+ if status != 0: -+ raise Exception(f"status {status} != 0") -+ """ % (spawn_args,)) - - # Use a subprocess to test os.posix_spawnp() with a modified PATH - # environment variable: posix_spawnp() uses the current environment -@@ -1828,23 +1838,14 @@ class _PosixSpawnMixin(): - assert_python_ok(*args, PATH=path) - - --@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") --class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): -- posix_func = getattr(posix, 'posix_spawn', None) -- if posix_func is not None: -- spawn_func = posix.posix_spawn -- -- --@unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") --class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): -- posix_func = getattr(posix, 'posix_spawnp', None) -- if posix_func is not None: -- spawn_func = posix.posix_spawnp -- -- - def test_main(): - try: -- support.run_unittest(PosixTester, PosixGroupsTester, TestPosixSpawn, TestPosixSpawn) -+ support.run_unittest( -+ PosixTester, -+ PosixGroupsTester, -+ TestPosixSpawn, -+ TestPosixSpawnP, -+ ) - finally: - support.reap_children() - -diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index a50d69114a..7bd1fa7cf6 100644 ---- a/Modules/posixmodule.c -+++ b/Modules/posixmodule.c -@@ -5462,14 +5462,14 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a - - _Py_BEGIN_SUPPRESS_IPH - if (use_posix_spawnp) { -- err_code = posix_spawn(&pid, path->narrow, -+ err_code = posix_spawnp(&pid, path->narrow, - file_actionsp, attrp, argvlist, envlist); - } - else { -- err_code = posix_spawnp(&pid, path->narrow, -+ err_code = posix_spawn(&pid, path->narrow, - file_actionsp, attrp, argvlist, envlist); - } -- -+ - _Py_END_SUPPRESS_IPH - if (err_code) { - errno = err_code; From 1ad7ad826eca3b4679b50a1c675fd1f00edafa66 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 16 Jan 2019 14:08:46 +0100 Subject: [PATCH 3/3] Fix py_posix_spawn() if HAVE_POSIX_SPAWNP is not defined * Fix also indentation in posixmodule.c. * Remove "if the *path* argument contains no directory" from os.posix_spawnp() doc * Remove outdated comment from py_posix_spawn(): /*[clinic end generated code: ...]*/ --- Doc/library/os.rst | 3 +-- Modules/posixmodule.c | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 11e373c8cb99ae..2aa51f875d9e65 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3471,8 +3471,7 @@ written in Python, such as a mail server's external command delivery program. Similar to :func:`posix_spawn` except that the system searches for the *executable* file in the list of directories specified by the - :envvar:`PATH` environment variable (in the same way as for ``execvp(3)``) - if the *path* argument contains no directory. + :envvar:`PATH` environment variable (in the same way as for ``execvp(3)``). .. versionadded:: 3.8 diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 3f138512b55463..b25b5220cdb399 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5384,10 +5384,9 @@ parse_file_actions(PyObject *file_actions, static PyObject * py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv, - PyObject *env, PyObject *file_actions, - PyObject *setpgroup, int resetids, PyObject *setsigmask, - PyObject *setsigdef, PyObject *scheduler) -/*[clinic end generated code: output=45dfa4c515d09f2c input=2891c2f1d457e39b]*/ + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, int resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler) { EXECV_CHAR **argvlist = NULL; EXECV_CHAR **envlist = NULL; @@ -5463,16 +5462,19 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a attrp = &attr; _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_POSIX_SPAWNP if (use_posix_spawnp) { err_code = posix_spawnp(&pid, path->narrow, - file_actionsp, attrp, argvlist, envlist); + file_actionsp, attrp, argvlist, envlist); } - else { + else +#endif /* HAVE_POSIX_SPAWNP */ + { err_code = posix_spawn(&pid, path->narrow, - file_actionsp, attrp, argvlist, envlist); + file_actionsp, attrp, argvlist, envlist); } - _Py_END_SUPPRESS_IPH + if (err_code) { errno = err_code; PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); @@ -5536,8 +5538,8 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, /*[clinic end generated code: output=45dfa4c515d09f2c input=2891c2f1d457e39b]*/ { return py_posix_spawn(0, module, path, argv, env, file_actions, - setpgroup, resetids, setsigmask, setsigdef, - scheduler); + setpgroup, resetids, setsigmask, setsigdef, + scheduler); } #endif /* HAVE_POSIX_SPAWN */ @@ -5579,8 +5581,8 @@ os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, /*[clinic end generated code: output=7955dc0edc82b8c3 input=b7576eb25b1ed9eb]*/ { return py_posix_spawn(1, module, path, argv, env, file_actions, - setpgroup, resetids, setsigmask, setsigdef, - scheduler); + setpgroup, resetids, setsigmask, setsigdef, + scheduler); } #endif /* HAVE_POSIX_SPAWNP */