Note: this PR was authored with assistance from Claude Code Opus 4.6
The launcher reads py.ini from%LOCALAPPDATA% viaSHGetFolderPathW, which is shared across all processes for a given user. When multiple test processes run concurrently on the same machine, one process'spy.ini writes leak into another process's launcher invocations, causing assertions like"PythonCore != PythonTestSuite" to fail.
Add aPYLAUNCHER_INIDIR environment variable to the launcher that, when set, redirectspy.ini lookup to the specified directory and skips the default locations. Updatetest_launcher.py to create an isolated temp directory per test class and pass it viaPYLAUNCHER_INIDIR.
I verified this fix on my Windows buildbot worker that's currently failing due to this issue.
PCbuild\amd64\python3.15t_d.exe -m test test_launcher -v
with this PR, all tests pass:
== CPython 3.15.0a6+ free-threading build (heads/windows-test-launcher-isolation:a8427d8c434, Feb 21 2026, 20:23:53) [MSC v.1950 64 bit (AMD64)]== Windows-2022Server-10.0.20348-SP0 little-endian== Python build: free_threading debug== cwd: C:\Users\Administrator\cpython\build\test_python_worker_10400æ== CPU count: 16== encodings: locale=cp1252 FS=utf-8== resources: all test resources are disabled, use -u option to unskip testsUsing random seed: 30742674890:00:00 Run 1 test sequentially in a single process0:00:00 [1/1] test_launchertest_filter_to_company (test.test_launcher.TestLauncher.test_filter_to_company) ... oktest_filter_to_company_and_tag (test.test_launcher.TestLauncher.test_filter_to_company_and_tag) ... oktest_filter_to_company_with_default (test.test_launcher.TestLauncher.test_filter_to_company_with_default) ... oktest_filter_to_tag (test.test_launcher.TestLauncher.test_filter_to_tag) ... oktest_filter_with_single_install (test.test_launcher.TestLauncher.test_filter_with_single_install) ... oktest_help_option (test.test_launcher.TestLauncher.test_help_option) ... oktest_install (test.test_launcher.TestLauncher.test_install) ... oktest_list (test.test_launcher.TestLauncher.test_list) ... oktest_list_option (test.test_launcher.TestLauncher.test_list_option) ... oktest_list_paths (test.test_launcher.TestLauncher.test_list_paths) ... oktest_literal_shebang_absolute (test.test_launcher.TestLauncher.test_literal_shebang_absolute) ... oktest_literal_shebang_command (test.test_launcher.TestLauncher.test_literal_shebang_command) ... oktest_literal_shebang_invalid_template (test.test_launcher.TestLauncher.test_literal_shebang_invalid_template) ... oktest_literal_shebang_quoted (test.test_launcher.TestLauncher.test_literal_shebang_quoted) ... oktest_literal_shebang_quoted_escape (test.test_launcher.TestLauncher.test_literal_shebang_quoted_escape) ... oktest_literal_shebang_relative (test.test_launcher.TestLauncher.test_literal_shebang_relative) ... oktest_py2_default (test.test_launcher.TestLauncher.test_py2_default) ... oktest_py2_default_env (test.test_launcher.TestLauncher.test_py2_default_env) ... oktest_py2_shebang (test.test_launcher.TestLauncher.test_py2_shebang) ... oktest_py2_shebang_nl (test.test_launcher.TestLauncher.test_py2_shebang_nl) ... oktest_py3_default (test.test_launcher.TestLauncher.test_py3_default) ... oktest_py3_default_env (test.test_launcher.TestLauncher.test_py3_default_env) ... oktest_py3_shebang (test.test_launcher.TestLauncher.test_py3_shebang) ... oktest_py3_shebang_nl (test.test_launcher.TestLauncher.test_py3_shebang_nl) ... oktest_py_default (test.test_launcher.TestLauncher.test_py_default) ... oktest_py_default_env (test.test_launcher.TestLauncher.test_py_default_env) ... oktest_py_default_in_list (test.test_launcher.TestLauncher.test_py_default_in_list) ... oktest_py_default_short_argv0 (test.test_launcher.TestLauncher.test_py_default_short_argv0) ... oktest_py_handle_64_in_ini (test.test_launcher.TestLauncher.test_py_handle_64_in_ini) ... oktest_py_shebang (test.test_launcher.TestLauncher.test_py_shebang) ... oktest_py_shebang_invalid_bom (test.test_launcher.TestLauncher.test_py_shebang_invalid_bom) ... oktest_py_shebang_nl (test.test_launcher.TestLauncher.test_py_shebang_nl) ... oktest_py_shebang_short_argv0 (test.test_launcher.TestLauncher.test_py_shebang_short_argv0) ... oktest_py_shebang_valid_bom (test.test_launcher.TestLauncher.test_py_shebang_valid_bom) ... oktest_python_shebang (test.test_launcher.TestLauncher.test_python_shebang) ... oktest_recursive_search_path (test.test_launcher.TestLauncher.test_recursive_search_path) ... oktest_search_major_2 (test.test_launcher.TestLauncher.test_search_major_2) ... skipped 'requires at least one Python 2.x install'test_search_major_3 (test.test_launcher.TestLauncher.test_search_major_3) ... oktest_search_major_3_32 (test.test_launcher.TestLauncher.test_search_major_3_32) ... skipped 'requires at least one 32-bit Python 3.x install'test_search_path (test.test_launcher.TestLauncher.test_search_path) ... oktest_search_path_exe (test.test_launcher.TestLauncher.test_search_path_exe) ... oktest_shebang_command_in_venv (test.test_launcher.TestLauncher.test_shebang_command_in_venv) ... oktest_shebang_executable_extension (test.test_launcher.TestLauncher.test_shebang_executable_extension) ... oktest_version (test.test_launcher.TestLauncher.test_version) ... oktest_virtualenv_in_list (test.test_launcher.TestLauncher.test_virtualenv_in_list) ... oktest_virtualenv_with_env (test.test_launcher.TestLauncher.test_virtualenv_with_env) ... ok----------------------------------------------------------------------Ran 46 tests in 1.353sOK (skipped=2)0:00:01 [1/1] test_launcher passed== Tests result: SUCCESS ==1 test OK.Total duration: 1.6 secTotal tests: run=46 skipped=2Total test files: run=1/1Result: SUCCESS
without it, 2 tests fail:
== CPython 3.15.0a6+ free-threading build (heads/main:c9380aebbe3, Feb 21 2026, 18:29:02) [MSC v.1950 64 bit (AMD64)]== Windows-2022Server-10.0.20348-SP0 little-endian== Python build: free_threading debug== cwd: C:\Users\Administrator\cpython\build\test_python_worker_4368æ== CPU count: 16== encodings: locale=cp1252 FS=utf-8== resources: all test resources are disabled, use -u option to unskip testsUsing random seed: 38668932470:00:00 Run 1 test sequentially in a single process0:00:00 [1/1] test_launchertest_filter_to_company (test.test_launcher.TestLauncher.test_filter_to_company) ... oktest_filter_to_company_and_tag (test.test_launcher.TestLauncher.test_filter_to_company_and_tag) ... oktest_filter_to_company_with_default (test.test_launcher.TestLauncher.test_filter_to_company_with_default) ... oktest_filter_to_tag (test.test_launcher.TestLauncher.test_filter_to_tag) ... oktest_filter_with_single_install (test.test_launcher.TestLauncher.test_filter_with_single_install) ... oktest_help_option (test.test_launcher.TestLauncher.test_help_option) ... oktest_install (test.test_launcher.TestLauncher.test_install) ... oktest_list (test.test_launcher.TestLauncher.test_list) ... oktest_list_option (test.test_launcher.TestLauncher.test_list_option) ... oktest_list_paths (test.test_launcher.TestLauncher.test_list_paths) ... oktest_literal_shebang_absolute (test.test_launcher.TestLauncher.test_literal_shebang_absolute) ... oktest_literal_shebang_command (test.test_launcher.TestLauncher.test_literal_shebang_command) ... oktest_literal_shebang_invalid_template (test.test_launcher.TestLauncher.test_literal_shebang_invalid_template) ... oktest_literal_shebang_quoted (test.test_launcher.TestLauncher.test_literal_shebang_quoted) ... oktest_literal_shebang_quoted_escape (test.test_launcher.TestLauncher.test_literal_shebang_quoted_escape) ... oktest_literal_shebang_relative (test.test_launcher.TestLauncher.test_literal_shebang_relative) ... oktest_py2_default (test.test_launcher.TestLauncher.test_py2_default) ... oktest_py2_default_env (test.test_launcher.TestLauncher.test_py2_default_env) ... oktest_py2_shebang (test.test_launcher.TestLauncher.test_py2_shebang) ... oktest_py2_shebang_nl (test.test_launcher.TestLauncher.test_py2_shebang_nl) ... oktest_py3_default (test.test_launcher.TestLauncher.test_py3_default) ... oktest_py3_default_env (test.test_launcher.TestLauncher.test_py3_default_env) ... oktest_py3_shebang (test.test_launcher.TestLauncher.test_py3_shebang) ... oktest_py3_shebang_nl (test.test_launcher.TestLauncher.test_py3_shebang_nl) ... oktest_py_default (test.test_launcher.TestLauncher.test_py_default) ... oktest_py_default_env (test.test_launcher.TestLauncher.test_py_default_env) ... oktest_py_default_in_list (test.test_launcher.TestLauncher.test_py_default_in_list) ... oktest_py_default_short_argv0 (test.test_launcher.TestLauncher.test_py_default_short_argv0) ... oktest_py_handle_64_in_ini (test.test_launcher.TestLauncher.test_py_handle_64_in_ini) ... oktest_py_shebang (test.test_launcher.TestLauncher.test_py_shebang) ... oktest_py_shebang_invalid_bom (test.test_launcher.TestLauncher.test_py_shebang_invalid_bom) ... oktest_py_shebang_nl (test.test_launcher.TestLauncher.test_py_shebang_nl) ... oktest_py_shebang_short_argv0 (test.test_launcher.TestLauncher.test_py_shebang_short_argv0) ... oktest_py_shebang_valid_bom (test.test_launcher.TestLauncher.test_py_shebang_valid_bom) ... oktest_python_shebang (test.test_launcher.TestLauncher.test_python_shebang) ... oktest_recursive_search_path (test.test_launcher.TestLauncher.test_recursive_search_path) ... oktest_search_major_2 (test.test_launcher.TestLauncher.test_search_major_2) ... FAILtest_search_major_3 (test.test_launcher.TestLauncher.test_search_major_3) ... FAILtest_search_major_3_32 (test.test_launcher.TestLauncher.test_search_major_3_32) ... skipped 'requires at least one 32-bit Python 3.x install'test_search_path (test.test_launcher.TestLauncher.test_search_path) ... oktest_search_path_exe (test.test_launcher.TestLauncher.test_search_path_exe) ... oktest_shebang_command_in_venv (test.test_launcher.TestLauncher.test_shebang_command_in_venv) ... oktest_shebang_executable_extension (test.test_launcher.TestLauncher.test_shebang_executable_extension) ... oktest_version (test.test_launcher.TestLauncher.test_version) ... oktest_virtualenv_in_list (test.test_launcher.TestLauncher.test_virtualenv_in_list) ... oktest_virtualenv_with_env (test.test_launcher.TestLauncher.test_virtualenv_with_env) ... ok======================================================================FAIL: test_search_major_2 (test.test_launcher.TestLauncher.test_search_major_2)----------------------------------------------------------------------Traceback (most recent call last): File "C:\Users\Administrator\cpython\Lib\test\test_launcher.py", line 465, in test_search_major_2 self.assertEqual("PythonCore", data["env.company"]) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^AssertionError: 'PythonCore' != 'PythonTestSuite'- PythonCore+ PythonTestSuite======================================================================FAIL: test_search_major_3 (test.test_launcher.TestLauncher.test_search_major_3)----------------------------------------------------------------------Traceback (most recent call last): File "C:\Users\Administrator\cpython\Lib\test\test_launcher.py", line 445, in test_search_major_3 self.assertEqual("PythonCore", data["env.company"]) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^AssertionError: 'PythonCore' != 'PythonTestSuite'- PythonCore+ PythonTestSuite----------------------------------------------------------------------Ran 46 tests in 1.302sFAILED (failures=2, skipped=1)test test_launcher failed0:00:01 [1/1/1] test_launcher failed (2 failures)== Tests result: FAILURE ==1 test failed: test_launcherTotal duration: 1.5 secTotal tests: run=46 failures=2 skipped=1Total test files: run=1/1 failed=1Result: FAILURE
Uh oh!
There was an error while loading.Please reload this page.
Note: this PR was authored with assistance from Claude Code Opus 4.6
The launcher reads py.ini from
%LOCALAPPDATA%viaSHGetFolderPathW, which is shared across all processes for a given user. When multiple test processes run concurrently on the same machine, one process'spy.iniwrites leak into another process's launcher invocations, causing assertions like"PythonCore != PythonTestSuite"to fail.Add a
PYLAUNCHER_INIDIRenvironment variable to the launcher that, when set, redirectspy.inilookup to the specified directory and skips the default locations. Updatetest_launcher.pyto create an isolated temp directory per test class and pass it viaPYLAUNCHER_INIDIR.I verified this fix on my Windows buildbot worker that's currently failing due to this issue.
with this PR, all tests pass:
without it, 2 tests fail: