[3.14] gh-143698: correctly check scheduler and setpgroup values for os.posix_spawn[p] (GH-143699) (#145073)

Fix an issue where passing invalid arguments to `os.posix_spawn[p]` functions
raised a SystemError instead of a TypeError, and allow to explicitly use `None`
for `scheduler` and `setpgroup` as specified in the docs.

(cherry picked from commit 347fc438cf)
This commit is contained in:
Bénédikt Tran 2026-02-21 16:04:31 +01:00 committed by GitHub
parent 8e482eb1ec
commit dcf96d0ed6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 46 additions and 15 deletions

View file

@ -6203,8 +6203,7 @@ def test_operator_module_has_signatures(self):
def test_os_module_has_signatures(self):
unsupported_signature = {'chmod', 'utime'}
unsupported_signature |= {name for name in
['get_terminal_size', 'link', 'posix_spawn', 'posix_spawnp',
'register_at_fork', 'startfile']
['get_terminal_size', 'link', 'register_at_fork', 'startfile']
if hasattr(os, name)}
self._test_module_has_signatures(os, unsupported_signature=unsupported_signature)

View file

@ -1933,6 +1933,11 @@ def test_setpgroup(self):
)
support.wait_process(pid, exitcode=0)
def test_setpgroup_allow_none(self):
path, args = self.NOOP_PROGRAM[0], self.NOOP_PROGRAM
pid = self.spawn_func(path, args, os.environ, setpgroup=None)
support.wait_process(pid, exitcode=0)
def test_setpgroup_wrong_type(self):
with self.assertRaises(TypeError):
self.spawn_func(sys.executable,
@ -2033,6 +2038,20 @@ def test_setsigdef_wrong_type(self):
[sys.executable, "-c", "pass"],
os.environ, setsigdef=[signal.NSIG, signal.NSIG+1])
def test_scheduler_allow_none(self):
path, args = self.NOOP_PROGRAM[0], self.NOOP_PROGRAM
pid = self.spawn_func(path, args, os.environ, scheduler=None)
support.wait_process(pid, exitcode=0)
@support.subTests("scheduler", [object(), 1, [1, 2]])
def test_scheduler_wrong_type(self, scheduler):
path, args = self.NOOP_PROGRAM[0], self.NOOP_PROGRAM
with self.assertRaisesRegex(
TypeError,
"scheduler must be a tuple or None",
):
self.spawn_func(path, args, os.environ, scheduler=scheduler)
@requires_sched
@unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')),
"bpo-34685: test can fail on BSD")

View file

@ -0,0 +1,3 @@
Raise :exc:`TypeError` instead of :exc:`SystemError` when the *scheduler*
in :func:`os.posix_spawn` or :func:`os.posix_spawnp` is not a tuple.
Patch by Bénédikt Tran.

View file

@ -0,0 +1,3 @@
Allow *scheduler* and *setpgroup* arguments to be explicitly :const:`None`
when calling :func:`os.posix_spawn` or :func:`os.posix_spawnp`. Patch by
Bénédikt Tran.

View file

@ -3845,8 +3845,8 @@ exit:
PyDoc_STRVAR(os_posix_spawn__doc__,
"posix_spawn($module, path, argv, env, /, *, file_actions=(),\n"
" setpgroup=<unrepresentable>, resetids=False, setsid=False,\n"
" setsigmask=(), setsigdef=(), scheduler=<unrepresentable>)\n"
" setpgroup=None, resetids=False, setsid=False,\n"
" setsigmask=(), setsigdef=(), scheduler=None)\n"
"--\n"
"\n"
"Execute the program specified by path in a new process.\n"
@ -3998,8 +3998,8 @@ exit:
PyDoc_STRVAR(os_posix_spawnp__doc__,
"posix_spawnp($module, path, argv, env, /, *, file_actions=(),\n"
" setpgroup=<unrepresentable>, resetids=False, setsid=False,\n"
" setsigmask=(), setsigdef=(), scheduler=<unrepresentable>)\n"
" setpgroup=None, resetids=False, setsid=False,\n"
" setsigmask=(), setsigdef=(), scheduler=None)\n"
"--\n"
"\n"
"Execute the program specified by path in a new process.\n"
@ -13476,4 +13476,4 @@ exit:
#ifndef OS__EMSCRIPTEN_LOG_METHODDEF
#define OS__EMSCRIPTEN_LOG_METHODDEF
#endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */
/*[clinic end generated code: output=9e5f9b9ce732a534 input=a9049054013a1b77]*/
/*[clinic end generated code: output=291f607b7a26ca5e input=a9049054013a1b77]*/

View file

@ -7155,6 +7155,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg
PyObject *setsigdef, PyObject *scheduler,
posix_spawnattr_t *attrp)
{
assert(scheduler == NULL || scheduler == Py_None || PyTuple_Check(scheduler));
long all_flags = 0;
errno = posix_spawnattr_init(attrp);
@ -7163,7 +7164,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg
return -1;
}
if (setpgroup) {
if (setpgroup && setpgroup != Py_None) {
pid_t pgid = PyLong_AsPid(setpgroup);
if (pgid == (pid_t)-1 && PyErr_Occurred()) {
goto fail;
@ -7236,7 +7237,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg
}
#endif
if (scheduler) {
if (scheduler && scheduler != Py_None) {
#ifdef POSIX_SPAWN_SETSCHEDULER
PyObject *py_schedpolicy;
PyObject *schedparam_obj;
@ -7461,6 +7462,12 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a
goto exit;
}
if (scheduler && !PyTuple_Check(scheduler) && scheduler != Py_None) {
PyErr_Format(PyExc_TypeError,
"%s: scheduler must be a tuple or None", func_name);
goto exit;
}
argvlist = parse_arglist(argv, &argc);
if (argvlist == NULL) {
goto exit;
@ -7572,7 +7579,7 @@ os.posix_spawn
*
file_actions: object(c_default='NULL') = ()
A sequence of file action tuples.
setpgroup: object = NULL
setpgroup: object(c_default='NULL') = None
The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
resetids: bool = False
If the value is `true` the POSIX_SPAWN_RESETIDS will be activated.
@ -7582,7 +7589,7 @@ os.posix_spawn
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
scheduler: object(c_default='NULL') = None
A tuple with the scheduler policy (optional) and parameters.
Execute the program specified by path in a new process.
@ -7594,7 +7601,7 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *setpgroup, int resetids, int setsid,
PyObject *setsigmask, PyObject *setsigdef,
PyObject *scheduler)
/*[clinic end generated code: output=14a1098c566bc675 input=808aed1090d84e33]*/
/*[clinic end generated code: output=14a1098c566bc675 input=69e7c9ebbdcf94a5]*/
{
return py_posix_spawn(0, module, path, argv, env, file_actions,
setpgroup, resetids, setsid, setsigmask, setsigdef,
@ -7618,7 +7625,7 @@ os.posix_spawnp
*
file_actions: object(c_default='NULL') = ()
A sequence of file action tuples.
setpgroup: object = NULL
setpgroup: object(c_default='NULL') = None
The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
resetids: bool = False
If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.
@ -7628,7 +7635,7 @@ os.posix_spawnp
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
scheduler: object(c_default='NULL') = None
A tuple with the scheduler policy (optional) and parameters.
Execute the program specified by path in a new process.
@ -7640,7 +7647,7 @@ os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *setpgroup, int resetids, int setsid,
PyObject *setsigmask, PyObject *setsigdef,
PyObject *scheduler)
/*[clinic end generated code: output=7b9aaefe3031238d input=9e89e616116752a1]*/
/*[clinic end generated code: output=7b9aaefe3031238d input=a5c057527c6881a5]*/
{
return py_posix_spawn(1, module, path, argv, env, file_actions,
setpgroup, resetids, setsid, setsigmask, setsigdef,