From d3aa5f689cddf6d58c2b988fd74e8bff2273e426 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:38:28 +0000 Subject: [PATCH 1/5] gh-140741: Fix `profiling.sampling` handling of error raised by target (#140745) --- Lib/profiling/sampling/_sync_coordinator.py | 9 +++++---- .../test_profiling/test_sampling_profiler.py | 16 ++++++++++++++++ ...025-10-29-09-40-10.gh-issue-140741.L13UCV.rst | 2 ++ 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-29-09-40-10.gh-issue-140741.L13UCV.rst diff --git a/Lib/profiling/sampling/_sync_coordinator.py b/Lib/profiling/sampling/_sync_coordinator.py index 79e8858ca17529..8716e654104791 100644 --- a/Lib/profiling/sampling/_sync_coordinator.py +++ b/Lib/profiling/sampling/_sync_coordinator.py @@ -175,14 +175,15 @@ def _execute_script(script_path: str, script_args: List[str], cwd: str) -> None: try: with open(script_path, 'rb') as f: source_code = f.read() - - # Compile and execute the script - code = compile(source_code, script_path, 'exec') - exec(code, {'__name__': '__main__', '__file__': script_path}) except FileNotFoundError as e: raise TargetError(f"Script file not found: {script_path}") from e except PermissionError as e: raise TargetError(f"Permission denied reading script: {script_path}") from e + + try: + # Compile and execute the script + code = compile(source_code, script_path, 'exec') + exec(code, {'__name__': '__main__', '__file__': script_path}) except SyntaxError as e: raise TargetError(f"Syntax error in script {script_path}: {e}") from e except SystemExit: diff --git a/Lib/test/test_profiling/test_sampling_profiler.py b/Lib/test/test_profiling/test_sampling_profiler.py index 59bc18b9bcf14d..cbfb21d3512eee 100644 --- a/Lib/test/test_profiling/test_sampling_profiler.py +++ b/Lib/test/test_profiling/test_sampling_profiler.py @@ -2080,6 +2080,22 @@ def test_valid_output_formats(self): # Expected errors - we just want to test format validation pass + def test_script_error_treatment(self): + script_file = tempfile.NamedTemporaryFile("w", delete=False, suffix=".py") + script_file.write("open('nonexistent_file.txt')\n") + script_file.close() + self.addCleanup(os.unlink, script_file.name) + + result = subprocess.run( + [sys.executable, "-m", "profiling.sampling.sample", "-d", "1", script_file.name], + capture_output=True, + text=True, + ) + output = result.stdout + result.stderr + + self.assertNotIn("Script file not found", output) + self.assertIn("No such file or directory: 'nonexistent_file.txt'", output) + class TestSampleProfilerCLI(unittest.TestCase): def _setup_sync_mocks(self, mock_socket, mock_popen): diff --git a/Misc/NEWS.d/next/Library/2025-10-29-09-40-10.gh-issue-140741.L13UCV.rst b/Misc/NEWS.d/next/Library/2025-10-29-09-40-10.gh-issue-140741.L13UCV.rst new file mode 100644 index 00000000000000..9fa8c561a03c78 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-29-09-40-10.gh-issue-140741.L13UCV.rst @@ -0,0 +1,2 @@ +Fix :func:`profiling.sampling.sample` incorrectly handling a +:exc:`FileNotFoundError` or :exc:`PermissionError`. From 376559bf54762d7b6e0a14591a1f284c8f09a855 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 29 Oct 2025 13:29:10 +0100 Subject: [PATCH 2/5] gh-140702: Log color and GHA env vars in test.pythoninfo (#140747) --- Lib/test/pythoninfo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index e8718decf6d6e9..3befc0f2e633ca 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -291,6 +291,8 @@ def format_groups(groups): "DISTUTILS_USE_SDK", "DYLD_LIBRARY_PATH", "ENSUREPIP_OPTIONS", + "FORCE_COLOR", + "GITHUB_ACTIONS", "HISTORY_FILE", "HOME", "HOMEDRIVE", @@ -307,11 +309,13 @@ def format_groups(groups): "MAKEFLAGS", "MIXERDEV", "MSSDK", + "NO_COLOR", "PATH", "PATHEXT", "PIP_CONFIG_FILE", "PLAT", "POSIXLY_CORRECT", + "PYTHON_COLORS", "PY_SAX_PARSER", "ProgramFiles", "ProgramFiles(x86)", From 6ff62ac4fbc3f17529823c146cc3a3f8c21eed42 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Wed, 29 Oct 2025 05:33:44 -0700 Subject: [PATCH 3/5] gh-140082: Forward colorizing from libregrtest to unittest (#140083) libregrtest redirects test output to a file as part of its operation. When `unittest` checks to see if it should colorize with `isatty(sys.stdout)` that fails resulting in no colorizing of the unittest output. Update `libregrtest` to set `FORCE_COLOR=1` when redirecting test output so that unittest will do color printing. Co-authored-by: Victor Stinner --- Lib/test/libregrtest/setup.py | 8 ++++++++ Lib/test/libregrtest/worker.py | 7 +++++++ .../Tests/2025-10-15-00-52-12.gh-issue-140082.fpET50.rst | 3 +++ 3 files changed, 18 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2025-10-15-00-52-12.gh-issue-140082.fpET50.rst diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 9bfc414cd615c8..b9b76a44e3b4e7 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -8,6 +8,7 @@ import unittest from test import support from test.support.os_helper import TESTFN_UNDECODABLE, FS_NONASCII +from _colorize import can_colorize # type: ignore[import-not-found] from .filter import set_match_tests from .runtests import RunTests @@ -139,3 +140,10 @@ def setup_tests(runtests: RunTests) -> None: gc.set_threshold(runtests.gc_threshold) random.seed(runtests.random_seed) + + # sys.stdout is redirected to a StringIO in single process mode on which + # color auto-detect fails as StringIO is not a TTY. If the original + # sys.stdout supports color pass that through with FORCE_COLOR so that when + # results are printed, such as with -W, they get color. + if can_colorize(file=sys.stdout): + os.environ['FORCE_COLOR'] = "1" diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py index 5d75bf7ae787ed..1ad67e1cebf288 100644 --- a/Lib/test/libregrtest/worker.py +++ b/Lib/test/libregrtest/worker.py @@ -1,6 +1,7 @@ import subprocess import sys import os +from _colorize import can_colorize # type: ignore[import-not-found] from typing import Any, NoReturn from test.support import os_helper, Py_DEBUG @@ -32,6 +33,12 @@ def create_worker_process(runtests: WorkerRunTests, output_fd: int, env['TEMP'] = tmp_dir env['TMP'] = tmp_dir + # The subcommand is run with a temporary output which means it is not a TTY + # and won't auto-color. The test results are printed to stdout so if we can + # color that have the subprocess use color. + if can_colorize(file=sys.stdout): + env['FORCE_COLOR'] = '1' + # Running the child from the same working directory as regrtest's original # invocation ensures that TEMPDIR for the child is the same when # sysconfig.is_python_build() is true. See issue 15300. diff --git a/Misc/NEWS.d/next/Tests/2025-10-15-00-52-12.gh-issue-140082.fpET50.rst b/Misc/NEWS.d/next/Tests/2025-10-15-00-52-12.gh-issue-140082.fpET50.rst new file mode 100644 index 00000000000000..70e70218254488 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-10-15-00-52-12.gh-issue-140082.fpET50.rst @@ -0,0 +1,3 @@ +Update ``python -m test`` to set ``FORCE_COLOR=1`` when being run with color +enabled so that :mod:`unittest` which is run by it with redirected output will +output in color. From 8706167474e9a625e5f6613d3c7ac77a62faff58 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 29 Oct 2025 16:27:35 +0300 Subject: [PATCH 4/5] gh-140576: Fixed crash produced by lexer in case of dedented zero byte (#140583) --- Lib/test/test_tokenize.py | 1 + .../2025-10-25-17-36-46.gh-issue-140576.kj0SCY.rst | 2 ++ Parser/lexer/lexer.c | 3 +++ 3 files changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-25-17-36-46.gh-issue-140576.kj0SCY.rst diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index d274726eed2e65..ca67e381958757 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -3183,6 +3183,7 @@ def get_tokens(string): f'__{ x:d }__'""", + " a\n\x00", ]: with self.subTest(case=case): self.assertRaises(tokenize.TokenError, get_tokens, case) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-25-17-36-46.gh-issue-140576.kj0SCY.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-25-17-36-46.gh-issue-140576.kj0SCY.rst new file mode 100644 index 00000000000000..2c27525d9f782c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-25-17-36-46.gh-issue-140576.kj0SCY.rst @@ -0,0 +1,2 @@ +Fixed crash in :func:`tokenize.generate_tokens` in case of +specific incorrect input. Patch by Mikhail Efimov. diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index a69994e9b3d005..7f25afec302c22 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -539,6 +539,9 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t return MAKE_TOKEN(ERRORTOKEN); } } + else if (c == EOF && PyErr_Occurred()) { + return MAKE_TOKEN(ERRORTOKEN); + } else { break; } From c74793c450249b90dd8aa01889a33b3d236c6aa9 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 29 Oct 2025 16:10:58 +0000 Subject: [PATCH 5/5] gh-140741: Fix test added in gh-140745 (#140760) Commit --- Lib/test/test_profiling/test_sampling_profiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_profiling/test_sampling_profiler.py b/Lib/test/test_profiling/test_sampling_profiler.py index cbfb21d3512eee..5b924cb24531b6 100644 --- a/Lib/test/test_profiling/test_sampling_profiler.py +++ b/Lib/test/test_profiling/test_sampling_profiler.py @@ -2093,6 +2093,8 @@ def test_script_error_treatment(self): ) output = result.stdout + result.stderr + if "PermissionError" in output: + self.skipTest("Insufficient permissions for remote profiling") self.assertNotIn("Script file not found", output) self.assertIn("No such file or directory: 'nonexistent_file.txt'", output)