Skip to content

Commit 73473fd

Browse files
authored
gh-92817: Fix precedence of options to py.exe launcher (GH-92988)
1 parent 38feffa commit 73473fd

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

Lib/test/test_launcher.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,17 @@ def script(self, content, encoding="utf-8"):
244244
finally:
245245
file.unlink()
246246

247+
@contextlib.contextmanager
248+
def test_venv(self):
249+
venv = Path.cwd() / "Scripts"
250+
venv.mkdir(exist_ok=True, parents=True)
251+
venv_exe = (venv / Path(sys.executable).name)
252+
venv_exe.touch()
253+
try:
254+
yield venv_exe, {"VIRTUAL_ENV": str(venv.parent)}
255+
finally:
256+
shutil.rmtree(venv)
257+
247258

248259
class TestLauncher(unittest.TestCase, RunPyMixin):
249260
@classmethod
@@ -451,12 +462,8 @@ def test_py_default_in_list(self):
451462
self.assertEqual("PythonTestSuite/3.100", default)
452463

453464
def test_virtualenv_in_list(self):
454-
venv = Path.cwd() / "Scripts"
455-
venv.mkdir(exist_ok=True, parents=True)
456-
venv_exe = (venv / Path(sys.executable).name)
457-
venv_exe.touch()
458-
try:
459-
data = self.run_py(["-0p"], env={"VIRTUAL_ENV": str(venv.parent)})
465+
with self.test_venv() as (venv_exe, env):
466+
data = self.run_py(["-0p"], env=env)
460467
for line in data["stdout"].splitlines():
461468
m = re.match(r"\s*\*\s+(.+)$", line)
462469
if m:
@@ -465,16 +472,25 @@ def test_virtualenv_in_list(self):
465472
else:
466473
self.fail("did not find active venv path")
467474

468-
data = self.run_py(["-0"], env={"VIRTUAL_ENV": str(venv.parent)})
475+
data = self.run_py(["-0"], env=env)
469476
for line in data["stdout"].splitlines():
470477
m = re.match(r"\s*\*\s+(.+)$", line)
471478
if m:
472479
self.assertEqual("Active venv", m.group(1))
473480
break
474481
else:
475482
self.fail("did not find active venv entry")
476-
finally:
477-
shutil.rmtree(venv)
483+
484+
def test_virtualenv_with_env(self):
485+
with self.test_venv() as (venv_exe, env):
486+
data1 = self.run_py([], env={**env, "PY_PYTHON": "-3"})
487+
data2 = self.run_py(["-3"], env={**env, "PY_PYTHON": "-3"})
488+
# Compare stdout, because stderr goes via ascii
489+
self.assertEqual(data1["stdout"].strip(), str(venv_exe))
490+
self.assertEqual(data1["SearchInfo.lowPriorityTag"], "True")
491+
# Ensure passing the argument doesn't trigger the same behaviour
492+
self.assertNotEqual(data2["stdout"].strip(), str(venv_exe))
493+
self.assertNotEqual(data2["SearchInfo.lowPriorityTag"], "True")
478494

479495
def test_py_shebang(self):
480496
with self.py_ini(TEST_PY_COMMANDS):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Ensures that :file:`py.exe` will prefer an active virtual environment over
2+
default tags specified with environment variables or through a
3+
:file:`py.ini` file.

PC/launcher2.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,12 @@ typedef struct {
386386
int tagLength;
387387
// if true, treats 'tag' as a non-PEP 514 filter
388388
bool oldStyleTag;
389+
// if true, ignores 'tag' when a high priority environment is found
390+
// gh-92817: This is currently set when a tag is read from configuration or
391+
// the environment, rather than the command line or a shebang line, and the
392+
// only currently possible high priority environment is an active virtual
393+
// environment
394+
bool lowPriorityTag;
389395
// if true, we had an old-style tag with '-64' suffix, and so do not
390396
// want to match tags like '3.x-32'
391397
bool exclude32Bit;
@@ -475,6 +481,7 @@ dumpSearchInfo(SearchInfo *search)
475481
DEBUG_2(company, companyLength);
476482
DEBUG_2(tag, tagLength);
477483
DEBUG_BOOL(oldStyleTag);
484+
DEBUG_BOOL(lowPriorityTag);
478485
DEBUG_BOOL(exclude32Bit);
479486
DEBUG_BOOL(only32Bit);
480487
DEBUG_BOOL(allowDefaults);
@@ -965,6 +972,9 @@ checkDefaults(SearchInfo *search)
965972
if (!slash) {
966973
search->tag = tag;
967974
search->tagLength = n;
975+
// gh-92817: allow a high priority env to be selected even if it
976+
// doesn't match the tag
977+
search->lowPriorityTag = true;
968978
} else {
969979
search->company = tag;
970980
search->companyLength = (int)(slash - tag);
@@ -995,7 +1005,7 @@ typedef struct EnvironmentInfo {
9951005
const wchar_t *executableArgs;
9961006
const wchar_t *architecture;
9971007
const wchar_t *displayName;
998-
bool isActiveVenv;
1008+
bool highPriority;
9991009
} EnvironmentInfo;
10001010

10011011

@@ -1481,7 +1491,7 @@ virtualenvSearch(const SearchInfo *search, EnvironmentInfo **result)
14811491
if (!env) {
14821492
return RC_NO_MEMORY;
14831493
}
1484-
env->isActiveVenv = true;
1494+
env->highPriority = true;
14851495
env->internalSortKey = 20;
14861496
exitCode = copyWstr(&env->displayName, L"Active venv");
14871497
if (exitCode) {
@@ -1821,6 +1831,15 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
18211831
return 0;
18221832
}
18231833

1834+
if (env->highPriority && search->lowPriorityTag) {
1835+
// This environment is marked high priority, and the search allows
1836+
// it to be selected even though a tag is specified, so select it
1837+
// gh-92817: this allows an active venv to be selected even when a
1838+
// default tag has been found in py.ini or the environment
1839+
*best = env;
1840+
return 0;
1841+
}
1842+
18241843
if (!search->oldStyleTag) {
18251844
if (_companyMatches(search, env) && _tagMatches(search, env)) {
18261845
// Because of how our sort tree is set up, we will walk up the

0 commit comments

Comments
 (0)