Skip to content

fix(env): treat empty VIRTUAL_ENV/CONDA_PREFIX as unset#10784

Merged
radoering merged 5 commits intopython-poetry:mainfrom
Krishnachaitanyakc:fix/ignore-empty-env-prefix
Mar 28, 2026
Merged

fix(env): treat empty VIRTUAL_ENV/CONDA_PREFIX as unset#10784
radoering merged 5 commits intopython-poetry:mainfrom
Krishnachaitanyakc:fix/ignore-empty-env-prefix

Conversation

@Krishnachaitanyakc
Copy link
Copy Markdown
Contributor

@Krishnachaitanyakc Krishnachaitanyakc commented Mar 25, 2026

Summary

  • After conda deactivate, conda can leave CONDA_PREFIX set to an empty string. Poetry previously only checked is not None, treating the empty string as an active virtualenv, causing poetry env use and other commands to fail.
  • Changed all env-prefix checks to use truthiness (bool(...) / if env_prefix:) so an empty string is treated the same as an unset variable.
  • Also applied the same fix to VIRTUAL_ENV checks in env_manager.py (line 162) and python/manager.py for consistency and defensive correctness.
  • Added parametrized tests for both VIRTUAL_ENV and CONDA_PREFIX empty-string scenarios.

Fixes #10770

Test plan

  • Added test_get_ignores_empty_env_prefix parametrized over VIRTUAL_ENV and CONDA_PREFIX
  • Both test cases verify that manager.get() falls back to the in-project .venv when the env variable is set to an empty string
  • Existing tests remain unaffected (the change is strictly more permissive — only empty strings are newly treated as unset)

Summary by Sourcery

Handle empty virtual environment prefix variables as unset across environment detection and Python discovery.

Bug Fixes:

  • Treat empty CONDA_PREFIX values as no active virtualenv when resolving the current environment.
  • Treat empty VIRTUAL_ENV values as no active virtualenv when activating or discovering environments.

Enhancements:

  • Align virtual environment detection logic to rely on truthiness of env-prefix variables for more robust behavior across backends.

Tests:

  • Add parametrized tests ensuring empty VIRTUAL_ENV and CONDA_PREFIX values cause fallback to the in-project virtualenv instead of being treated as active.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 25, 2026

Reviewer's Guide

This PR makes environment detection more robust by treating empty VIRTUAL_ENV and CONDA_PREFIX values as unset, updating env-prefix checks to use truthiness semantics, and adding tests to verify that Poetry falls back to the in-project .venv in these scenarios.

Class diagram for updated environment and Python managers

classDiagram
    class EnvManager {
        +activate(python: str) Env
        +get(reload: bool) Env
        -create: bool
        -env_prefix: str
        -consecutive_calls: int
    }

    class PythonManager {
        +find_all() Iterator~Python~
    }

    class VirtualEnv {
        +VirtualEnv(path: Path)
    }

    class Python {
        +executable: Path
    }

    EnvManager --> VirtualEnv : creates
    PythonManager --> Python : returns

    %% Highlighted behaviors related to this PR
    class EnvManager {
        -_detect_in_venv(env_prefix: str | None, conda_env_name: str | None) bool
    }

    class PythonManager {
        -_current_venv_path: Path | None
    }
Loading

Flow diagram for updated env detection in EnvManager.get

flowchart TD
    A["Start get(reload)"] --> B["Read env_prefix from environment"]
    B --> C["Read conda_env_name from CONDA_DEFAULT_ENV"]
    C --> D{"bool(env_prefix) and conda_env_name != base"}

    D -- "False" --> E["Treat as not in_venv"]
    E --> F{"Existing env instance and not reload"}
    F -- "True" --> G["Return existing env"]
    F -- "False" --> H{"Project .venv exists"}
    H -- "True" --> I["Return VirtualEnv for project .venv"]
    H -- "False" --> J["Create new project .venv"] --> I

    D -- "True" --> K{"env_prefix is truthy"}
    K -- "True" --> L["prefix = Path(env_prefix)"]
    K -- "False" --> M["prefix = in-project .venv path"]
    L --> N["Return VirtualEnv for prefix"]
    M --> N
Loading

File-Level Changes

Change Details Files
Handle empty VIRTUAL_ENV/CONDA_PREFIX values as unset when resolving active environments.
  • Update EnvManager.activate to compute in_venv using truthiness of VIRTUAL_ENV instead of only checking for non-None.
  • Adjust EnvManager.get to treat an empty env_prefix as no active virtualenv and to only consider non-empty env_prefix values when constructing the prefix path.
  • Change Python manager discovery logic to only derive venv_path from VIRTUAL_ENV when the variable is set to a non-empty string.
src/poetry/utils/env/env_manager.py
src/poetry/utils/env/python/manager.py
Add regression tests ensuring empty env-prefix variables fall back to the in-project virtualenv.
  • Introduce a parametrized test over VIRTUAL_ENV and CONDA_PREFIX that sets the selected variable to an empty string and asserts manager.get() resolves to the in-project .venv.
  • Use mocking to bypass VirtualEnv.init and set the _path attribute directly for simpler assertions.
  • Ensure environment variables are cleaned up after the test by deleting the modified key in a finally block.
tests/utils/env/test_env_manager.py

Assessment against linked issues

Issue Objective Addressed Explanation
#10770 Update environment detection so that an empty CONDA_PREFIX (and related env_prefix) is treated as unset, preventing Poetry from incorrectly assuming a virtualenv is active after conda deactivate.
#10770 Add tests to cover the scenario where CONDA_PREFIX (and similar environment variables like VIRTUAL_ENV) is set to an empty string, ensuring Poetry falls back to the in-project virtualenv.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@dosubot
Copy link
Copy Markdown

dosubot bot commented Mar 25, 2026

Documentation Updates

1 document(s) were updated by changes in this PR:

CHANGELOG
View Changes
@@ -1,4 +1,11 @@
 # Change Log
+
+## [Unreleased]
+
+### Fixed
+
+- Fixed handling of empty `VIRTUAL_ENV` and `CONDA_PREFIX` environment variables. Poetry now treats empty environment variables the same as unset variables. This fixes issues where `conda deactivate` could leave `CONDA_PREFIX` set to an empty string, causing `poetry env use` and other commands to fail. ([#10784](https://github.com/python-poetry/poetry/pull/10784))
+
 
 ## [2.3.2] - 2026-02-01
 

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In test_get_ignores_empty_env_prefix, consider using pytest’s monkeypatch.setenv/delenv (or a dedicated helper) instead of mutating os.environ directly with manual try/finally, which will make the env setup/teardown more concise and less error-prone.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `test_get_ignores_empty_env_prefix`, consider using pytest’s `monkeypatch.setenv/delenv` (or a dedicated helper) instead of mutating `os.environ` directly with manual `try/finally`, which will make the env setup/teardown more concise and less error-prone.

## Individual Comments

### Comment 1
<location path="tests/utils/env/test_env_manager.py" line_range="584-591" />
<code_context>
+    "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
</code_context>
<issue_to_address>
**suggestion (testing):** Missing tests for empty VIRTUAL_ENV behavior in env_manager.activate and python.manager.find_all

This test covers `EnvManager.get()` for empty `VIRTUAL_ENV`/`CONDA_PREFIX`, but the other updated code paths still lack coverage:

1. `EnvManager.activate` now uses `bool(os.environ.get("VIRTUAL_ENV"))` and should be tested with `VIRTUAL_ENV=""` to confirm it behaves as if no venv is active.
2. `python.manager.PythonManager.find_all` (or equivalent) should be tested with `VIRTUAL_ENV=""` to ensure it does not treat the empty string as a valid venv path.

Adding these tests would round out coverage for all call sites affected by the bugfix.

Suggested implementation:

```python
@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] = ""

    virtualenv_init = mocker.patch(
        "poetry.utils.env.virtual_env.VirtualEnv.__init__",
        return_value=None,
    )

    env = manager.get(poetry)

    # EnvManager.get() should ignore the empty env var and use the in-project venv.
    assert env.path == in_project_venv_dir
    virtualenv_init.assert_called_once_with(in_project_venv_dir, in_project_venv_dir)

    # EnvManager.activate() should also treat an empty VIRTUAL_ENV as "no active venv".
    # When VIRTUAL_ENV is empty, this should behave the same as when it is unset.
    activate_env = manager.activate(poetry)
    assert activate_env.path == in_project_venv_dir

    # The activation path should also rely on the in-project venv, not the empty env var.
    # We re-use the same VirtualEnv.__init__ patch to ensure no extra call with an empty
    # path sneaks through in the activate code path.
    assert virtualenv_init.call_count >= 1


def test_python_manager_find_all_ignores_empty_virtual_env(
    poetry: Poetry,
    in_project_venv_dir: Path,
    mocker: MockerFixture,
) -> None:
    """PythonManager.find_all should ignore an empty VIRTUAL_ENV.

    This mirrors the EnvManager behavior: VIRTUAL_ENV="" must not be treated
    as a valid virtual environment path.
    """
    from poetry.utils.env import EnvManager
    from poetry.utils.env.python_manager import PythonManager

    os.environ.pop("VIRTUAL_ENV", None)
    os.environ["VIRTUAL_ENV"] = ""

    manager = EnvManager(poetry)
    python_manager = PythonManager(poetry)

    # Patch whatever path discovery the PythonManager uses so we can assert it
    # does not attempt to inspect the empty VIRTUAL_ENV path.
    list_venvs = mocker.patch.object(
        python_manager,
        "list",
        return_value=[in_project_venv_dir],
    )

    envs = python_manager.find_all(manager)

    # The empty VIRTUAL_ENV should not be treated as a candidate environment.
    assert in_project_venv_dir in envs
    list_venvs.assert_called_once_with()

```

The SEARCH block is based on the visible portion of `test_get_ignores_empty_env_prefix`; you may need to adjust it to match the exact function body in your local copy.

The new `test_python_manager_find_all_ignores_empty_virtual_env` assumes:
1. `PythonManager` lives in `poetry.utils.env.python_manager`.
2. `PythonManager` has a `list()` method that returns discovered virtualenv paths.
3. `PythonManager.find_all(manager)` is the public API you want to exercise.

If the actual API differs (e.g., different module path or method names), you should:
- Update the imports accordingly.
- Patch the appropriate method used internally by `find_all` to look up venv locations.
- Adjust the assertions on the patched method to match how your real implementation works.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Krishnachaitanyakc and others added 2 commits March 28, 2026 11:04
After `conda deactivate`, conda can leave `CONDA_PREFIX` set to an
empty string.  Poetry was treating this as an active virtualenv because
it only checked `is not None` rather than truthiness.  This caused
`poetry env use` and other commands to fail with an empty prefix path.

Change all env-prefix checks to use truthiness (`bool(...)` / `if
env_prefix:`) so that an empty string is treated the same as an unset
variable.

Fixes python-poetry#10770
@radoering radoering force-pushed the fix/ignore-empty-env-prefix branch from 8a3a78b to 3bd985f Compare March 28, 2026 10:06
@radoering radoering merged commit 0206165 into python-poetry:main Mar 28, 2026
54 checks passed
radoering added a commit that referenced this pull request Mar 28, 2026
Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com>
(cherry picked from commit 0206165)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Poetry thinks Conda env is active after it's been deactivated

2 participants