Commit c7342e45 by Sébastien Eustace

Fix executables discovery for generic environments

parent d526348c
......@@ -1042,10 +1042,11 @@ class EnvManager:
(e.g. plugin installation or self update).
"""
prefix, base_prefix = Path(sys.prefix), Path(cls.get_base_prefix())
env = SystemEnv(prefix)
if not naive:
if prefix.joinpath("poetry_env").exists():
return GenericEnv(base_prefix)
env = GenericEnv(base_prefix, child_env=env)
else:
from poetry.locations import data_dir
try:
......@@ -1053,9 +1054,9 @@ class EnvManager:
except ValueError:
pass
else:
return GenericEnv(base_prefix)
env = GenericEnv(base_prefix, child_env=env)
return SystemEnv(prefix)
return env
@classmethod
def get_base_prefix(cls) -> Path:
......@@ -1093,29 +1094,10 @@ class Env:
self._path = path
self._bin_dir = self._path / bin_dir
try:
python_executables = sorted(
[
p.name
for p in self._bin_dir.glob("python*")
if re.match(r"python(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
]
)
self._executable = python_executables[0].rstrip(".exe")
except IndexError:
self._executable = "python" + (".exe" if self._is_windows else "")
self._executable = "python"
self._pip_executable = "pip"
try:
pip_executables = sorted(
[
p.name
for p in self._bin_dir.glob("pip*")
if re.match(r"pip(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
]
)
self._pip_executable = pip_executables[0].rstrip(".exe")
except IndexError:
self._pip_executable = "pip" + (".exe" if self._is_windows else "")
self.find_executables()
self._base = base or path
......@@ -1160,6 +1142,39 @@ class Env:
return self._marker_env
@property
def parent_env(self) -> "GenericEnv":
return GenericEnv(self.base, child_env=self)
def find_executables(self) -> None:
python_executables = sorted(
[
p.name
for p in self._bin_dir.glob("python*")
if re.match(r"python(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
]
)
if python_executables:
executable = python_executables[0]
if executable.endswith(".exe"):
executable = executable[:-4]
self._executable = executable
pip_executables = sorted(
[
p.name
for p in self._bin_dir.glob("pip*")
if re.match(r"pip(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
]
)
if pip_executables:
pip_executable = pip_executables[0]
if pip_executable.endswith(".exe"):
pip_executable = pip_executable[:-4]
self._pip_executable = pip_executable
def get_embedded_wheel(self, distribution):
return get_embed_wheel(
distribution, "{}.{}".format(self.version_info[0], self.version_info[1])
......@@ -1390,7 +1405,11 @@ class Env:
"""
Return path to the given executable.
"""
bin_path = (self._bin_dir / bin).with_suffix(".exe" if self._is_windows else "")
if self._is_windows and not bin.endswith(".exe"):
bin_path = self._bin_dir / (bin + ".exe")
else:
bin_path = self._bin_dir / bin
if not bin_path.exists():
# On Windows, some executables can be in the base path
# This is especially true when installing Python with
......@@ -1401,7 +1420,11 @@ class Env:
# that creates a fake virtual environment pointing to
# a base Python install.
if self._is_windows:
bin_path = (self._path / bin).with_suffix(".exe")
if not bin.endswith(".exe"):
bin_path = self._bin_dir / (bin + ".exe")
else:
bin_path = self._path / bin
if bin_path.exists():
return str(bin_path)
......@@ -1649,6 +1672,70 @@ class VirtualEnv(Env):
class GenericEnv(VirtualEnv):
def __init__(
self, path: Path, base: Optional[Path] = None, child_env: Optional["Env"] = None
) -> None:
self._child_env = child_env
super().__init__(path, base=base)
def find_executables(self) -> None:
patterns = [("python*", "pip*")]
if self._child_env:
minor_version = "{}.{}".format(
self._child_env.version_info[0], self._child_env.version_info[1]
)
major_version = "{}".format(self._child_env.version_info[0])
patterns = [
("python{}".format(minor_version), "pip{}".format(minor_version)),
("python{}".format(major_version), "pip{}".format(major_version)),
]
python_executable = None
pip_executable = None
for python_pattern, pip_pattern in patterns:
if python_executable and pip_executable:
break
if not python_executable:
python_executables = sorted(
[
p.name
for p in self._bin_dir.glob(python_pattern)
if re.match(r"python(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
]
)
if python_executables:
executable = python_executables[0]
if executable.endswith(".exe"):
executable = executable[:-4]
python_executable = executable
if not pip_executable:
pip_executables = sorted(
[
p.name
for p in self._bin_dir.glob(pip_pattern)
if re.match(r"pip(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
]
)
if pip_executables:
pip_executable = pip_executables[0]
if pip_executable.endswith(".exe"):
pip_executable = pip_executable[:-4]
pip_executable = pip_executable
if python_executable:
self._executable = python_executable
if pip_executable:
self._pip_executable = pip_executable
def get_paths(self) -> Dict[str, str]:
output = self.run_python_script(GET_PATHS_FOR_GENERIC_ENVS)
......
......@@ -19,6 +19,7 @@ from poetry.utils._compat import WINDOWS
from poetry.utils.env import GET_BASE_PREFIX
from poetry.utils.env import EnvCommandError
from poetry.utils.env import EnvManager
from poetry.utils.env import GenericEnv
from poetry.utils.env import NoCompatiblePythonVersionFound
from poetry.utils.env import SystemEnv
from poetry.utils.env import VirtualEnv
......@@ -1001,3 +1002,84 @@ def test_env_finds_the_correct_executables(tmp_dir, manager):
assert Path(venv.python).name == expected_executable
assert Path(venv.pip).name == expected_pip_executable
def test_env_finds_the_correct_executables_for_generic_env(tmp_dir, manager):
venv_path = Path(tmp_dir) / "Virtual Env"
child_venv_path = Path(tmp_dir) / "Child Virtual Env"
manager.build_venv(str(venv_path), with_pip=True)
parent_venv = VirtualEnv(venv_path)
manager.build_venv(
str(child_venv_path), executable=parent_venv.python, with_pip=True
)
venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path))
expected_executable = "python{}.{}{}".format(
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else ""
)
expected_pip_executable = "pip{}.{}{}".format(
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else ""
)
assert Path(venv.python).name == expected_executable
assert Path(venv.pip).name == expected_pip_executable
def test_env_finds_fallback_executables_for_generic_env(tmp_dir, manager):
venv_path = Path(tmp_dir) / "Virtual Env"
child_venv_path = Path(tmp_dir) / "Child Virtual Env"
manager.build_venv(str(venv_path), with_pip=True)
parent_venv = VirtualEnv(venv_path)
manager.build_venv(
str(child_venv_path), executable=parent_venv.python, with_pip=True
)
venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path))
default_executable = "python" + (".exe" if WINDOWS else "")
major_executable = "python{}{}".format(
sys.version_info[0], ".exe" if WINDOWS else ""
)
minor_executable = "python{}.{}{}".format(
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else ""
)
expected_executable = minor_executable
if (
venv._bin_dir.joinpath(expected_executable).exists()
and venv._bin_dir.joinpath(major_executable).exists()
):
venv._bin_dir.joinpath(expected_executable).unlink()
expected_executable = major_executable
if (
venv._bin_dir.joinpath(expected_executable).exists()
and venv._bin_dir.joinpath(default_executable).exists()
):
venv._bin_dir.joinpath(expected_executable).unlink()
expected_executable = default_executable
default_pip_executable = "pip" + (".exe" if WINDOWS else "")
major_pip_executable = "pip{}{}".format(
sys.version_info[0], ".exe" if WINDOWS else ""
)
minor_pip_executable = "pip{}.{}{}".format(
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else ""
)
expected_pip_executable = minor_pip_executable
if (
venv._bin_dir.joinpath(expected_pip_executable).exists()
and venv._bin_dir.joinpath(major_pip_executable).exists()
):
venv._bin_dir.joinpath(expected_pip_executable).unlink()
expected_pip_executable = major_pip_executable
if (
venv._bin_dir.joinpath(expected_pip_executable).exists()
and venv._bin_dir.joinpath(default_pip_executable).exists()
):
venv._bin_dir.joinpath(expected_pip_executable).unlink()
expected_pip_executable = default_pip_executable
venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path))
assert Path(venv.python).name == expected_executable
assert Path(venv.pip).name == expected_pip_executable
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment