mirror of https://github.com/python/cpython
bpo-35674: Add os.posix_spawnp() (GH-11554)
Add a new os.posix_spawnp() function.
This commit is contained in:
parent
9daecf37a5
commit
92b8322e7e
|
@ -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
|
The positional-only arguments *path*, *args*, and *env* are similar to
|
||||||
:func:`execve`.
|
: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
|
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
|
to take on specific file descriptors in the child process between the C
|
||||||
library implementation's :c:func:`fork` and :c:func:`exec` steps.
|
library implementation's :c:func:`fork` and :c:func:`exec` steps.
|
||||||
|
@ -3459,6 +3463,19 @@ written in Python, such as a mail server's external command delivery program.
|
||||||
.. versionadded:: 3.7
|
.. 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)``).
|
||||||
|
|
||||||
|
.. versionadded:: 3.8
|
||||||
|
|
||||||
|
|
||||||
.. function:: register_at_fork(*, before=None, after_in_parent=None, \
|
.. function:: register_at_fork(*, before=None, after_in_parent=None, \
|
||||||
after_in_child=None)
|
after_in_child=None)
|
||||||
|
|
||||||
|
|
|
@ -1489,10 +1489,10 @@ class PosixGroupsTester(unittest.TestCase):
|
||||||
self.assertListEqual(groups, posix.getgroups())
|
self.assertListEqual(groups, posix.getgroups())
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn")
|
class _PosixSpawnMixin:
|
||||||
class TestPosixSpawn(unittest.TestCase):
|
# Program which does nothing and exits with status 0 (success)
|
||||||
# Program which does nothing and exit with status 0 (success)
|
|
||||||
NOOP_PROGRAM = (sys.executable, '-I', '-S', '-c', 'pass')
|
NOOP_PROGRAM = (sys.executable, '-I', '-S', '-c', 'pass')
|
||||||
|
spawn_func = None
|
||||||
|
|
||||||
def python_args(self, *args):
|
def python_args(self, *args):
|
||||||
# Disable site module to avoid side effects. For example,
|
# Disable site module to avoid side effects. For example,
|
||||||
|
@ -1511,7 +1511,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
pidfile.write(str(os.getpid()))
|
pidfile.write(str(os.getpid()))
|
||||||
"""
|
"""
|
||||||
args = self.python_args('-c', script)
|
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))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
with open(pidfile) as f:
|
with open(pidfile) as f:
|
||||||
self.assertEqual(f.read(), str(pid))
|
self.assertEqual(f.read(), str(pid))
|
||||||
|
@ -1519,9 +1519,9 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
def test_no_such_executable(self):
|
def test_no_such_executable(self):
|
||||||
no_such_executable = 'no_such_executable'
|
no_such_executable = 'no_such_executable'
|
||||||
try:
|
try:
|
||||||
pid = posix.posix_spawn(no_such_executable,
|
pid = self.spawn_func(no_such_executable,
|
||||||
[no_such_executable],
|
[no_such_executable],
|
||||||
os.environ)
|
os.environ)
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
self.assertEqual(exc.filename, no_such_executable)
|
self.assertEqual(exc.filename, no_such_executable)
|
||||||
else:
|
else:
|
||||||
|
@ -1538,14 +1538,14 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
envfile.write(os.environ['foo'])
|
envfile.write(os.environ['foo'])
|
||||||
"""
|
"""
|
||||||
args = self.python_args('-c', script)
|
args = self.python_args('-c', script)
|
||||||
pid = posix.posix_spawn(args[0], args,
|
pid = self.spawn_func(args[0], args,
|
||||||
{**os.environ, 'foo': 'bar'})
|
{**os.environ, 'foo': 'bar'})
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
with open(envfile) as f:
|
with open(envfile) as f:
|
||||||
self.assertEqual(f.read(), 'bar')
|
self.assertEqual(f.read(), 'bar')
|
||||||
|
|
||||||
def test_empty_file_actions(self):
|
def test_empty_file_actions(self):
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
self.NOOP_PROGRAM[0],
|
self.NOOP_PROGRAM[0],
|
||||||
self.NOOP_PROGRAM,
|
self.NOOP_PROGRAM,
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1554,7 +1554,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
|
|
||||||
def test_resetids_explicit_default(self):
|
def test_resetids_explicit_default(self):
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', 'pass'],
|
[sys.executable, '-c', 'pass'],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1563,7 +1563,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
|
|
||||||
def test_resetids(self):
|
def test_resetids(self):
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', 'pass'],
|
[sys.executable, '-c', 'pass'],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1573,12 +1573,12 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
|
|
||||||
def test_resetids_wrong_type(self):
|
def test_resetids_wrong_type(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, resetids=None)
|
os.environ, resetids=None)
|
||||||
|
|
||||||
def test_setpgroup(self):
|
def test_setpgroup(self):
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', 'pass'],
|
[sys.executable, '-c', 'pass'],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1588,9 +1588,9 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
|
|
||||||
def test_setpgroup_wrong_type(self):
|
def test_setpgroup_wrong_type(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setpgroup="023")
|
os.environ, setpgroup="023")
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
|
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
|
||||||
'need signal.pthread_sigmask()')
|
'need signal.pthread_sigmask()')
|
||||||
|
@ -1599,7 +1599,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
import signal
|
import signal
|
||||||
signal.raise_signal(signal.SIGUSR1)""")
|
signal.raise_signal(signal.SIGUSR1)""")
|
||||||
|
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', code],
|
[sys.executable, '-c', code],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1609,18 +1609,18 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
|
|
||||||
def test_setsigmask_wrong_type(self):
|
def test_setsigmask_wrong_type(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setsigmask=34)
|
os.environ, setsigmask=34)
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setsigmask=["j"])
|
os.environ, setsigmask=["j"])
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setsigmask=[signal.NSIG,
|
os.environ, setsigmask=[signal.NSIG,
|
||||||
signal.NSIG+1])
|
signal.NSIG+1])
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
|
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
|
||||||
'need signal.pthread_sigmask()')
|
'need signal.pthread_sigmask()')
|
||||||
|
@ -1630,7 +1630,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
import signal
|
import signal
|
||||||
signal.raise_signal(signal.SIGUSR1)""")
|
signal.raise_signal(signal.SIGUSR1)""")
|
||||||
try:
|
try:
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', code],
|
[sys.executable, '-c', code],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1646,17 +1646,17 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
|
|
||||||
def test_setsigdef_wrong_type(self):
|
def test_setsigdef_wrong_type(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setsigdef=34)
|
os.environ, setsigdef=34)
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setsigdef=["j"])
|
os.environ, setsigdef=["j"])
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
posix.posix_spawn(sys.executable,
|
self.spawn_func(sys.executable,
|
||||||
[sys.executable, "-c", "pass"],
|
[sys.executable, "-c", "pass"],
|
||||||
os.environ, setsigdef=[signal.NSIG, signal.NSIG+1])
|
os.environ, setsigdef=[signal.NSIG, signal.NSIG+1])
|
||||||
|
|
||||||
@requires_sched
|
@requires_sched
|
||||||
@unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')),
|
@unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')),
|
||||||
|
@ -1670,7 +1670,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
sys.exit(101)
|
sys.exit(101)
|
||||||
if os.sched_getparam(0).sched_priority != {priority}:
|
if os.sched_getparam(0).sched_priority != {priority}:
|
||||||
sys.exit(102)""")
|
sys.exit(102)""")
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', code],
|
[sys.executable, '-c', code],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1690,7 +1690,7 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
sys.exit(101)
|
sys.exit(101)
|
||||||
if os.sched_getparam(0).sched_priority != {priority}:
|
if os.sched_getparam(0).sched_priority != {priority}:
|
||||||
sys.exit(102)""")
|
sys.exit(102)""")
|
||||||
pid = posix.posix_spawn(
|
pid = self.spawn_func(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
[sys.executable, '-c', code],
|
[sys.executable, '-c', code],
|
||||||
os.environ,
|
os.environ,
|
||||||
|
@ -1704,40 +1704,40 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
(os.POSIX_SPAWN_CLOSE, 0),
|
(os.POSIX_SPAWN_CLOSE, 0),
|
||||||
(os.POSIX_SPAWN_DUP2, 1, 4),
|
(os.POSIX_SPAWN_DUP2, 1, 4),
|
||||||
]
|
]
|
||||||
pid = posix.posix_spawn(self.NOOP_PROGRAM[0],
|
pid = self.spawn_func(self.NOOP_PROGRAM[0],
|
||||||
self.NOOP_PROGRAM,
|
self.NOOP_PROGRAM,
|
||||||
os.environ,
|
os.environ,
|
||||||
file_actions=file_actions)
|
file_actions=file_actions)
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
|
|
||||||
def test_bad_file_actions(self):
|
def test_bad_file_actions(self):
|
||||||
args = self.NOOP_PROGRAM
|
args = self.NOOP_PROGRAM
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[None])
|
file_actions=[None])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[()])
|
file_actions=[()])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(None,)])
|
file_actions=[(None,)])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(12345,)])
|
file_actions=[(12345,)])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(os.POSIX_SPAWN_CLOSE,)])
|
file_actions=[(os.POSIX_SPAWN_CLOSE,)])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)])
|
file_actions=[(os.POSIX_SPAWN_CLOSE, 1, 2)])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(os.POSIX_SPAWN_CLOSE, None)])
|
file_actions=[(os.POSIX_SPAWN_CLOSE, None)])
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
posix.posix_spawn(args[0], args, os.environ,
|
self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(os.POSIX_SPAWN_OPEN,
|
file_actions=[(os.POSIX_SPAWN_OPEN,
|
||||||
3, __file__ + '\0',
|
3, __file__ + '\0',
|
||||||
os.O_RDONLY, 0)])
|
os.O_RDONLY, 0)])
|
||||||
|
|
||||||
def test_open_file(self):
|
def test_open_file(self):
|
||||||
outfile = support.TESTFN
|
outfile = support.TESTFN
|
||||||
|
@ -1752,8 +1752,8 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
stat.S_IRUSR | stat.S_IWUSR),
|
stat.S_IRUSR | stat.S_IWUSR),
|
||||||
]
|
]
|
||||||
args = self.python_args('-c', script)
|
args = self.python_args('-c', script)
|
||||||
pid = posix.posix_spawn(args[0], args, os.environ,
|
pid = self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=file_actions)
|
file_actions=file_actions)
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
with open(outfile) as f:
|
with open(outfile) as f:
|
||||||
self.assertEqual(f.read(), 'hello')
|
self.assertEqual(f.read(), 'hello')
|
||||||
|
@ -1770,8 +1770,8 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
closefile.write('is closed %d' % e.errno)
|
closefile.write('is closed %d' % e.errno)
|
||||||
"""
|
"""
|
||||||
args = self.python_args('-c', script)
|
args = self.python_args('-c', script)
|
||||||
pid = posix.posix_spawn(args[0], args, os.environ,
|
pid = self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=[(os.POSIX_SPAWN_CLOSE, 0),])
|
file_actions=[(os.POSIX_SPAWN_CLOSE, 0)])
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
with open(closefile) as f:
|
with open(closefile) as f:
|
||||||
self.assertEqual(f.read(), 'is closed %d' % errno.EBADF)
|
self.assertEqual(f.read(), 'is closed %d' % errno.EBADF)
|
||||||
|
@ -1788,16 +1788,64 @@ class TestPosixSpawn(unittest.TestCase):
|
||||||
(os.POSIX_SPAWN_DUP2, childfile.fileno(), 1),
|
(os.POSIX_SPAWN_DUP2, childfile.fileno(), 1),
|
||||||
]
|
]
|
||||||
args = self.python_args('-c', script)
|
args = self.python_args('-c', script)
|
||||||
pid = posix.posix_spawn(args[0], args, os.environ,
|
pid = self.spawn_func(args[0], args, os.environ,
|
||||||
file_actions=file_actions)
|
file_actions=file_actions)
|
||||||
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
|
||||||
with open(dupfile) as f:
|
with open(dupfile) as f:
|
||||||
self.assertEqual(f.read(), 'hello')
|
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():
|
def test_main():
|
||||||
try:
|
try:
|
||||||
support.run_unittest(PosixTester, PosixGroupsTester, TestPosixSpawn)
|
support.run_unittest(
|
||||||
|
PosixTester,
|
||||||
|
PosixGroupsTester,
|
||||||
|
TestPosixSpawn,
|
||||||
|
TestPosixSpawnP,
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
support.reap_children()
|
support.reap_children()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add a new :func:`os.posix_spawnp` function.
|
||||||
|
Patch by Joannah Nanjekye.
|
|
@ -1791,6 +1791,75 @@ exit:
|
||||||
|
|
||||||
#endif /* defined(HAVE_POSIX_SPAWN) */
|
#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))
|
#if (defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV))
|
||||||
|
|
||||||
PyDoc_STRVAR(os_spawnv__doc__,
|
PyDoc_STRVAR(os_spawnv__doc__,
|
||||||
|
@ -6851,6 +6920,10 @@ exit:
|
||||||
#define OS_POSIX_SPAWN_METHODDEF
|
#define OS_POSIX_SPAWN_METHODDEF
|
||||||
#endif /* !defined(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
|
#ifndef OS_SPAWNV_METHODDEF
|
||||||
#define OS_SPAWNV_METHODDEF
|
#define OS_SPAWNV_METHODDEF
|
||||||
#endif /* !defined(OS_SPAWNV_METHODDEF) */
|
#endif /* !defined(OS_SPAWNV_METHODDEF) */
|
||||||
|
@ -7258,4 +7331,4 @@ exit:
|
||||||
#ifndef OS_GETRANDOM_METHODDEF
|
#ifndef OS_GETRANDOM_METHODDEF
|
||||||
#define OS_GETRANDOM_METHODDEF
|
#define OS_GETRANDOM_METHODDEF
|
||||||
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
|
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
|
||||||
/*[clinic end generated code: output=febc1e16c9024e40 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=dabd0fa27bf87044 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -5381,39 +5381,12 @@ fail:
|
||||||
return -1;
|
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 *
|
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 *env, PyObject *file_actions,
|
||||||
PyObject *setpgroup, int resetids, PyObject *setsigmask,
|
PyObject *setpgroup, int resetids, PyObject *setsigmask,
|
||||||
PyObject *setsigdef, PyObject *scheduler)
|
PyObject *setsigdef, PyObject *scheduler)
|
||||||
/*[clinic end generated code: output=45dfa4c515d09f2c input=2891c2f1d457e39b]*/
|
|
||||||
{
|
{
|
||||||
EXECV_CHAR **argvlist = NULL;
|
EXECV_CHAR **argvlist = NULL;
|
||||||
EXECV_CHAR **envlist = NULL;
|
EXECV_CHAR **envlist = NULL;
|
||||||
|
@ -5489,9 +5462,19 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
|
||||||
attrp = &attr;
|
attrp = &attr;
|
||||||
|
|
||||||
_Py_BEGIN_SUPPRESS_IPH
|
_Py_BEGIN_SUPPRESS_IPH
|
||||||
err_code = posix_spawn(&pid, path->narrow,
|
#ifdef HAVE_POSIX_SPAWNP
|
||||||
file_actionsp, attrp, argvlist, envlist);
|
if (use_posix_spawnp) {
|
||||||
|
err_code = posix_spawnp(&pid, path->narrow,
|
||||||
|
file_actionsp, attrp, argvlist, envlist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif /* HAVE_POSIX_SPAWNP */
|
||||||
|
{
|
||||||
|
err_code = posix_spawn(&pid, path->narrow,
|
||||||
|
file_actionsp, attrp, argvlist, envlist);
|
||||||
|
}
|
||||||
_Py_END_SUPPRESS_IPH
|
_Py_END_SUPPRESS_IPH
|
||||||
|
|
||||||
if (err_code) {
|
if (err_code) {
|
||||||
errno = err_code;
|
errno = err_code;
|
||||||
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
|
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
|
||||||
|
@ -5518,7 +5501,90 @@ exit:
|
||||||
Py_XDECREF(temp_buffer);
|
Py_XDECREF(temp_buffer);
|
||||||
return result;
|
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)
|
#if defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV)
|
||||||
|
@ -13084,6 +13150,7 @@ static PyMethodDef posix_methods[] = {
|
||||||
OS_GETPRIORITY_METHODDEF
|
OS_GETPRIORITY_METHODDEF
|
||||||
OS_SETPRIORITY_METHODDEF
|
OS_SETPRIORITY_METHODDEF
|
||||||
OS_POSIX_SPAWN_METHODDEF
|
OS_POSIX_SPAWN_METHODDEF
|
||||||
|
OS_POSIX_SPAWNP_METHODDEF
|
||||||
OS_READLINK_METHODDEF
|
OS_READLINK_METHODDEF
|
||||||
OS_RENAME_METHODDEF
|
OS_RENAME_METHODDEF
|
||||||
OS_REPLACE_METHODDEF
|
OS_REPLACE_METHODDEF
|
||||||
|
|
|
@ -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
|
# This file is free software; the Free Software Foundation
|
||||||
# gives unlimited permission to copy and/or distribute it,
|
# 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($@)])])
|
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 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
|
||||||
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||||
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
|
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
|
||||||
|
@ -55,7 +55,7 @@ dnl
|
||||||
dnl See the "Since" comment for each macro you use to see what version
|
dnl See the "Since" comment for each macro you use to see what version
|
||||||
dnl of the macros you require.
|
dnl of the macros you require.
|
||||||
m4_defun([PKG_PREREQ],
|
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_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
|
||||||
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
||||||
])dnl PKG_PREREQ
|
])dnl PKG_PREREQ
|
||||||
|
|
|
@ -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 \
|
initgroups kill killpg lchown lockf linkat lstat lutimes mmap \
|
||||||
memrchr mbrtowc mkdirat mkfifo \
|
memrchr mbrtowc mkdirat mkfifo \
|
||||||
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \
|
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 \
|
pthread_init pthread_kill putenv pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \
|
||||||
sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
|
sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
|
||||||
setgid sethostname \
|
setgid sethostname \
|
||||||
|
|
|
@ -3505,7 +3505,7 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
|
||||||
initgroups kill killpg lchown lockf linkat lstat lutimes mmap \
|
initgroups kill killpg lchown lockf linkat lstat lutimes mmap \
|
||||||
memrchr mbrtowc mkdirat mkfifo \
|
memrchr mbrtowc mkdirat mkfifo \
|
||||||
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \
|
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 \
|
pthread_init pthread_kill putenv pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \
|
||||||
sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
|
sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
|
||||||
setgid sethostname \
|
setgid sethostname \
|
||||||
|
|
|
@ -732,6 +732,9 @@
|
||||||
/* Define to 1 if you have the `posix_spawn' function. */
|
/* Define to 1 if you have the `posix_spawn' function. */
|
||||||
#undef HAVE_POSIX_SPAWN
|
#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. */
|
/* Define to 1 if you have the `pread' function. */
|
||||||
#undef HAVE_PREAD
|
#undef HAVE_PREAD
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue