Skip to content
Open
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
17 changes: 17 additions & 0 deletions src/poetry/utils/env/env_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,23 @@ def get(self, reload: bool = False) -> Env:
# most users have it activated all the time.
in_venv = env_prefix is not None and conda_env_name != "base"

project_path = self._poetry.file.path.parent.resolve()
cwd = Path.cwd().resolve()
invoked_outside_project = not (
cwd == project_path or cwd.is_relative_to(project_path)
Comment on lines +221 to +222
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Using Path.is_relative_to requires Python 3.9+; ensure this aligns with the supported runtime or add a compatibility fallback.

If any supported runtime (including bootstrap/runtime tooling, not just managed envs) can be <3.9, this will raise AttributeError at runtime. If 3.9+ is guaranteed everywhere, no change needed; otherwise, add a small compatibility helper (e.g., try cwd.is_relative_to and fall back to a str(cwd).startswith(str(project_path) + os.sep) check) or centralize this logic in a utility so the version-specific handling lives in one place.

)

if (
in_venv
and env is None
and invoked_outside_project
and self.in_project_venv_exists()
):
# When operating on another project directory (for example via `-C`),
# prefer that project's in-project virtualenv over an inherited
# VIRTUAL_ENV from the caller process.
return VirtualEnv(self.in_project_venv)

if not in_venv or env is not None:
# Checking if a local virtualenv exists
if self.in_project_venv_exists():
Expand Down
31 changes: 31 additions & 0 deletions tests/utils/env/test_env_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,37 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var(
assert env.base == Path(sys.base_prefix)


def test_get_prefers_in_project_venv_when_running_outside_project(
tmp_path: Path,
manager: EnvManager,
in_project_venv_dir: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("VIRTUAL_ENV", "/environment/prefix")
outside_cwd = tmp_path / "outside"
outside_cwd.mkdir()
monkeypatch.chdir(outside_cwd)
Comment on lines +580 to +589
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Use monkeypatch.setenv instead of mutating os.environ directly to avoid cross-test leakage

This test (and the one below) assigns to os.environ["VIRTUAL_ENV"], which can leak into later tests if cleanup is skipped. Since monkeypatch is already in use, please switch to monkeypatch.setenv("VIRTUAL_ENV", "/environment/prefix") in both tests so the environment is automatically restored.

Suggested implementation:

def test_get_prefers_in_project_venv_when_running_outside_project(
    tmp_path: Path,
    manager: EnvManager,
    in_project_venv_dir: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setenv("VIRTUAL_ENV", "/environment/prefix")
    outside_cwd = tmp_path / "outside"

In the same file (tests/utils/env/test_env_manager.py), inside the test_get_keeps_active_virtualenv_when_running_inside_project function, replace any direct assignment like:

os.environ["VIRTUAL_ENV"] = "/environment/prefix"

(or similar) with:

monkeypatch.setenv("VIRTUAL_ENV", "/environment/prefix")

This ensures the environment variable is automatically restored after the test and prevents cross-test leakage.


env = manager.get()

assert env.path == in_project_venv_dir
assert env.base == Path(sys.base_prefix)


def test_get_keeps_active_virtualenv_when_running_inside_project(
manager: EnvManager,
poetry: Poetry,
in_project_venv_dir: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("VIRTUAL_ENV", "/environment/prefix")
monkeypatch.chdir(poetry.file.path.parent)

env = manager.get()

assert env.path == Path("/environment/prefix")


def test_list(
tmp_path: Path,
manager: EnvManager,
Expand Down
Loading