Skip to content

Commit c5fe517

Browse files
sdispaterfinswimmer
andcommitted
Ensure the correct environment executables are used
Co-authored-by: finswimmer <[email protected]>
1 parent 66a4813 commit c5fe517

File tree

4 files changed

+99
-12
lines changed

4 files changed

+99
-12
lines changed

poetry/console/commands/env/info.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def handle(self) -> Optional[int]:
3333
self._display_complete_info(env)
3434

3535
def _display_complete_info(self, env: "Env") -> None:
36+
from poetry.utils.env import GenericEnv
37+
3638
env_python_version = ".".join(str(s) for s in env.version_info[:3])
3739
self.line("")
3840
self.line("<b>Virtualenv</b>")
@@ -44,6 +46,9 @@ def _display_complete_info(self, env: "Env") -> None:
4446
"<info>Path</info>: <comment>{}</>".format(
4547
env.path if env.is_venv() else "NA"
4648
),
49+
"<info>Executable</info>: <comment>{}</>".format(
50+
env.python if env.is_venv() else "NA"
51+
),
4752
]
4853
if env.is_venv():
4954
listing.append(
@@ -55,13 +60,18 @@ def _display_complete_info(self, env: "Env") -> None:
5560

5661
self.line("")
5762

63+
system_env = GenericEnv(env.base)
5864
self.line("<b>System</b>")
5965
self.line(
6066
"\n".join(
6167
[
62-
"<info>Platform</info>: <comment>{}</>".format(env.platform),
63-
"<info>OS</info>: <comment>{}</>".format(env.os),
64-
"<info>Python</info>: <comment>{}</>".format(env.base),
68+
"<info>Platform</info>: <comment>{}</>".format(env.platform),
69+
"<info>OS</info>: <comment>{}</>".format(env.os),
70+
"<info>Python</info>: <comment>{}</>".format(
71+
".".join(str(v) for v in system_env.version_info[:3])
72+
),
73+
"<info>Path</info>: <comment>{}</>".format(system_env.path),
74+
"<info>Executable</info>: <comment>{}</>".format(system_env.python),
6575
]
6676
)
6777
)

poetry/utils/env.py

+35-5
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,30 @@ def __init__(self, path: Path, base: Optional[Path] = None) -> None:
10611061
self._path = path
10621062
self._bin_dir = self._path / bin_dir
10631063

1064+
try:
1065+
python_executables = sorted(
1066+
[
1067+
p.name
1068+
for p in self._bin_dir.glob("python*")
1069+
if re.match(r"python(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
1070+
]
1071+
)
1072+
self._executable = python_executables[0].rstrip(".exe")
1073+
except IndexError:
1074+
self._executable = "python" + (".exe" if self._is_windows else "")
1075+
1076+
try:
1077+
pip_executables = sorted(
1078+
[
1079+
p.name
1080+
for p in self._bin_dir.glob("pip*")
1081+
if re.match(r"pip(?:\d+(?:\.\d+)?)?(?:\.exe)?$", p.name)
1082+
]
1083+
)
1084+
self._pip_executable = pip_executables[0].rstrip(".exe")
1085+
except IndexError:
1086+
self._pip_executable = "pip" + (".exe" if self._is_windows else "")
1087+
10641088
self._base = base or path
10651089

10661090
self._marker_env = None
@@ -1095,7 +1119,7 @@ def python(self) -> str:
10951119
"""
10961120
Path to current python executable
10971121
"""
1098-
return self._bin("python")
1122+
return self._bin(self._executable)
10991123

11001124
@property
11011125
def marker_env(self) -> Dict[str, Any]:
@@ -1121,7 +1145,7 @@ def pip(self) -> str:
11211145
Path to current pip executable
11221146
"""
11231147
# we do not use as_posix() here due to issues with windows pathlib2 implementation
1124-
path = self._bin("pip")
1148+
path = self._bin(self._pip_executable)
11251149
if not Path(path).exists():
11261150
return str(self.pip_embedded)
11271151
return path
@@ -1267,7 +1291,7 @@ def run_pip(self, *args: str, **kwargs: Any) -> Union[int, str]:
12671291
return self._run(cmd, **kwargs)
12681292

12691293
def run_python_script(self, content: str, **kwargs: Any) -> str:
1270-
return self.run("python", "-W", "ignore", "-", input_=content, **kwargs)
1294+
return self.run(self._executable, "-W", "ignore", "-", input_=content, **kwargs)
12711295

12721296
def _run(self, cmd: List[str], **kwargs: Any) -> Union[int, str]:
12731297
"""
@@ -1489,7 +1513,10 @@ def get_python_implementation(self) -> str:
14891513
def get_pip_command(self, embedded: bool = False) -> List[str]:
14901514
# We're in a virtualenv that is known to be sane,
14911515
# so assume that we have a functional pip
1492-
return [self._bin("python"), self.pip_embedded if embedded else self.pip]
1516+
return [
1517+
self._bin(self._executable),
1518+
self.pip_embedded if embedded else self.pip,
1519+
]
14931520

14941521
def get_supported_tags(self) -> List[Tag]:
14951522
file_path = Path(packaging.tags.__file__)
@@ -1607,7 +1634,10 @@ def __init__(
16071634
self.executed = []
16081635

16091636
def get_pip_command(self, embedded: bool = False) -> List[str]:
1610-
return [self._bin("python"), self.pip_embedded if embedded else self.pip]
1637+
return [
1638+
self._bin(self._executable),
1639+
self.pip_embedded if embedded else self.pip,
1640+
]
16111641

16121642
def _run(self, cmd: List[str], **kwargs: Any) -> int:
16131643
self.executed.append(cmd)

tests/console/commands/env/test_info.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import sys
2+
13
from pathlib import Path
24

35
import pytest
46

7+
from poetry.utils._compat import WINDOWS
58
from poetry.utils.env import MockEnv
69

710

@@ -28,14 +31,21 @@ def test_env_info_displays_complete_info(tester):
2831
Python: 3.7.0
2932
Implementation: CPython
3033
Path: {prefix}
34+
Executable: {executable}
3135
Valid: True
3236
3337
System
34-
Platform: darwin
35-
OS: posix
36-
Python: {base_prefix}
38+
Platform: darwin
39+
OS: posix
40+
Python: {base_version}
41+
Path: {base_prefix}
42+
Executable: {base_executable}
3743
""".format(
38-
prefix=str(Path("/prefix")), base_prefix=str(Path("/base/prefix"))
44+
prefix=str(Path("/prefix")),
45+
base_prefix=str(Path("/base/prefix")),
46+
base_version=".".join(str(v) for v in sys.version_info[:3]),
47+
executable=sys.executable,
48+
base_executable="python" + (".exe" if WINDOWS else ""),
3949
)
4050

4151
assert expected == tester.io.fetch_output()

tests/utils/test_env.py

+37
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from poetry.core.semver.version import Version
1616
from poetry.core.toml.file import TOMLFile
1717
from poetry.factory import Factory
18+
from poetry.utils._compat import WINDOWS
1819
from poetry.utils.env import GET_BASE_PREFIX
1920
from poetry.utils.env import EnvCommandError
2021
from poetry.utils.env import EnvManager
@@ -964,3 +965,39 @@ def test_env_system_packages(tmp_path, config):
964965
assert not venv_path.joinpath(
965966
"lib", "python2.7", "no-global-site-packages.txt"
966967
).exists()
968+
969+
970+
def test_env_finds_the_correct_executables(tmp_dir, manager):
971+
venv_path = Path(tmp_dir) / "Virtual Env"
972+
manager.build_venv(str(venv_path), with_pip=True)
973+
venv = VirtualEnv(venv_path)
974+
975+
default_executable = expected_executable = "python" + (".exe" if WINDOWS else "")
976+
default_pip_executable = expected_pip_executable = "pip" + (
977+
".exe" if WINDOWS else ""
978+
)
979+
major_executable = "python{}{}".format(
980+
sys.version_info[0], ".exe" if WINDOWS else ""
981+
)
982+
major_pip_executable = "pip{}{}".format(
983+
sys.version_info[0], ".exe" if WINDOWS else ""
984+
)
985+
986+
if (
987+
venv._bin_dir.joinpath(default_executable).exists()
988+
and venv._bin_dir.joinpath(major_executable).exists()
989+
):
990+
venv._bin_dir.joinpath(default_executable).unlink()
991+
expected_executable = major_executable
992+
993+
if (
994+
venv._bin_dir.joinpath(default_pip_executable).exists()
995+
and venv._bin_dir.joinpath(major_pip_executable).exists()
996+
):
997+
venv._bin_dir.joinpath(default_pip_executable).unlink()
998+
expected_pip_executable = major_pip_executable
999+
1000+
venv = VirtualEnv(venv_path)
1001+
1002+
assert Path(venv.python).name == expected_executable
1003+
assert Path(venv.pip).name == expected_pip_executable

0 commit comments

Comments
 (0)