]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-92817: Fix precedence of options to py.exe launcher (GH-92988)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 19 May 2022 23:10:38 +0000 (16:10 -0700)
committerGitHub <noreply@github.com>
Thu, 19 May 2022 23:10:38 +0000 (16:10 -0700)
(cherry picked from commit 73473fdeac3ff9d75ac9d189bb8552b7459812f1)

Co-authored-by: Steve Dower <steve.dower@python.org>
Lib/test/test_launcher.py
Misc/NEWS.d/next/Windows/2022-05-19-21-44-25.gh-issue-92817.Jrf-Kv.rst [new file with mode: 0644]
PC/launcher2.c

index 2e10c55e339d5c45e2d7df8cddbbba9165506d16..aeacbbecbfa12ca08d9dbd10109743df978b404a 100644 (file)
@@ -244,6 +244,17 @@ class RunPyMixin:
         finally:
             file.unlink()
 
+    @contextlib.contextmanager
+    def test_venv(self):
+        venv = Path.cwd() / "Scripts"
+        venv.mkdir(exist_ok=True, parents=True)
+        venv_exe = (venv / Path(sys.executable).name)
+        venv_exe.touch()
+        try:
+            yield venv_exe, {"VIRTUAL_ENV": str(venv.parent)}
+        finally:
+            shutil.rmtree(venv)
+
 
 class TestLauncher(unittest.TestCase, RunPyMixin):
     @classmethod
@@ -451,12 +462,8 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
         self.assertEqual("PythonTestSuite/3.100", default)
 
     def test_virtualenv_in_list(self):
-        venv = Path.cwd() / "Scripts"
-        venv.mkdir(exist_ok=True, parents=True)
-        venv_exe = (venv / Path(sys.executable).name)
-        venv_exe.touch()
-        try:
-            data = self.run_py(["-0p"], env={"VIRTUAL_ENV": str(venv.parent)})
+        with self.test_venv() as (venv_exe, env):
+            data = self.run_py(["-0p"], env=env)
             for line in data["stdout"].splitlines():
                 m = re.match(r"\s*\*\s+(.+)$", line)
                 if m:
@@ -465,7 +472,7 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
             else:
                 self.fail("did not find active venv path")
 
-            data = self.run_py(["-0"], env={"VIRTUAL_ENV": str(venv.parent)})
+            data = self.run_py(["-0"], env=env)
             for line in data["stdout"].splitlines():
                 m = re.match(r"\s*\*\s+(.+)$", line)
                 if m:
@@ -473,8 +480,17 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
                     break
             else:
                 self.fail("did not find active venv entry")
-        finally:
-            shutil.rmtree(venv)
+
+    def test_virtualenv_with_env(self):
+        with self.test_venv() as (venv_exe, env):
+            data1 = self.run_py([], env={**env, "PY_PYTHON": "-3"})
+            data2 = self.run_py(["-3"], env={**env, "PY_PYTHON": "-3"})
+        # Compare stdout, because stderr goes via ascii
+        self.assertEqual(data1["stdout"].strip(), str(venv_exe))
+        self.assertEqual(data1["SearchInfo.lowPriorityTag"], "True")
+        # Ensure passing the argument doesn't trigger the same behaviour
+        self.assertNotEqual(data2["stdout"].strip(), str(venv_exe))
+        self.assertNotEqual(data2["SearchInfo.lowPriorityTag"], "True")
 
     def test_py_shebang(self):
         with self.py_ini(TEST_PY_COMMANDS):
diff --git a/Misc/NEWS.d/next/Windows/2022-05-19-21-44-25.gh-issue-92817.Jrf-Kv.rst b/Misc/NEWS.d/next/Windows/2022-05-19-21-44-25.gh-issue-92817.Jrf-Kv.rst
new file mode 100644 (file)
index 0000000..16acba1
--- /dev/null
@@ -0,0 +1,3 @@
+Ensures that :file:`py.exe` will prefer an active virtual environment over
+default tags specified with environment variables or through a
+:file:`py.ini` file.
index 35c932aa329c8e7a995cfebbb13c4faa365c0608..763bc138ed71d410acc3a99a01e66fc89e2f1012 100644 (file)
@@ -386,6 +386,12 @@ typedef struct {
     int tagLength;
     // if true, treats 'tag' as a non-PEP 514 filter
     bool oldStyleTag;
+    // if true, ignores 'tag' when a high priority environment is found
+    // gh-92817: This is currently set when a tag is read from configuration or
+    // the environment, rather than the command line or a shebang line, and the
+    // only currently possible high priority environment is an active virtual
+    // environment
+    bool lowPriorityTag;
     // if true, we had an old-style tag with '-64' suffix, and so do not
     // want to match tags like '3.x-32'
     bool exclude32Bit;
@@ -475,6 +481,7 @@ dumpSearchInfo(SearchInfo *search)
     DEBUG_2(company, companyLength);
     DEBUG_2(tag, tagLength);
     DEBUG_BOOL(oldStyleTag);
+    DEBUG_BOOL(lowPriorityTag);
     DEBUG_BOOL(exclude32Bit);
     DEBUG_BOOL(only32Bit);
     DEBUG_BOOL(allowDefaults);
@@ -965,6 +972,9 @@ checkDefaults(SearchInfo *search)
         if (!slash) {
             search->tag = tag;
             search->tagLength = n;
+            // gh-92817: allow a high priority env to be selected even if it
+            // doesn't match the tag
+            search->lowPriorityTag = true;
         } else {
             search->company = tag;
             search->companyLength = (int)(slash - tag);
@@ -995,7 +1005,7 @@ typedef struct EnvironmentInfo {
     const wchar_t *executableArgs;
     const wchar_t *architecture;
     const wchar_t *displayName;
-    bool isActiveVenv;
+    bool highPriority;
 } EnvironmentInfo;
 
 
@@ -1481,7 +1491,7 @@ virtualenvSearch(const SearchInfo *search, EnvironmentInfo **result)
     if (!env) {
         return RC_NO_MEMORY;
     }
-    env->isActiveVenv = true;
+    env->highPriority = true;
     env->internalSortKey = 20;
     exitCode = copyWstr(&env->displayName, L"Active venv");
     if (exitCode) {
@@ -1821,6 +1831,15 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
             return 0;
         }
 
+        if (env->highPriority && search->lowPriorityTag) {
+            // This environment is marked high priority, and the search allows
+            // it to be selected even though a tag is specified, so select it
+            // gh-92817: this allows an active venv to be selected even when a
+            // default tag has been found in py.ini or the environment
+            *best = env;
+            return 0;
+        }
+
         if (!search->oldStyleTag) {
             if (_companyMatches(search, env) && _tagMatches(search, env)) {
                 // Because of how our sort tree is set up, we will walk up the