summaryrefslogtreecommitdiff
path: root/src/virtualenv
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2021-01-10 12:23:14 +0000
committerGitHub <noreply@github.com>2021-01-10 12:23:14 +0000
commit684a632c714b06a76bb6ddc217eaa0df7b40976f (patch)
tree1c7dde851d67b9619c606da89e740ace970389a2 /src/virtualenv
parent9201a757d0582a8181914dc7a9c863daa136a4b8 (diff)
downloadvirtualenv-684a632c714b06a76bb6ddc217eaa0df7b40976f.tar.gz
Improve discovery on Windows and provide escape hatchet (#2046)
Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
Diffstat (limited to 'src/virtualenv')
-rw-r--r--src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py19
-rw-r--r--src/virtualenv/discovery/builtin.py28
-rw-r--r--src/virtualenv/discovery/windows/__init__.py5
3 files changed, 41 insertions, 11 deletions
diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py
index 1938509..ad73eed 100644
--- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py
+++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py
@@ -55,20 +55,27 @@ class CPython3Windows(CPythonWindows, CPython3):
def sources(cls, interpreter):
for src in super(CPython3Windows, cls).sources(interpreter):
yield src
- if not cls.venv_37p(interpreter):
+ if not cls.has_shim(interpreter):
for src in cls.include_dll_and_pyd(interpreter):
yield src
- @staticmethod
- def venv_37p(interpreter):
- return interpreter.version_info.minor >= 7
+ @classmethod
+ def has_shim(cls, interpreter):
+ return interpreter.version_info.minor >= 7 and cls.shim(interpreter) is not None
+
+ @classmethod
+ def shim(cls, interpreter):
+ shim = Path(interpreter.system_stdlib) / "venv" / "scripts" / "nt" / "python.exe"
+ if shim.exists():
+ return shim
+ return None
@classmethod
def host_python(cls, interpreter):
- if cls.venv_37p(interpreter):
+ if cls.has_shim(interpreter):
# starting with CPython 3.7 Windows ships with a venvlauncher.exe that avoids the need for dll/pyd copies
# it also means the wrapper must be copied to avoid bugs such as https://bugs.python.org/issue42013
- return Path(interpreter.system_stdlib) / "venv" / "scripts" / "nt" / "python.exe"
+ return cls.shim(interpreter)
return super(CPython3Windows, cls).host_python(interpreter)
@classmethod
diff --git a/src/virtualenv/discovery/builtin.py b/src/virtualenv/discovery/builtin.py
index b66ecb1..70ffbce 100644
--- a/src/virtualenv/discovery/builtin.py
+++ b/src/virtualenv/discovery/builtin.py
@@ -17,6 +17,7 @@ class Builtin(Discover):
super(Builtin, self).__init__(options)
self.python_spec = options.python if options.python else [sys.executable]
self.app_data = options.app_data
+ self.try_first_with = options.try_first_with
@classmethod
def add_parser_arguments(cls, parser):
@@ -31,10 +32,19 @@ class Builtin(Discover):
help="interpreter based on what to create environment (path/identifier) "
"- by default use the interpreter where the tool is installed - first found wins",
)
+ parser.add_argument(
+ "--try-first-with",
+ dest="try_first_with",
+ metavar="py_exe",
+ type=str,
+ action="append",
+ default=[],
+ help="try first these interpreters before starting the discovery",
+ )
def run(self):
for python_spec in self.python_spec:
- result = get_interpreter(python_spec, self.app_data)
+ result = get_interpreter(python_spec, self.try_first_with, self.app_data)
if result is not None:
return result
return None
@@ -47,11 +57,11 @@ class Builtin(Discover):
return "{} discover of python_spec={!r}".format(self.__class__.__name__, spec)
-def get_interpreter(key, app_data=None):
+def get_interpreter(key, try_first_with, app_data=None):
spec = PythonSpec.from_string_spec(key)
logging.info("find interpreter for spec %r", spec)
proposed_paths = set()
- for interpreter, impl_must_match in propose_interpreters(spec, app_data):
+ for interpreter, impl_must_match in propose_interpreters(spec, try_first_with, app_data):
key = interpreter.system_executable, impl_must_match
if key in proposed_paths:
continue
@@ -62,7 +72,17 @@ def get_interpreter(key, app_data=None):
proposed_paths.add(key)
-def propose_interpreters(spec, app_data):
+def propose_interpreters(spec, try_first_with, app_data):
+ # 0. try with first
+ for py_exe in try_first_with:
+ path = os.path.abspath(py_exe)
+ try:
+ os.lstat(path) # Windows Store Python does not work with os.path.exists, but does for os.lstat
+ except OSError:
+ pass
+ else:
+ yield PythonInfo.from_exe(os.path.abspath(path), app_data), True
+
# 1. if it's a path and exists
if spec.path is not None:
try:
diff --git a/src/virtualenv/discovery/windows/__init__.py b/src/virtualenv/discovery/windows/__init__.py
index 9063ab8..556ecf2 100644
--- a/src/virtualenv/discovery/windows/__init__.py
+++ b/src/virtualenv/discovery/windows/__init__.py
@@ -13,8 +13,11 @@ def propose_interpreters(spec, cache_dir):
# see if PEP-514 entries are good
# start with higher python versions in an effort to use the latest version available
+ # and prefer PythonCore over conda pythons (as virtualenv is mostly used by non conda tools)
existing = list(discover_pythons())
- existing.sort(key=lambda i: tuple(-1 if j is None else j for j in i[1:4]), reverse=True)
+ existing.sort(
+ key=lambda i: tuple(-1 if j is None else j for j in i[1:4]) + (1 if i[0] == "PythonCore" else 0,), reverse=True
+ )
for name, major, minor, arch, exe, _ in existing:
# pre-filter