Skip to content

Commit 254a466

Browse files
authored
bpo-20104: Add flag capabilities to posix_spawn (GH-6693)
Implement the "attributes objects" parameter of `os.posix_spawn` to complete the implementation and fully cover the underlying API.
1 parent 5e92265 commit 254a466

File tree

5 files changed

+344
-14
lines changed

5 files changed

+344
-14
lines changed

Doc/library/os.rst

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3394,7 +3394,9 @@ written in Python, such as a mail server's external command delivery program.
33943394
subprocesses.
33953395

33963396

3397-
.. function:: posix_spawn(path, argv, env, file_actions=None)
3397+
.. function:: posix_spawn(path, argv, env, file_actions=None, /, *, \
3398+
setpgroup=None, resetids=False, setsigmask=(), \
3399+
setsigdef=(), scheduler=None)
33983400

33993401
Wraps the :c:func:`posix_spawn` C library API for use from Python.
34003402

@@ -3432,6 +3434,36 @@ written in Python, such as a mail server's external command delivery program.
34323434
:c:func:`posix_spawn_file_actions_adddup2` API calls used to prepare
34333435
for the :c:func:`posix_spawn` call itself.
34343436

3437+
The *setpgroup* argument will set the process group of the child to the value
3438+
specified. If the value specified is 0, the child's process group ID will be
3439+
made the same as its process ID. If the value of *setpgroup* is not set, the
3440+
child will inherit the parent's process group ID. This argument corresponds
3441+
to the C library :c:data:`POSIX_SPAWN_SETPGROUP` flag.
3442+
3443+
If the *resetids* argument is ``True`` it will reset the effective UID and
3444+
GID of the child to the real UID and GID of the parent process. If the
3445+
argument is ``False``, then the child retains the effective UID and GID of
3446+
the parent. In either case, if the set-user-ID and set-group-ID permission
3447+
bits are enabled on the executable file, their effect will override the
3448+
setting of the effective UID and GID. This argument corresponds to the C
3449+
library :c:data:`POSIX_SPAWN_RESETIDS` flag.
3450+
3451+
The *setsigmask* argument will set the signal mask to the signal set
3452+
specified. If the parameter is not used, then the child inherits the
3453+
parent's signal mask. This argument corresponds to the C library
3454+
:c:data:`POSIX_SPAWN_SETSIGMASK` flag.
3455+
3456+
The *sigdef* argument will reset the disposition of all signals in the set
3457+
specified. This argument corresponds to the C library
3458+
:c:data:`POSIX_SPAWN_SETSIGDEF` flag.
3459+
3460+
The *scheduler* argument must be a tuple containing the (optional) scheduler
3461+
policy and an instance of :class:`sched_param` with the scheduler parameters.
3462+
A value of ``None`` in the place of the scheduler policy indicates that is
3463+
not being provided. This argument is a combination of the C library
3464+
:c:data:`POSIX_SPAWN_SETSCHEDPARAM` and :c:data:`POSIX_SPAWN_SETSCHEDULER`
3465+
flags.
3466+
34353467
.. versionadded:: 3.7
34363468

34373469

Lib/test/test_posix.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import errno
1010
import sys
11+
import signal
1112
import time
1213
import os
1314
import platform
@@ -16,6 +17,7 @@
1617
import tempfile
1718
import unittest
1819
import warnings
20+
import textwrap
1921

2022
_DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(),
2123
support.TESTFN + '-dummy-symlink')
@@ -1540,6 +1542,147 @@ def test_empty_file_actions(self):
15401542
)
15411543
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
15421544

1545+
def test_resetids_explicit_default(self):
1546+
pid = posix.posix_spawn(
1547+
sys.executable,
1548+
[sys.executable, '-c', 'pass'],
1549+
os.environ,
1550+
resetids=False
1551+
)
1552+
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
1553+
1554+
def test_resetids(self):
1555+
pid = posix.posix_spawn(
1556+
sys.executable,
1557+
[sys.executable, '-c', 'pass'],
1558+
os.environ,
1559+
resetids=True
1560+
)
1561+
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
1562+
1563+
def test_resetids_wrong_type(self):
1564+
with self.assertRaises(TypeError):
1565+
posix.posix_spawn(sys.executable,
1566+
[sys.executable, "-c", "pass"],
1567+
os.environ, resetids=None)
1568+
1569+
def test_setpgroup(self):
1570+
pid = posix.posix_spawn(
1571+
sys.executable,
1572+
[sys.executable, '-c', 'pass'],
1573+
os.environ,
1574+
setpgroup=os.getpgrp()
1575+
)
1576+
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
1577+
1578+
def test_setpgroup_wrong_type(self):
1579+
with self.assertRaises(TypeError):
1580+
posix.posix_spawn(sys.executable,
1581+
[sys.executable, "-c", "pass"],
1582+
os.environ, setpgroup="023")
1583+
1584+
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
1585+
'need signal.pthread_sigmask()')
1586+
def test_setsigmask(self):
1587+
code = textwrap.dedent("""\
1588+
import _testcapi, signal
1589+
_testcapi.raise_signal(signal.SIGUSR1)""")
1590+
1591+
pid = posix.posix_spawn(
1592+
sys.executable,
1593+
[sys.executable, '-c', code],
1594+
os.environ,
1595+
setsigmask=[signal.SIGUSR1]
1596+
)
1597+
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
1598+
1599+
def test_setsigmask_wrong_type(self):
1600+
with self.assertRaises(TypeError):
1601+
posix.posix_spawn(sys.executable,
1602+
[sys.executable, "-c", "pass"],
1603+
os.environ, setsigmask=34)
1604+
with self.assertRaises(TypeError):
1605+
posix.posix_spawn(sys.executable,
1606+
[sys.executable, "-c", "pass"],
1607+
os.environ, setsigmask=["j"])
1608+
with self.assertRaises(ValueError):
1609+
posix.posix_spawn(sys.executable,
1610+
[sys.executable, "-c", "pass"],
1611+
os.environ, setsigmask=[signal.NSIG,
1612+
signal.NSIG+1])
1613+
1614+
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
1615+
'need signal.pthread_sigmask()')
1616+
def test_setsigdef(self):
1617+
original_handler = signal.signal(signal.SIGUSR1, signal.SIG_IGN)
1618+
code = textwrap.dedent("""\
1619+
import _testcapi, signal
1620+
_testcapi.raise_signal(signal.SIGUSR1)""")
1621+
try:
1622+
pid = posix.posix_spawn(
1623+
sys.executable,
1624+
[sys.executable, '-c', code],
1625+
os.environ,
1626+
setsigdef=[signal.SIGUSR1]
1627+
)
1628+
finally:
1629+
signal.signal(signal.SIGUSR1, original_handler)
1630+
1631+
pid2, status = os.waitpid(pid, 0)
1632+
self.assertEqual(pid2, pid)
1633+
self.assertTrue(os.WIFSIGNALED(status), status)
1634+
self.assertEqual(os.WTERMSIG(status), signal.SIGUSR1)
1635+
1636+
def test_setsigdef_wrong_type(self):
1637+
with self.assertRaises(TypeError):
1638+
posix.posix_spawn(sys.executable,
1639+
[sys.executable, "-c", "pass"],
1640+
os.environ, setsigdef=34)
1641+
with self.assertRaises(TypeError):
1642+
posix.posix_spawn(sys.executable,
1643+
[sys.executable, "-c", "pass"],
1644+
os.environ, setsigdef=["j"])
1645+
with self.assertRaises(ValueError):
1646+
posix.posix_spawn(sys.executable,
1647+
[sys.executable, "-c", "pass"],
1648+
os.environ, setsigdef=[signal.NSIG, signal.NSIG+1])
1649+
1650+
@unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler")
1651+
def test_setscheduler_only_param(self):
1652+
policy = os.sched_getscheduler(0)
1653+
priority = os.sched_get_priority_min(policy)
1654+
code = textwrap.dedent(f"""\
1655+
import os
1656+
if os.sched_getscheduler(0) != {policy}:
1657+
os.exit(101)
1658+
if os.sched_getparam(0).sched_priority != {priority}:
1659+
os.exit(102)""")
1660+
pid = posix.posix_spawn(
1661+
sys.executable,
1662+
[sys.executable, '-c', code],
1663+
os.environ,
1664+
scheduler=(None, os.sched_param(priority))
1665+
)
1666+
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
1667+
1668+
@unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler")
1669+
def test_setscheduler_with_policy(self):
1670+
policy = os.sched_getscheduler(0)
1671+
priority = os.sched_get_priority_min(policy)
1672+
code = textwrap.dedent(f"""\
1673+
import os
1674+
if os.sched_getscheduler(0) != {policy}:
1675+
os.exit(101)
1676+
if os.sched_getparam(0).sched_priority != {priority}:
1677+
os.exit(102)""")
1678+
pid = posix.posix_spawn(
1679+
sys.executable,
1680+
[sys.executable, '-c', code],
1681+
os.environ,
1682+
scheduler=(policy, os.sched_param(priority))
1683+
)
1684+
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
1685+
15431686
def test_multiple_file_actions(self):
15441687
file_actions = [
15451688
(os.POSIX_SPAWN_OPEN, 3, os.path.realpath(__file__), os.O_RDONLY, 0),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added support for the `setpgroup`, `resetids`, `setsigmask`, `setsigdef` and
2+
`scheduler` parameters of `posix_spawn`. Patch by Pablo Galindo.

Modules/clinic/posixmodule.c.h

Lines changed: 30 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)