mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-35674: Add os.posix_spawnp() (GH-11554)
Add a new os.posix_spawnp() function.
This commit is contained in:
parent
9daecf37a5
commit
92b8322e7e
9 changed files with 322 additions and 112 deletions
|
@ -1489,10 +1489,10 @@ class PosixGroupsTester(unittest.TestCase):
|
|||
self.assertListEqual(groups, posix.getgroups())
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn")
|
||||
class TestPosixSpawn(unittest.TestCase):
|
||||
# Program which does nothing and exit with status 0 (success)
|
||||
class _PosixSpawnMixin:
|
||||
# Program which does nothing and exits 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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
|
||||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
|
||||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
|
||||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
|
||||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
(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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
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 @@ class TestPosixSpawn(unittest.TestCase):
|
|||
(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()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue