From: Victor Stinner Date: Fri, 23 Nov 2018 18:02:26 +0000 (+0100) Subject: bpo-34812: subprocess._args_from_interpreter_flags(): add isolated (GH-10675) (GH... X-Git-Tag: v3.6.8rc1~84 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cc0e0a2214d6515cf6ba4c7b164902a87e321b45;p=thirdparty%2FPython%2Fcpython.git bpo-34812: subprocess._args_from_interpreter_flags(): add isolated (GH-10675) (GH-10688) The "-I" command line option (run Python in isolated mode) and -X options (like -X faulthandler) are now also copied by the multiprocessing and distutils modules when spawning child processes. Previously, only -E and -s options (enabled by -I) were copied. subprocess._args_from_interpreter_flags() now copies the -I flag and options from sys._xoptions like -X dev. (cherry picked from commit 9de363271519e0616f4a7b59427057c4810d3acc) --- diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 290ae44f03d7..8c3fa1bf9c8e 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -232,15 +232,13 @@ def _optim_args_from_interpreter_flags(): def _args_from_interpreter_flags(): """Return a list of command-line arguments reproducing the current - settings in sys.flags and sys.warnoptions.""" + settings in sys.flags, sys.warnoptions and sys._xoptions.""" flag_opt_map = { 'debug': 'd', # 'inspect': 'i', # 'interactive': 'i', 'dont_write_bytecode': 'B', - 'no_user_site': 's', 'no_site': 'S', - 'ignore_environment': 'E', 'verbose': 'v', 'bytes_warning': 'b', 'quiet': 'q', @@ -251,8 +249,30 @@ def _args_from_interpreter_flags(): v = getattr(sys.flags, flag) if v > 0: args.append('-' + opt * v) + + if sys.flags.isolated: + args.append('-I') + else: + if sys.flags.ignore_environment: + args.append('-E') + if sys.flags.no_user_site: + args.append('-s') + for opt in sys.warnoptions: args.append('-W' + opt) + + # -X options + xoptions = getattr(sys, '_xoptions', {}) + for opt in ('faulthandler', 'tracemalloc', + 'showalloccount', 'showrefcount', 'utf8'): + if opt in xoptions: + value = xoptions[opt] + if value is True: + arg = opt + else: + arg = '%s=%s' % (opt, value) + args.extend(('-X', arg)) + return args diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 45cdc94393eb..adc59b622998 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -1,13 +1,14 @@ +import errno import importlib +import os import shutil +import socket import stat +import subprocess import sys -import os -import unittest -import socket import tempfile import textwrap -import errno +import unittest from test import support from test.support import script_helper @@ -394,6 +395,65 @@ class TestSupport(unittest.TestCase): self.assertRaises(AssertionError, support.check__all__, self, unittest) + def check_options(self, args, func, expected=None): + code = f'from test.support import {func}; print(repr({func}()))' + cmd = [sys.executable, *args, '-c', code] + env = {key: value for key, value in os.environ.items() + if not key.startswith('PYTHON')} + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + universal_newlines=True, + env=env) + if expected is None: + expected = args + self.assertEqual(proc.stdout.rstrip(), repr(expected)) + self.assertEqual(proc.returncode, 0) + + def test_args_from_interpreter_flags(self): + # Test test.support.args_from_interpreter_flags() + for opts in ( + # no option + [], + # single option + ['-B'], + ['-s'], + ['-S'], + ['-E'], + ['-v'], + ['-b'], + ['-q'], + ['-I'], + # same option multiple times + ['-bb'], + ['-vvv'], + # -W options + ['-Wignore'], + # -X options + ['-X', 'faulthandler'], + ['-X', 'showalloccount'], + ['-X', 'showrefcount'], + ['-X', 'tracemalloc'], + ['-X', 'tracemalloc=3'], + ): + with self.subTest(opts=opts): + self.check_options(opts, 'args_from_interpreter_flags') + + self.check_options(['-I', '-E', '-s'], 'args_from_interpreter_flags', + ['-I']) + + def test_optim_args_from_interpreter_flags(self): + # Test test.support.optim_args_from_interpreter_flags() + for opts in ( + # no option + [], + ['-O'], + ['-OO'], + ['-OOOO'], + ): + with self.subTest(opts=opts): + self.check_options(opts, 'optim_args_from_interpreter_flags') + def test_match_test(self): class Test: def __init__(self, test_id): @@ -485,7 +545,6 @@ class TestSupport(unittest.TestCase): # reap_threads # reap_children # strip_python_stderr - # args_from_interpreter_flags # can_symlink # skip_unless_symlink # SuppressCrashReport diff --git a/Misc/NEWS.d/next/Security/2018-11-23-15-00-23.bpo-34812.84VQnb.rst b/Misc/NEWS.d/next/Security/2018-11-23-15-00-23.bpo-34812.84VQnb.rst new file mode 100644 index 000000000000..860404f019d2 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2018-11-23-15-00-23.bpo-34812.84VQnb.rst @@ -0,0 +1,4 @@ +The :option:`-I` command line option (run Python in isolated mode) is now +also copied by the :mod:`multiprocessing` and :mod:`distutils` modules when +spawning child processes. Previously, only :option:`-E` and :option:`-s` options +(enabled by :option:`-I`) were copied.