From 329afe78c3bbc234492a53f7a4084d07e215a077 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 May 2022 03:41:24 +0200 Subject: [PATCH] gh-57684: Update tests for PYTHONSAFEPATH=1 (#92358) Fix tests failing with the PYTHONSAFEPATH=1 env var. Enhance also -P help in Python usage (python --help). --- Lib/distutils/tests/test_bdist_rpm.py | 6 ++++++ Lib/test/test_cmd_line.py | 5 ++++- Lib/test/test_cmd_line_script.py | 4 +++- Lib/test/test_pdb.py | 2 ++ Lib/test/test_runpy.py | 22 ++++++++++++---------- Lib/unittest/test/test_program.py | 4 +++- Python/initconfig.c | 2 +- Tools/freeze/test/freeze.py | 3 ++- 8 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Lib/distutils/tests/test_bdist_rpm.py b/Lib/distutils/tests/test_bdist_rpm.py index f1eb9ba4493..7eefa7b9cad 100644 --- a/Lib/distutils/tests/test_bdist_rpm.py +++ b/Lib/distutils/tests/test_bdist_rpm.py @@ -49,6 +49,9 @@ def tearDown(self): 'the rpm command is not found') @unittest.skipIf(find_executable('rpmbuild') is None, 'the rpmbuild command is not found') + # import foo fails with safe path + @unittest.skipIf(sys.flags.safe_path, + 'PYTHONSAFEPATH changes default sys.path') def test_quiet(self): # let's create a package tmp_dir = self.mkdtemp() @@ -93,6 +96,9 @@ def test_quiet(self): 'the rpm command is not found') @unittest.skipIf(find_executable('rpmbuild') is None, 'the rpmbuild command is not found') + # import foo fails with safe path + @unittest.skipIf(sys.flags.safe_path, + 'PYTHONSAFEPATH changes default sys.path') def test_no_optimize_flag(self): # let's create a package that breaks bdist_rpm tmp_dir = self.mkdtemp() diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 26506312185..5a9e14bbd32 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -363,6 +363,8 @@ def test_large_PYTHONPATH(self): self.assertIn(path1.encode('ascii'), out) self.assertIn(path2.encode('ascii'), out) + @unittest.skipIf(sys.flags.safe_path, + 'PYTHONSAFEPATH changes default sys.path') def test_empty_PYTHONPATH_issue16309(self): # On Posix, it is documented that setting PATH to the # empty string is equivalent to not setting PATH at all, @@ -594,9 +596,10 @@ def test_isolatedmode(self): with open(main, "w", encoding="utf-8") as f: f.write("import uuid\n") f.write("print('ok')\n") + # Use -E to ignore PYTHONSAFEPATH env var self.assertRaises(subprocess.CalledProcessError, subprocess.check_output, - [sys.executable, main], cwd=tmpdir, + [sys.executable, '-E', main], cwd=tmpdir, stderr=subprocess.DEVNULL) out = subprocess.check_output([sys.executable, "-I", main], cwd=tmpdir) diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 9fe748c1870..bb433dc1e73 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -116,7 +116,9 @@ def _check_output(self, script_name, exit_code, data, self.assertIn(printed_file.encode('utf-8'), data) self.assertIn(printed_package.encode('utf-8'), data) self.assertIn(printed_argv0.encode('utf-8'), data) - self.assertIn(printed_path0.encode('utf-8'), data) + # PYTHONSAFEPATH=1 changes the default sys.path[0] + if not sys.flags.safe_path: + self.assertIn(printed_path0.encode('utf-8'), data) self.assertIn(printed_cwd.encode('utf-8'), data) def _check_script(self, script_exec_args, expected_file, diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index bfa2cc92d25..0141b739c25 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1369,6 +1369,8 @@ class PdbTestCase(unittest.TestCase): def tearDown(self): os_helper.unlink(os_helper.TESTFN) + @unittest.skipIf(sys.flags.safe_path, + 'PYTHONSAFEPATH changes default sys.path') def _run_pdb(self, pdb_args, commands): self.addCleanup(os_helper.rmtree, '__pycache__') cmd = [sys.executable, '-m', 'pdb'] + pdb_args diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 93e1ab556ae..6aaa288c14e 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -781,13 +781,15 @@ def run(self, *args, **kwargs): super().run(*args, **kwargs) @requires_subprocess() - def assertSigInt(self, *args, **kwargs): - proc = subprocess.run(*args, **kwargs, text=True, stderr=subprocess.PIPE) - self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n")) + def assertSigInt(self, cmd, *args, **kwargs): + # Use -E to ignore PYTHONSAFEPATH + cmd = [sys.executable, '-E', *cmd] + proc = subprocess.run(cmd, *args, **kwargs, text=True, stderr=subprocess.PIPE) + self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n"), proc.stderr) self.assertEqual(proc.returncode, self.EXPECTED_CODE) def test_pymain_run_file(self): - self.assertSigInt([sys.executable, self.ham]) + self.assertSigInt([self.ham]) def test_pymain_run_file_runpy_run_module(self): tmp = self.ham.parent @@ -800,7 +802,7 @@ def test_pymain_run_file_runpy_run_module(self): """ ) ) - self.assertSigInt([sys.executable, run_module], cwd=tmp) + self.assertSigInt([run_module], cwd=tmp) def test_pymain_run_file_runpy_run_module_as_main(self): tmp = self.ham.parent @@ -813,23 +815,23 @@ def test_pymain_run_file_runpy_run_module_as_main(self): """ ) ) - self.assertSigInt([sys.executable, run_module_as_main], cwd=tmp) + self.assertSigInt([run_module_as_main], cwd=tmp) def test_pymain_run_command_run_module(self): self.assertSigInt( - [sys.executable, "-c", "import runpy; runpy.run_module('ham')"], + ["-c", "import runpy; runpy.run_module('ham')"], cwd=self.ham.parent, ) def test_pymain_run_command(self): - self.assertSigInt([sys.executable, "-c", "import ham"], cwd=self.ham.parent) + self.assertSigInt(["-c", "import ham"], cwd=self.ham.parent) def test_pymain_run_stdin(self): - self.assertSigInt([sys.executable], input="import ham", cwd=self.ham.parent) + self.assertSigInt([], input="import ham", cwd=self.ham.parent) def test_pymain_run_module(self): ham = self.ham - self.assertSigInt([sys.executable, "-m", ham.stem], cwd=ham.parent) + self.assertSigInt(["-m", ham.stem], cwd=ham.parent) if __name__ == "__main__": diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py index 126497aa566..26a8550af8f 100644 --- a/Lib/unittest/test/test_program.py +++ b/Lib/unittest/test/test_program.py @@ -454,7 +454,9 @@ def testParseArgsSelectedTestNames(self): def testSelectedTestNamesFunctionalTest(self): def run_unittest(args): - p = subprocess.Popen([sys.executable, '-m', 'unittest'] + args, + # Use -E to ignore PYTHONSAFEPATH env var + cmd = [sys.executable, '-E', '-m', 'unittest'] + args + p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, cwd=os.path.dirname(__file__)) with p: _, stderr = p.communicate() diff --git a/Python/initconfig.c b/Python/initconfig.c index 265c7ca8e8f..a623973f953 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -49,7 +49,7 @@ static const char usage_2[] = "\ .pyc extension; also PYTHONOPTIMIZE=x\n\ -OO : do -O changes and also discard docstrings; add .opt-2 before\n\ .pyc extension\n\ --P : don't add sys.path[0]\n\ +-P : don't prepend a potentially unsafe path to sys.path\n\ -q : don't print version and copyright messages on interactive startup\n\ -s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\ -S : don't imply 'import site' on initialization\n\ diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index abedac03f91..ddbfd7fc9c2 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -172,7 +172,8 @@ def freeze(python, scriptfile, outdir): print(f'freezing {scriptfile}...') os.makedirs(outdir, exist_ok=True) - _run_quiet([python, FREEZE, '-o', outdir, scriptfile], outdir) + # Use -E to ignore PYTHONSAFEPATH + _run_quiet([python, '-E', FREEZE, '-o', outdir, scriptfile], outdir) _run_quiet([MAKE, '-C', os.path.dirname(scriptfile)]) name = os.path.basename(scriptfile).rpartition('.')[0]