Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-92817: Fix precedence of options to py.exe launcher #92988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions Lib/test/test_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,17 @@ def script(self, content, encoding="utf-8"):
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
Expand Down Expand Up @@ -451,12 +462,8 @@ def test_py_default_in_list(self):
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:
Expand All @@ -465,16 +472,25 @@ def test_virtualenv_in_list(self):
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:
self.assertEqual("Active venv", m.group(1))
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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
23 changes: 21 additions & 2 deletions PC/launcher2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -995,7 +1005,7 @@ typedef struct EnvironmentInfo {
const wchar_t *executableArgs;
const wchar_t *architecture;
const wchar_t *displayName;
bool isActiveVenv;
bool highPriority;
} EnvironmentInfo;


Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down