]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-122941: Fix test_launcher sporadic failures via py.ini isolation (GH-145090)
authorItamar Oren <itamarost@gmail.com>
Wed, 4 Mar 2026 18:06:49 +0000 (10:06 -0800)
committerGitHub <noreply@github.com>
Wed, 4 Mar 2026 18:06:49 +0000 (18:06 +0000)
Adds _PYLAUNCHER_INIDIR as a private variable since the launcher is deprecated and not getting new features.

Lib/test/test_launcher.py
PC/launcher2.c

index caa1603c78eb019b6df77d50bd0f7b19ae06387f..c522bc1c2c093c7d69c56b56257493c5f9f07acc 100644 (file)
@@ -227,6 +227,8 @@ class RunPyMixin:
             "PYLAUNCHER_LIMIT_TO_COMPANY": "",
             **{k.upper(): v for k, v in (env or {}).items()},
         }
+        if ini_dir := getattr(self, '_ini_dir', None):
+            env.setdefault("_PYLAUNCHER_INIDIR", ini_dir)
         if not argv:
             argv = [self.py_exe, *args]
         with subprocess.Popen(
@@ -262,11 +264,14 @@ class RunPyMixin:
         return data
 
     def py_ini(self, content):
-        local_appdata = os.environ.get("LOCALAPPDATA")
-        if not local_appdata:
-            raise unittest.SkipTest("LOCALAPPDATA environment variable is "
-                                    "missing or empty")
-        return PreservePyIni(Path(local_appdata) / "py.ini", content)
+        ini_dir = getattr(self, '_ini_dir', None)
+        if not ini_dir:
+            local_appdata = os.environ.get("LOCALAPPDATA")
+            if not local_appdata:
+                raise unittest.SkipTest("LOCALAPPDATA environment variable is "
+                                        "missing or empty")
+            ini_dir = local_appdata
+        return PreservePyIni(Path(ini_dir) / "py.ini", content)
 
     @contextlib.contextmanager
     def script(self, content, encoding="utf-8"):
@@ -302,6 +307,8 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
             p = subprocess.check_output("reg query HKCU\\Software\\Python /s")
             #print(p.decode('mbcs'))
 
+        cls._ini_dir = tempfile.mkdtemp()
+        cls.addClassCleanup(shutil.rmtree, cls._ini_dir, ignore_errors=True)
 
     @classmethod
     def tearDownClass(cls):
index 832935c5cc6c1cf2fdbba1b7c32b44f53a62b3c5..4dd18c8eb5462e91107a517b4c4fe39749219e7c 100644 (file)
@@ -922,6 +922,20 @@ _readIni(const wchar_t *section, const wchar_t *settingName, wchar_t *buffer, in
 {
     wchar_t iniPath[MAXLEN];
     int n;
+    // Check for _PYLAUNCHER_INIDIR override (used for test isolation)
+    DWORD len = GetEnvironmentVariableW(L"_PYLAUNCHER_INIDIR", iniPath, MAXLEN);
+    if (len && len < MAXLEN) {
+        if (join(iniPath, MAXLEN, L"py.ini")) {
+            debug(L"# Reading from %s for %s/%s\n", iniPath, section, settingName);
+            n = GetPrivateProfileStringW(section, settingName, NULL, buffer, bufferLength, iniPath);
+            if (n) {
+                debug(L"# Found %s in %s\n", settingName, iniPath);
+                return n;
+            }
+        }
+        // When _PYLAUNCHER_INIDIR is set, skip the default locations
+        return 0;
+    }
     if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, iniPath)) &&
         join(iniPath, MAXLEN, L"py.ini")) {
         debug(L"# Reading from %s for %s/%s\n", iniPath, section, settingName);