summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorGregory P. Smith <greg@krypto.org>2022-05-05 23:22:32 (GMT)
committerGitHub <noreply@github.com>2022-05-05 23:22:32 (GMT)
commitf6dd14c65336cda4e2ebccbc6408dfe3b0a68a34 (patch)
tree75e0b51fc5787a7ba02cf1f0534b292fb9a0a528 /Lib
parent49fda0cc51c09e26d68431d5f86e11d923cf7b8e (diff)
downloadcpython-f6dd14c65336cda4e2ebccbc6408dfe3b0a68a34.zip
cpython-f6dd14c65336cda4e2ebccbc6408dfe3b0a68a34.tar.gz
cpython-f6dd14c65336cda4e2ebccbc6408dfe3b0a68a34.tar.bz2
gh-82616: Add process_group support to subprocess.Popen (#23930)
One more thing that can help prevent people from using `preexec_fn`. Also adds conditional skips to two tests exposing ASAN flakiness on the Ubuntu 20.04 Address Sanitizer Github CI system. When that build is run on more modern systems the "problem" does not show up. It seems ASAN implementation related. Co-authored-by: Zackery Spytz <zspytz@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/multiprocessing/util.py2
-rw-r--r--Lib/subprocess.py17
-rw-r--r--Lib/test/test_asyncio/test_subprocess.py3
-rw-r--r--Lib/test/test_capi.py6
-rw-r--r--Lib/test/test_distutils.py2
-rw-r--r--Lib/test/test_subprocess.py26
6 files changed, 43 insertions, 13 deletions
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index 6ad4632..6ee0d33 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -453,7 +453,7 @@ def spawnv_passfds(path, args, passfds):
return _posixsubprocess.fork_exec(
args, [path], True, passfds, None, None,
-1, -1, -1, -1, -1, -1, errpipe_read, errpipe_write,
- False, False, None, None, None, -1, None,
+ False, False, -1, None, None, None, -1, None,
subprocess._USE_VFORK)
finally:
os.close(errpipe_read)
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 968cfc1..9099822 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -769,6 +769,8 @@ class Popen:
start_new_session (POSIX only)
+ process_group (POSIX only)
+
group (POSIX only)
extra_groups (POSIX only)
@@ -794,7 +796,8 @@ class Popen:
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, user=None, group=None, extra_groups=None,
- encoding=None, errors=None, text=None, umask=-1, pipesize=-1):
+ encoding=None, errors=None, text=None, umask=-1, pipesize=-1,
+ process_group=None):
"""Create new Popen instance."""
_cleanup()
# Held while anything is calling waitpid before returncode has been
@@ -900,6 +903,9 @@ class Popen:
else:
line_buffering = False
+ if process_group is None:
+ process_group = -1 # The internal APIs are int-only
+
gid = None
if group is not None:
if not hasattr(os, 'setregid'):
@@ -1003,7 +1009,7 @@ class Popen:
errread, errwrite,
restore_signals,
gid, gids, uid, umask,
- start_new_session)
+ start_new_session, process_group)
except:
# Cleanup if the child failed starting.
for f in filter(None, (self.stdin, self.stdout, self.stderr)):
@@ -1387,7 +1393,7 @@ class Popen:
unused_restore_signals,
unused_gid, unused_gids, unused_uid,
unused_umask,
- unused_start_new_session):
+ unused_start_new_session, unused_process_group):
"""Execute program (MS Windows version)"""
assert not pass_fds, "pass_fds not supported on Windows."
@@ -1719,7 +1725,7 @@ class Popen:
errread, errwrite,
restore_signals,
gid, gids, uid, umask,
- start_new_session):
+ start_new_session, process_group):
"""Execute program (POSIX version)"""
if isinstance(args, (str, bytes)):
@@ -1755,6 +1761,7 @@ class Popen:
and (c2pwrite == -1 or c2pwrite > 2)
and (errwrite == -1 or errwrite > 2)
and not start_new_session
+ and process_group == -1
and gid is None
and gids is None
and uid is None
@@ -1812,7 +1819,7 @@ class Popen:
errread, errwrite,
errpipe_read, errpipe_write,
restore_signals, start_new_session,
- gid, gids, uid, umask,
+ process_group, gid, gids, uid, umask,
preexec_fn, _USE_VFORK)
self._child_created = True
finally:
diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py
index 14fa6dd..09a5c39 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -15,6 +15,9 @@ from test.support import os_helper
if sys.platform != 'win32':
from asyncio import unix_events
+if support.check_sanitizer(address=True):
+ raise unittest.SkipTest("Exposes ASAN flakiness in GitHub CI")
+
# Program blocking
PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)']
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index ab4caef..6d75895 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -140,7 +140,7 @@ class CAPITest(unittest.TestCase):
def __len__(self):
return 1
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
- 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
+ 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)
# Issue #15736: overflow in _PySequence_BytesToCharpArray()
class Z(object):
def __len__(self):
@@ -148,7 +148,7 @@ class CAPITest(unittest.TestCase):
def __getitem__(self, i):
return b'x'
self.assertRaises(MemoryError, _posixsubprocess.fork_exec,
- 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
+ 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)
@unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.')
def test_subprocess_fork_exec(self):
@@ -158,7 +158,7 @@ class CAPITest(unittest.TestCase):
# Issue #15738: crash in subprocess_fork_exec()
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
- Z(),[b'1'],3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
+ Z(),[b'1'],3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)
@unittest.skipIf(MISSING_C_DOCSTRINGS,
"Signature information for builtins requires docstrings")
diff --git a/Lib/test/test_distutils.py b/Lib/test/test_distutils.py
index d82d2b6..28320fb 100644
--- a/Lib/test/test_distutils.py
+++ b/Lib/test/test_distutils.py
@@ -23,6 +23,8 @@ def load_tests(*_):
def tearDownModule():
support.reap_children()
+if support.check_sanitizer(address=True):
+ raise unittest.SkipTest("Exposes ASAN flakiness in GitHub CI")
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 0764120..f685492 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1905,14 +1905,32 @@ class POSIXProcessTestCase(BaseTestCase):
output = subprocess.check_output(
[sys.executable, "-c", "import os; print(os.getsid(0))"],
start_new_session=True)
- except OSError as e:
+ except PermissionError as e:
if e.errno != errno.EPERM:
- raise
+ raise # EACCES?
else:
parent_sid = os.getsid(0)
child_sid = int(output)
self.assertNotEqual(parent_sid, child_sid)
+ @unittest.skipUnless(hasattr(os, 'setpgid') and hasattr(os, 'getpgid'),
+ 'no setpgid or getpgid on platform')
+ def test_process_group_0(self):
+ # For code coverage of calling setpgid(). We don't care if we get an
+ # EPERM error from it depending on the test execution environment, that
+ # still indicates that it was called.
+ try:
+ output = subprocess.check_output(
+ [sys.executable, "-c", "import os; print(os.getpgid(0))"],
+ process_group=0)
+ except PermissionError as e:
+ if e.errno != errno.EPERM:
+ raise # EACCES?
+ else:
+ parent_pgid = os.getpgid(0)
+ child_pgid = int(output)
+ self.assertNotEqual(parent_pgid, child_pgid)
+
@unittest.skipUnless(hasattr(os, 'setreuid'), 'no setreuid on platform')
def test_user(self):
# For code coverage of the user parameter. We don't care if we get an
@@ -3134,7 +3152,7 @@ class POSIXProcessTestCase(BaseTestCase):
True, (), cwd, env_list,
-1, -1, -1, -1,
1, 2, 3, 4,
- True, True,
+ True, True, 0,
False, [], 0, -1,
func, False)
# Attempt to prevent
@@ -3183,7 +3201,7 @@ class POSIXProcessTestCase(BaseTestCase):
True, fds_to_keep, None, [b"env"],
-1, -1, -1, -1,
1, 2, 3, 4,
- True, True,
+ True, True, 0,
None, None, None, -1,
None, "no vfork")
self.assertIn('fds_to_keep', str(c.exception))