diff --git a/src/poetry/utils/env/env_manager.py b/src/poetry/utils/env/env_manager.py index 1061ff772ce..0c1d52e0433 100644 --- a/src/poetry/utils/env/env_manager.py +++ b/src/poetry/utils/env/env_manager.py @@ -159,7 +159,7 @@ def activate(self, python: str) -> Env: # Create if needed if not venv.exists() or create: - in_venv = os.environ.get("VIRTUAL_ENV") is not None + in_venv = bool(os.environ.get("VIRTUAL_ENV")) if in_venv or not venv.exists(): create = True @@ -214,7 +214,9 @@ def get(self, reload: bool = False) -> Env: conda_env_name = os.environ.get("CONDA_DEFAULT_ENV") # It's probably not a good idea to pollute Conda's global "base" env, since # most users have it activated all the time. - in_venv = env_prefix is not None and conda_env_name != "base" + # Treat an empty env_prefix as if no virtualenv is active, since conda + # can leave CONDA_PREFIX set to an empty string after deactivation. + in_venv = bool(env_prefix) and conda_env_name != "base" if not in_venv or env is not None: # Checking if a local virtualenv exists @@ -249,14 +251,8 @@ def get(self, reload: bool = False) -> Env: return VirtualEnv(venv) - if env_prefix is not None: - prefix = Path(env_prefix) - base_prefix = None - else: - prefix = Path(sys.prefix) - base_prefix = self.get_base_prefix() - - return VirtualEnv(prefix, base_prefix) + assert env_prefix + return VirtualEnv(Path(env_prefix)) def list(self, name: str | None = None) -> list[VirtualEnv]: if name is None: diff --git a/src/poetry/utils/env/python/manager.py b/src/poetry/utils/env/python/manager.py index ec02c6d638a..aa3bbdc1e9c 100644 --- a/src/poetry/utils/env/python/manager.py +++ b/src/poetry/utils/env/python/manager.py @@ -84,7 +84,7 @@ def __init__( @classmethod def find_all(cls) -> Iterator[Python]: venv_path: Path | None = ( - Path(os.environ["VIRTUAL_ENV"]) if "VIRTUAL_ENV" in os.environ else None + Path(venv) if (venv := os.environ.get("VIRTUAL_ENV")) else None ) for python in findpython.find_all(): if venv_path and python.executable.is_relative_to(venv_path): diff --git a/tests/utils/env/test_env_manager.py b/tests/utils/env/test_env_manager.py index df5cbf1a0af..71e05e4cb5c 100644 --- a/tests/utils/env/test_env_manager.py +++ b/tests/utils/env/test_env_manager.py @@ -577,6 +577,33 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( assert env.base == Path(sys.base_prefix) +@pytest.mark.parametrize("env_var", ["VIRTUAL_ENV", "CONDA_PREFIX"]) +def test_get_ignores_empty_env_prefix( + manager: EnvManager, + poetry: Poetry, + in_project_venv_dir: Path, + env_var: str, + mocker: MockerFixture, +) -> None: + """An empty VIRTUAL_ENV or CONDA_PREFIX should be treated as unset. + + After ``conda deactivate``, conda can leave CONDA_PREFIX set to an + empty string. Poetry should not consider that as an active + virtualenv and should fall back to the in-project .venv instead. + + See: https://github.com/python-poetry/poetry/issues/10770 + """ + os.environ.pop("VIRTUAL_ENV", None) + os.environ.pop("CONDA_PREFIX", None) + os.environ[env_var] = "" + mocker.patch( + "poetry.utils.env.virtual_env.VirtualEnv.__init__", + lambda self, *args, **kwargs: setattr(self, "_path", args[0]), + ) + venv = manager.get() + assert venv.path == in_project_venv_dir + + def test_list( tmp_path: Path, manager: EnvManager,