From 4982abde2e66b1f9899f27d741a12c02dc09697c Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Wed, 2 May 2018 11:53:46 +0100 Subject: [PATCH 01/11] Add attr capabilities to posix_spawn --- Lib/test/test_posix.py | 127 +++++++++++++++ .../2018-05-05-23-26-58.bpo-20104.tDBciE.rst | 2 + Modules/clinic/posixmodule.c.h | 40 +---- Modules/posixmodule.c | 150 +++++++++++++++++- 4 files changed, 276 insertions(+), 43 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 4f6abc6f7d6fd9..0a55f10a385212 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -8,6 +8,7 @@ import errno import sys +import signal import time import os import platform @@ -16,6 +17,7 @@ import tempfile import unittest import warnings +import textwrap _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), support.TESTFN + '-dummy-symlink') @@ -1476,6 +1478,131 @@ def test_empty_file_actions(self): ) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + def test_resetids_explicit_default(self): + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', 'pass'], + os.environ, + resetids=False + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_resetids(self): + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', 'pass'], + os.environ, + resetids=True + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_resetids_wrong_type(self): + with self.assertRaises(TypeError): + posix.posix_spawn(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, resetids=0) + with self.assertRaises(TypeError): + posix.posix_spawn(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, resetids=None) + + def test_setpgroup(self): + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', 'pass'], + os.environ, + setpgroup=os.getpgrp() + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), + 'need signal.pthread_sigmask()') + def test_setsigmask(self): + code = textwrap.dedent("""\ + import _testcapi, signal + _testcapi.raise_signal(signal.SIGUSR1)""") + + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', code], + os.environ, + setsigmask=[signal.SIGUSR1] + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_setsigmask_wrong_type(self): + with self.assertRaises(TypeError): + posix.posix_spawn(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"]) + with self.assertRaises(ValueError): + posix.posix_spawn(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigmask=[9998, 9999]) + + @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") + def test_setscheduler_only_param(self): + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', "pass"], + os.environ, + scheduler=(None, os.sched_param(os.PRIO_PROCESS)) + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") + def test_setscheduler_with_policy(self): + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', "pass"], + os.environ, + scheduler=(os.SCHED_OTHER, os.sched_param(os.PRIO_PROCESS)) + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), + 'need signal.pthread_sigmask()') + def test_setsigdef(self): + original_handler = signal.signal(signal.SIGUSR1, signal.SIG_IGN) + code = textwrap.dedent("""\ + import _testcapi, signal + _testcapi.raise_signal(signal.SIGUSR1)""") + try: + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', code], + os.environ, + setsigdef=[signal.SIGUSR1] + ) + finally: + signal.signal(signal.SIGUSR1, original_handler) + + self.assertNotEqual(os.waitpid(pid, 0), (pid, 0)) + + def test_setsigdef_wrong_type(self): + with self.assertRaises(TypeError): + posix.posix_spawn(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"]) + with self.assertRaises(ValueError): + posix.posix_spawn(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setsigdef=[9998, 9999]) + + def test_setgroup_wrong_type(self): + with self.assertRaises(TypeError): + posix.posix_spawn(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setgroup="023") + def test_multiple_file_actions(self): file_actions = [ (os.POSIX_SPAWN_OPEN, 3, os.path.realpath(__file__), os.O_RDONLY, 0), diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst b/Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst new file mode 100644 index 00000000000000..1d725ba7eb7120 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst @@ -0,0 +1,2 @@ +Added support for the `setpgroup`, `resetids`, `setsigmask`, `setsigdef` and +`scheduler` parameters of `posix_spawn`. Patch by Pablo Galindo. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 7a2188504ac57e..e4bbd082450baa 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -3853,40 +3853,6 @@ os_write(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#if defined(__APPLE__) - -PyDoc_STRVAR(os__fcopyfile__doc__, -"_fcopyfile($module, infd, outfd, flags, /)\n" -"--\n" -"\n" -"Efficiently copy content or metadata of 2 regular file descriptors (macOS)."); - -#define OS__FCOPYFILE_METHODDEF \ - {"_fcopyfile", (PyCFunction)os__fcopyfile, METH_FASTCALL, os__fcopyfile__doc__}, - -static PyObject * -os__fcopyfile_impl(PyObject *module, int infd, int outfd, int flags); - -static PyObject * -os__fcopyfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - int infd; - int outfd; - int flags; - - if (!_PyArg_ParseStack(args, nargs, "iii:_fcopyfile", - &infd, &outfd, &flags)) { - goto exit; - } - return_value = os__fcopyfile_impl(module, infd, outfd, flags); - -exit: - return return_value; -} - -#endif /* defined(__APPLE__) */ - PyDoc_STRVAR(os_fstat__doc__, "fstat($module, /, fd)\n" "--\n" @@ -6448,10 +6414,6 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #define OS_PREADV_METHODDEF #endif /* !defined(OS_PREADV_METHODDEF) */ -#ifndef OS__FCOPYFILE_METHODDEF - #define OS__FCOPYFILE_METHODDEF -#endif /* !defined(OS__FCOPYFILE_METHODDEF) */ - #ifndef OS_PIPE_METHODDEF #define OS_PIPE_METHODDEF #endif /* !defined(OS_PIPE_METHODDEF) */ @@ -6627,4 +6589,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=47fb6a3e88cba6d9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8d3d9dddf254c3c2 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 435e5f61271cc4..43c8437031bf79 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5177,6 +5177,123 @@ enum posix_spawn_file_actions_identifier { POSIX_SPAWN_DUP2 }; +static int +convert_sched_param(PyObject *param, struct sched_param *res); + +static int +parse_posix_spawn_flags(PyObject *setpgroup, PyObject *resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler, + posix_spawnattr_t *attrp) +{ + long all_flags = 0; + + errno = posix_spawnattr_init(attrp); + if (errno) { + posix_error(); + return -1; + } + + if (setpgroup) { + pid_t pgid = PyLong_AsPid(setpgroup); + if (pgid == (pid_t)-1 && PyErr_Occurred()) { + goto fail; + } + errno = posix_spawnattr_setpgroup(attrp, pgid); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETPGROUP; + } + + if (resetids == Py_True) { + all_flags |= POSIX_SPAWN_RESETIDS; + } + else if (resetids != Py_False) { + PyErr_SetString(PyExc_TypeError, + "The resetids parameter must be either True or False."); + goto fail; + } + + if (setsigmask) { + sigset_t set; + if (!PyArg_Parse(setsigmask, "O&", _Py_Sigset_Converter, &set)) { + goto fail; + } + errno = posix_spawnattr_setsigmask(attrp, &set); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSIGMASK; + } + + if (setsigdef) { + sigset_t set; + if (!PyArg_Parse(setsigdef, "O&", _Py_Sigset_Converter, &set)) { + goto fail; + } + errno = posix_spawnattr_setsigdefault(attrp, &set); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSIGDEF; + } + + if (scheduler) { +#ifdef POSIX_SPAWN_SETSCHEDULER + PyObject *py_schedpolicy; + struct sched_param schedparam; + + if (!PyArg_ParseTuple(scheduler, "OO&" + ";A scheduler tuple must have two elements", + &py_schedpolicy, convert_sched_param, &schedparam)) { + goto fail; + } + if (py_schedpolicy != Py_None) { + int schedpolicy = _PyLong_AsInt(py_schedpolicy); + + if (schedpolicy == -1 && PyErr_Occurred()) { + goto fail; + } + if (schedpolicy > INT_MAX || schedpolicy < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, "sched_policy out of range"); + goto fail; + } + errno = posix_spawnattr_setschedpolicy(attrp, schedpolicy); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSCHEDULER; + } + errno = posix_spawnattr_setschedparam(attrp, &schedparam); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSCHEDPARAM; +#else + PyErr_SetString(PyExc_NotImplementedError, + "The scheduler option is not supported in this system."); + goto fail; +#endif + } + + errno = posix_spawnattr_setflags(attrp, all_flags); + if (errno) { + posix_error(); + goto fail; + } + + return 0; + +fail: + (void)posix_spawnattr_destroy(attrp); + return -1; +} + static int parse_file_actions(PyObject *file_actions, posix_spawn_file_actions_t *file_actionsp, @@ -5277,6 +5394,7 @@ parse_file_actions(PyObject *file_actions, } Py_DECREF(file_action); } + Py_DECREF(seq); return 0; @@ -5299,19 +5417,34 @@ os.posix_spawn file_actions: object = None A sequence of file action tuples. / - + * + setpgroup: object = NULL + The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. + resetids: object = 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 (optinal) 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) -/*[clinic end generated code: output=d023521f541c709c input=a3db1021d33230dc]*/ + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, PyObject *resetids, + PyObject *setsigmask, PyObject *setsigdef, + PyObject *scheduler) +/*[clinic end generated code: output=9ed52ce93bef3143 input=2f923b47b11cc8c4]*/ { EXECV_CHAR **argvlist = NULL; EXECV_CHAR **envlist = NULL; posix_spawn_file_actions_t file_actions_buf; posix_spawn_file_actions_t *file_actionsp = NULL; + posix_spawnattr_t attr; + posix_spawnattr_t *attrp = NULL; Py_ssize_t argc, envc; PyObject *result = NULL; PyObject *temp_buffer = NULL; @@ -5373,9 +5506,15 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, file_actionsp = &file_actions_buf; } + if (parse_posix_spawn_flags(setpgroup, resetids, setsigmask, + setsigdef, scheduler, &attr)) { + goto exit; + } + attrp = &attr; + _Py_BEGIN_SUPPRESS_IPH err_code = posix_spawn(&pid, path->narrow, - file_actionsp, NULL, argvlist, envlist); + file_actionsp, attrp, argvlist, envlist); _Py_END_SUPPRESS_IPH if (err_code) { errno = err_code; @@ -5388,6 +5527,9 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, if (file_actionsp) { (void)posix_spawn_file_actions_destroy(file_actionsp); } + if (attrp) { + (void)posix_spawnattr_destroy(attrp); + } if (envlist) { free_string_array(envlist, envc); } From ad862ccc82a8c4cc72bf2f38fd90b2430fabe363 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 8 May 2018 00:04:02 +0100 Subject: [PATCH 02/11] Add documentation for the new parameters of posix_spawn --- Doc/library/os.rst | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index c64f9321143731..183bbcb1ed0273 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3392,7 +3392,9 @@ written in Python, such as a mail server's external command delivery program. subprocesses. -.. function:: posix_spawn(path, argv, env, file_actions=None) +.. function:: posix_spawn(path, argv, env, file_actions=None, /, *, \ + setpgroup=None, resetids=False, setsigmask=(), \ + setsigdef=(), scheduler=None) Wraps the :c:func:`posix_spawn` C library API for use from Python. @@ -3430,6 +3432,36 @@ written in Python, such as a mail server's external command delivery program. :c:func:`posix_spawn_file_actions_adddup2` API calls used to prepare for the :c:func:`posix_spawn` call itself. + The *setpgroup* argument will set the process group of the child to the value + specified. If the value specified is 0, the child's process group ID will be + made the same as its process ID. If the value of *setpgroup* is not set, the + child will inherit the parent's process group ID. This argument corresponds + to the C library :c:data:`POSIX_SPAWN_SETPGROUP` flag. + + If the *resetids* argument is ``True`` it will reset the effective UID and + GID of the child to the real UID and GID of the parent process. If the + argument is ``False``, then the child retains the effective UID and GID of + the parent. In either case, if the set-user-ID and set-group-ID permission + bits are enabled on the executable file, their effect will override the + setting of the effective UID and GID. This argument corresponds to the C + library :c:data:`POSIX_SPAWN_RESETIDS` flag. + + The *setsigmask* argument will set the signal mask to the signal set + specified. If the parameter is not used, then the child will inherits the + parent's signal mask. This argument corresponds to the C library + :c:data:`POSIX_SPAWN_SETSIGMASK` flag. + + The *sigdef* argument will reset the disposition of all signals in the set + specified. This argument corresponds to the C library + :c:data:`POSIX_SPAWN_SETSIGDEF` flag. + + The *scheduler* argument must be a tuple containing the (optional) scheduler + policy and an instance of :class:`sched_param` with the scheduler parameters. + A value of ``None`` in the place of the scheduler policy indicates that is + not being provided. This argument is a combination of the C library + :c:data:`POSIX_SPAWN_SETSCHEDPARAM` and :c:data:`POSIX_SPAWN_SETSCHEDULER` + flags. + .. versionadded:: 3.7 From 552b1dccc2e4bac4598deb4adfdeb6118ad0fe81 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 8 May 2018 21:28:06 +0100 Subject: [PATCH 03/11] Refactor and clean tests for posix_spawn flags --- Lib/test/test_posix.py | 70 ++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 0a55f10a385212..94e32a95f889c8 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1515,6 +1515,12 @@ def test_setpgroup(self): ) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + def test_setpgroup_wrong_type(self): + with self.assertRaises(TypeError): + posix.posix_spawn(sys.executable, + [sys.executable, "-c", "pass"], + os.environ, setpgroup="023") + @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), 'need signal.pthread_sigmask()') def test_setsigmask(self): @@ -1544,26 +1550,6 @@ def test_setsigmask_wrong_type(self): [sys.executable, "-c", "pass"], os.environ, setsigmask=[9998, 9999]) - @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") - def test_setscheduler_only_param(self): - pid = posix.posix_spawn( - sys.executable, - [sys.executable, '-c', "pass"], - os.environ, - scheduler=(None, os.sched_param(os.PRIO_PROCESS)) - ) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - - @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") - def test_setscheduler_with_policy(self): - pid = posix.posix_spawn( - sys.executable, - [sys.executable, '-c', "pass"], - os.environ, - scheduler=(os.SCHED_OTHER, os.sched_param(os.PRIO_PROCESS)) - ) - self.assertEqual(os.waitpid(pid, 0), (pid, 0)) - @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), 'need signal.pthread_sigmask()') def test_setsigdef(self): @@ -1581,7 +1567,9 @@ def test_setsigdef(self): finally: signal.signal(signal.SIGUSR1, original_handler) - self.assertNotEqual(os.waitpid(pid, 0), (pid, 0)) + pid2, status = os.waitpid(pid, 0) + self.assertEqual(pid2, pid) + self.assertNotEqual(status, 0) def test_setsigdef_wrong_type(self): with self.assertRaises(TypeError): @@ -1597,11 +1585,41 @@ def test_setsigdef_wrong_type(self): [sys.executable, "-c", "pass"], os.environ, setsigdef=[9998, 9999]) - def test_setgroup_wrong_type(self): - with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, setgroup="023") + @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") + def test_setscheduler_only_param(self): + policy = os.sched_getscheduler(0) + priority = os.sched_get_priority_min(policy) + code = textwrap.dedent(f"""\ + import os + if os.sched_getscheduler(0) != {policy}: + os.exit(101) + if os.sched_getparam(0).sched_priority != {priority}: + os.exit(102)""") + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', code], + os.environ, + scheduler=(None, os.sched_param(os.PRIO_PROCESS)) + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + + @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") + def test_setscheduler_with_policy(self): + policy = os.sched_getscheduler(0) + priority = os.sched_get_priority_min(policy) + code = textwrap.dedent(f"""\ + import os + if os.sched_getscheduler(0) != {policy}: + os.exit(101) + if os.sched_getparam(0).sched_priority != {priority}: + os.exit(102)""") + pid = posix.posix_spawn( + sys.executable, + [sys.executable, '-c', code], + os.environ, + scheduler=(os.SCHED_OTHER, os.sched_param(os.PRIO_PROCESS)) + ) + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) def test_multiple_file_actions(self): file_actions = [ From 713d80364dbeb6b2722b93b54c31501a024b1f8e Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 8 May 2018 21:31:28 +0100 Subject: [PATCH 04/11] Refactor and clean implementation of parse_posix_spawn_flags --- Modules/posixmodule.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 43c8437031bf79..276eb66a972b48 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5217,7 +5217,7 @@ parse_posix_spawn_flags(PyObject *setpgroup, PyObject *resetids, PyObject *setsi if (setsigmask) { sigset_t set; - if (!PyArg_Parse(setsigmask, "O&", _Py_Sigset_Converter, &set)) { + if (!_Py_Sigset_Converter(setsigmask, &set)) { goto fail; } errno = posix_spawnattr_setsigmask(attrp, &set); @@ -5230,7 +5230,7 @@ parse_posix_spawn_flags(PyObject *setpgroup, PyObject *resetids, PyObject *setsi if (setsigdef) { sigset_t set; - if (!PyArg_Parse(setsigdef, "O&", _Py_Sigset_Converter, &set)) { + if (!_Py_Sigset_Converter(setsigdef, &set)) { goto fail; } errno = posix_spawnattr_setsigdefault(attrp, &set); @@ -5257,10 +5257,6 @@ parse_posix_spawn_flags(PyObject *setpgroup, PyObject *resetids, PyObject *setsi if (schedpolicy == -1 && PyErr_Occurred()) { goto fail; } - if (schedpolicy > INT_MAX || schedpolicy < INT_MIN) { - PyErr_SetString(PyExc_OverflowError, "sched_policy out of range"); - goto fail; - } errno = posix_spawnattr_setschedpolicy(attrp, schedpolicy); if (errno) { posix_error(); From ecd64e4505e2a65b20954d810bf56d8e4d7ff7f5 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 8 May 2018 21:33:19 +0100 Subject: [PATCH 05/11] Fix scheduler test for posix_spawn flags to use correctly the policy and priority --- Lib/test/test_posix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 94e32a95f889c8..1aded410ccf824 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1599,7 +1599,7 @@ def test_setscheduler_only_param(self): sys.executable, [sys.executable, '-c', code], os.environ, - scheduler=(None, os.sched_param(os.PRIO_PROCESS)) + scheduler=(None, os.sched_param(priority)) ) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) @@ -1617,7 +1617,7 @@ def test_setscheduler_with_policy(self): sys.executable, [sys.executable, '-c', code], os.environ, - scheduler=(os.SCHED_OTHER, os.sched_param(os.PRIO_PROCESS)) + scheduler=(policy, os.sched_param(priority)) ) self.assertEqual(os.waitpid(pid, 0), (pid, 0)) From 88a831c78a0288e47482fc01f4f3a5b6fddb6f74 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 10 May 2018 12:43:01 +0100 Subject: [PATCH 06/11] Indent string code blocks --- Lib/test/test_posix.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 1aded410ccf824..da3689d7ae2ca6 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1590,11 +1590,11 @@ def test_setscheduler_only_param(self): policy = os.sched_getscheduler(0) priority = os.sched_get_priority_min(policy) code = textwrap.dedent(f"""\ - import os - if os.sched_getscheduler(0) != {policy}: - os.exit(101) - if os.sched_getparam(0).sched_priority != {priority}: - os.exit(102)""") + import os + if os.sched_getscheduler(0) != {policy}: + os.exit(101) + if os.sched_getparam(0).sched_priority != {priority}: + os.exit(102)""") pid = posix.posix_spawn( sys.executable, [sys.executable, '-c', code], @@ -1608,11 +1608,11 @@ def test_setscheduler_with_policy(self): policy = os.sched_getscheduler(0) priority = os.sched_get_priority_min(policy) code = textwrap.dedent(f"""\ - import os - if os.sched_getscheduler(0) != {policy}: - os.exit(101) - if os.sched_getparam(0).sched_priority != {priority}: - os.exit(102)""") + import os + if os.sched_getscheduler(0) != {policy}: + os.exit(101) + if os.sched_getparam(0).sched_priority != {priority}: + os.exit(102)""") pid = posix.posix_spawn( sys.executable, [sys.executable, '-c', code], From 0fbae92c4dfb5c2fac421ec33f0be9e6839a5cde Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 14 May 2018 09:12:47 -0400 Subject: [PATCH 07/11] Fix typo in the documentation --- Doc/library/os.rst | 2 +- Modules/posixmodule.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 183bbcb1ed0273..1b8d9040ff5cfd 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3447,7 +3447,7 @@ written in Python, such as a mail server's external command delivery program. library :c:data:`POSIX_SPAWN_RESETIDS` flag. The *setsigmask* argument will set the signal mask to the signal set - specified. If the parameter is not used, then the child will inherits the + specified. If the parameter is not used, then the child inherits the parent's signal mask. This argument corresponds to the C library :c:data:`POSIX_SPAWN_SETSIGMASK` flag. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 276eb66a972b48..d8dc47d0e39837 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5423,7 +5423,7 @@ os.posix_spawn setsigdef: object(c_default='NULL') = () The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. scheduler: object = NULL - A tuple with the scheduler policy (optinal) and parameters. + A tuple with the scheduler policy (optional) and parameters. Execute the program specified by path in a new process. [clinic start generated code]*/ @@ -5433,7 +5433,7 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *setpgroup, PyObject *resetids, PyObject *setsigmask, PyObject *setsigdef, PyObject *scheduler) -/*[clinic end generated code: output=9ed52ce93bef3143 input=2f923b47b11cc8c4]*/ +/*[clinic end generated code: output=9ed52ce93bef3143 input=29fb938a0224bcb0]*/ { EXECV_CHAR **argvlist = NULL; EXECV_CHAR **envlist = NULL; From 06262a5df005b18adb95ad4bae92c1f19dc60513 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Fri, 20 Jul 2018 10:57:05 +0100 Subject: [PATCH 08/11] Regenerated the clinic files --- Modules/clinic/posixmodule.c.h | 78 ++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index e4bbd082450baa..53b94ead29f5f2 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1730,7 +1730,9 @@ os_execve(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k #if defined(HAVE_POSIX_SPAWN) PyDoc_STRVAR(os_posix_spawn__doc__, -"posix_spawn($module, path, argv, env, file_actions=None, /)\n" +"posix_spawn($module, path, argv, env, file_actions=None, /, *,\n" +" setpgroup=None, resetids=False, setsigmask=(),\n" +" setsigdef=(), scheduler=None)\n" "--\n" "\n" "Execute the program specified by path in a new process.\n" @@ -1742,29 +1744,49 @@ PyDoc_STRVAR(os_posix_spawn__doc__, " env\n" " Dictionary of strings mapping to strings.\n" " file_actions\n" -" A sequence of file action tuples."); +" 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_SPAWN_METHODDEF \ - {"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL, os_posix_spawn__doc__}, + {"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL|METH_KEYWORDS, os_posix_spawn__doc__}, static PyObject * os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, - PyObject *env, PyObject *file_actions); + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, PyObject *resetids, + PyObject *setsigmask, PyObject *setsigdef, + PyObject *scheduler); static PyObject * -os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + static const char * const _keywords[] = {"", "", "", "", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL}; + static _PyArg_Parser _parser = {"O&OO|O$OOOOO:posix_spawn", _keywords, 0}; path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0); PyObject *argv; PyObject *env; PyObject *file_actions = Py_None; + PyObject *setpgroup = NULL; + PyObject *resetids = Py_False; + PyObject *setsigmask = NULL; + PyObject *setsigdef = NULL; + PyObject *scheduler = NULL; - if (!_PyArg_ParseStack(args, nargs, "O&OO|O:posix_spawn", - path_converter, &path, &argv, &env, &file_actions)) { + 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_spawn_impl(module, &path, argv, env, file_actions); + return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsigmask, setsigdef, scheduler); exit: /* Cleanup for path */ @@ -3853,6 +3875,40 @@ os_write(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +#if defined(__APPLE__) + +PyDoc_STRVAR(os__fcopyfile__doc__, +"_fcopyfile($module, infd, outfd, flags, /)\n" +"--\n" +"\n" +"Efficiently copy content or metadata of 2 regular file descriptors (macOS)."); + +#define OS__FCOPYFILE_METHODDEF \ + {"_fcopyfile", (PyCFunction)os__fcopyfile, METH_FASTCALL, os__fcopyfile__doc__}, + +static PyObject * +os__fcopyfile_impl(PyObject *module, int infd, int outfd, int flags); + +static PyObject * +os__fcopyfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int infd; + int outfd; + int flags; + + if (!_PyArg_ParseStack(args, nargs, "iii:_fcopyfile", + &infd, &outfd, &flags)) { + goto exit; + } + return_value = os__fcopyfile_impl(module, infd, outfd, flags); + +exit: + return return_value; +} + +#endif /* defined(__APPLE__) */ + PyDoc_STRVAR(os_fstat__doc__, "fstat($module, /, fd)\n" "--\n" @@ -6414,6 +6470,10 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #define OS_PREADV_METHODDEF #endif /* !defined(OS_PREADV_METHODDEF) */ +#ifndef OS__FCOPYFILE_METHODDEF + #define OS__FCOPYFILE_METHODDEF +#endif /* !defined(OS__FCOPYFILE_METHODDEF) */ + #ifndef OS_PIPE_METHODDEF #define OS_PIPE_METHODDEF #endif /* !defined(OS_PIPE_METHODDEF) */ @@ -6589,4 +6649,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=8d3d9dddf254c3c2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3ac6dffb4488c7e7 input=a9049054013a1b77]*/ From c784e0f28e19b5da745934f7a0ac0aaa7e59536c Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Fri, 31 Aug 2018 23:02:52 +0100 Subject: [PATCH 09/11] Make tests more strict --- Lib/test/test_posix.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index da3689d7ae2ca6..60e6faa5d6ccc0 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1548,7 +1548,8 @@ def test_setsigmask_wrong_type(self): with self.assertRaises(ValueError): posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], - os.environ, setsigmask=[9998, 9999]) + os.environ, setsigmask=[signal.NSIG, + signal.NSIG+1]) @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), 'need signal.pthread_sigmask()') @@ -1569,7 +1570,8 @@ def test_setsigdef(self): pid2, status = os.waitpid(pid, 0) self.assertEqual(pid2, pid) - self.assertNotEqual(status, 0) + self.assertTrue(os.WIFSIGNALED(status), status) + self.assertEqual(os.WTERMSIG(status), signal.SIGUSR1) def test_setsigdef_wrong_type(self): with self.assertRaises(TypeError): From 514f55ebe6aad026454ea333a329bb6552c118ab Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 3 Sep 2018 22:40:40 +0100 Subject: [PATCH 10/11] Use signal.NSIG for testing invalid signals --- Lib/test/test_posix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 60e6faa5d6ccc0..3d08c4254d1cf9 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1585,7 +1585,7 @@ def test_setsigdef_wrong_type(self): with self.assertRaises(ValueError): posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], - os.environ, setsigdef=[9998, 9999]) + os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler") def test_setscheduler_only_param(self): From abfcd6a139a89b2d2e009c34c84d2c23eb7c25ec Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 3 Sep 2018 22:51:24 +0100 Subject: [PATCH 11/11] Accept 1 and 0 as valid values for resetids --- Lib/test/test_posix.py | 4 ---- Modules/clinic/posixmodule.c.h | 11 +++++------ Modules/posixmodule.c | 18 ++++++------------ 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 3d08c4254d1cf9..56f8e07ab6a44b 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1497,10 +1497,6 @@ def test_resetids(self): self.assertEqual(os.waitpid(pid, 0), (pid, 0)) def test_resetids_wrong_type(self): - with self.assertRaises(TypeError): - posix.posix_spawn(sys.executable, - [sys.executable, "-c", "pass"], - os.environ, resetids=0) with self.assertRaises(TypeError): posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 53b94ead29f5f2..f7767c4af0bb81 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1762,22 +1762,21 @@ PyDoc_STRVAR(os_posix_spawn__doc__, static PyObject * os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env, PyObject *file_actions, - PyObject *setpgroup, PyObject *resetids, - PyObject *setsigmask, PyObject *setsigdef, - PyObject *scheduler); + PyObject *setpgroup, int resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler); static PyObject * os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; static const char * const _keywords[] = {"", "", "", "", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL}; - static _PyArg_Parser _parser = {"O&OO|O$OOOOO:posix_spawn", _keywords, 0}; + static _PyArg_Parser _parser = {"O&OO|O$OiOOO:posix_spawn", _keywords, 0}; path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0); PyObject *argv; PyObject *env; PyObject *file_actions = Py_None; PyObject *setpgroup = NULL; - PyObject *resetids = Py_False; + int resetids = 0; PyObject *setsigmask = NULL; PyObject *setsigdef = NULL; PyObject *scheduler = NULL; @@ -6649,4 +6648,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=3ac6dffb4488c7e7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ef78384ae88712e1 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d8dc47d0e39837..02bc30754225ba 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5181,7 +5181,7 @@ static int convert_sched_param(PyObject *param, struct sched_param *res); static int -parse_posix_spawn_flags(PyObject *setpgroup, PyObject *resetids, PyObject *setsigmask, +parse_posix_spawn_flags(PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setsigdef, PyObject *scheduler, posix_spawnattr_t *attrp) { @@ -5206,14 +5206,9 @@ parse_posix_spawn_flags(PyObject *setpgroup, PyObject *resetids, PyObject *setsi all_flags |= POSIX_SPAWN_SETPGROUP; } - if (resetids == Py_True) { + if (resetids) { all_flags |= POSIX_SPAWN_RESETIDS; } - else if (resetids != Py_False) { - PyErr_SetString(PyExc_TypeError, - "The resetids parameter must be either True or False."); - goto fail; - } if (setsigmask) { sigset_t set; @@ -5416,7 +5411,7 @@ os.posix_spawn * setpgroup: object = NULL The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. - resetids: object = False + 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. @@ -5430,10 +5425,9 @@ Execute the program specified by path in a new process. static PyObject * os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env, PyObject *file_actions, - PyObject *setpgroup, PyObject *resetids, - PyObject *setsigmask, PyObject *setsigdef, - PyObject *scheduler) -/*[clinic end generated code: output=9ed52ce93bef3143 input=29fb938a0224bcb0]*/ + PyObject *setpgroup, int resetids, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler) +/*[clinic end generated code: output=45dfa4c515d09f2c input=2d7a7578430a90f0]*/ { EXECV_CHAR **argvlist = NULL; EXECV_CHAR **envlist = NULL;