Skip to content
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

fix: take virtualenvs.prefer-active-python into account on EnvManager.get() #6986

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/poetry/console/application.py
Expand Up @@ -295,8 +295,8 @@ def configure_env(self, event: Event, event_name: str, _: EventDispatcher) -> No
io = event.io
poetry = command.poetry

env_manager = EnvManager(poetry)
env = env_manager.create_venv(io)
env_manager = EnvManager(poetry, io=io)
env = env_manager.create_venv()

if env.is_venv() and io.is_verbose():
io.write_line(f"Using virtualenv: <comment>{env.path}</>")
Expand Down
6 changes: 3 additions & 3 deletions src/poetry/console/commands/env/use.py
Expand Up @@ -14,14 +14,14 @@ class EnvUseCommand(Command):
def handle(self) -> int:
from poetry.utils.env import EnvManager

manager = EnvManager(self.poetry)
manager = EnvManager(self.poetry, io=self.io)

if self.argument("python") == "system":
manager.deactivate(self.io)
manager.deactivate()

return 0

env = manager.activate(self.argument("python"), self.io)
env = manager.activate(self.argument("python"))

self.line(f"Using virtualenv: <comment>{env.path}</>")

Expand Down
74 changes: 51 additions & 23 deletions src/poetry/utils/env.py
Expand Up @@ -20,11 +20,13 @@
from subprocess import CalledProcessError
from typing import TYPE_CHECKING
from typing import Any
from typing import cast

import packaging.tags
import tomlkit
import virtualenv

from cleo.io.null_io import NullIO
from cleo.io.outputs.output import Verbosity
from packaging.tags import Tag
from packaging.tags import interpreter_name
Expand Down Expand Up @@ -515,8 +517,9 @@ class EnvManager:

ENVS_FILE = "envs.toml"

def __init__(self, poetry: Poetry) -> None:
def __init__(self, poetry: Poetry, io: None | IO = None) -> None:
self._poetry = poetry
self._io = io or NullIO()

def _full_python_path(self, python: str) -> str:
try:
Expand All @@ -533,26 +536,48 @@ def _full_python_path(self, python: str) -> str:

return executable

def _detect_active_python(self, io: IO) -> str | None:
def _detect_active_python(self) -> str | None:
executable = None

try:
io.write_error_line(
self._io.write_error_line(
"Trying to detect current active python executable as specified in the"
" config.",
verbosity=Verbosity.VERBOSE,
)
executable = self._full_python_path("python")
io.write_error_line(f"Found: {executable}", verbosity=Verbosity.VERBOSE)
self._io.write_error_line(
f"Found: {executable}", verbosity=Verbosity.VERBOSE
)
except CalledProcessError:
io.write_error_line(
self._io.write_error_line(
"Unable to detect the current active python executable. Falling back to"
" default.",
verbosity=Verbosity.VERBOSE,
)
return executable

def activate(self, python: str, io: IO) -> Env:
def _get_python_version(self) -> tuple[int, int, int]:
version_info = tuple(sys.version_info[:3])

if self._poetry.config.get("virtualenvs.prefer-active-python"):
executable = self._detect_active_python()

if executable:
python_patch = decode(
subprocess.check_output(
list_to_shell_command(
[executable, "-c", GET_PYTHON_VERSION_ONELINER]
),
shell=True,
).strip()
)

version_info = tuple(int(v) for v in python_patch.split(".")[:3])

return cast("tuple[int, int, int]", version_info)

def activate(self, python: str) -> Env:
venv_path = self._poetry.config.virtualenvs_path
cwd = self._poetry.file.parent

Expand Down Expand Up @@ -598,7 +623,7 @@ def activate(self, python: str, io: IO) -> Env:
if patch != current_patch:
create = True

self.create_venv(io, executable=python, force=create)
self.create_venv(executable=python, force=create)

return self.get(reload=True)

Expand Down Expand Up @@ -632,15 +657,15 @@ def activate(self, python: str, io: IO) -> Env:
if patch != current_patch:
create = True

self.create_venv(io, executable=python, force=create)
self.create_venv(executable=python, force=create)

# Activate
envs[base_env_name] = {"minor": minor, "patch": patch}
envs_file.write(envs)

return self.get(reload=True)

def deactivate(self, io: IO) -> None:
def deactivate(self) -> None:
venv_path = self._poetry.config.virtualenvs_path
name = self.generate_env_name(
self._poetry.package.name, str(self._poetry.file.parent)
Expand All @@ -652,7 +677,7 @@ def deactivate(self, io: IO) -> None:
env = envs.get(name)
if env is not None:
venv = venv_path / f"{name}-py{env['minor']}"
io.write_error_line(
self._io.write_error_line(
f"Deactivating virtualenv: <comment>{venv}</comment>"
)
del envs[name]
Expand All @@ -663,7 +688,7 @@ def get(self, reload: bool = False) -> Env:
if self._env is not None and not reload:
return self._env

python_minor = ".".join([str(v) for v in sys.version_info[:2]])
python_minor = ".".join([str(v) for v in self._get_python_version()[:2]])

venv_path = self._poetry.config.virtualenvs_path

Expand Down Expand Up @@ -855,7 +880,6 @@ def remove(self, python: str) -> Env:

def create_venv(
self,
io: IO,
name: str | None = None,
executable: str | None = None,
force: bool = False,
Expand Down Expand Up @@ -888,7 +912,7 @@ def create_venv(
venv_prompt = self._poetry.config.get("virtualenvs.prompt")

if not executable and prefer_active_python:
executable = self._detect_active_python(io)
executable = self._detect_active_python()

venv_path = cwd / ".venv" if root_venv else self._poetry.config.virtualenvs_path
if not name:
Expand Down Expand Up @@ -921,7 +945,7 @@ def create_venv(
self._poetry.package.python_versions, python_patch
)

io.write_error_line(
self._io.write_error_line(
f"<warning>The currently activated Python version {python_patch} is not"
f" supported by the project ({self._poetry.package.python_versions}).\n"
"Trying to find and use a compatible version.</warning> "
Expand All @@ -944,8 +968,8 @@ def create_venv(

python = "python" + python_to_try

if io.is_debug():
io.write_error_line(f"<debug>Trying {python}</debug>")
if self._io.is_debug():
self._io.write_error_line(f"<debug>Trying {python}</debug>")

try:
python_patch = decode(
Expand All @@ -964,7 +988,9 @@ def create_venv(
continue

if supported_python.allows(Version.parse(python_patch)):
io.write_error_line(f"Using <c1>{python}</c1> ({python_patch})")
self._io.write_error_line(
f"Using <c1>{python}</c1> ({python_patch})"
)
executable = python
python_minor = ".".join(python_patch.split(".")[:2])
break
Expand All @@ -989,7 +1015,7 @@ def create_venv(

if not venv.exists():
if create_venv is False:
io.write_error_line(
self._io.write_error_line(
"<fg=black;bg=yellow>"
"Skipping virtualenv creation, "
"as specified in config file."
Expand All @@ -998,23 +1024,25 @@ def create_venv(

return self.get_system_env()

io.write_error_line(
self._io.write_error_line(
f"Creating virtualenv <c1>{name}</> in"
f" {venv_path if not WINDOWS else get_real_windows_path(venv_path)!s}"
)
else:
create_venv = False
if force:
if not env.is_sane():
io.write_error_line(
self._io.write_error_line(
f"<warning>The virtual environment found in {env.path} seems to"
" be broken.</warning>"
)
io.write_error_line(f"Recreating virtualenv <c1>{name}</> in {venv!s}")
self._io.write_error_line(
f"Recreating virtualenv <c1>{name}</> in {venv!s}"
)
self.remove_venv(venv)
create_venv = True
elif io.is_very_verbose():
io.write_error_line(f"Virtualenv <c1>{name}</> already exists.")
elif self._io.is_very_verbose():
self._io.write_error_line(f"Virtualenv <c1>{name}</> already exists.")

if create_venv:
self.build_venv(
Expand Down