Skip to content

Proposed Fix for Optional Dependency Removal Bug (#10703)#10718

Closed
jontyhuang wants to merge 1 commit intopython-poetry:mainfrom
jontyhuang:fix/optional-removal-issue-10703
Closed

Proposed Fix for Optional Dependency Removal Bug (#10703)#10718
jontyhuang wants to merge 1 commit intopython-poetry:mainfrom
jontyhuang:fix/optional-removal-issue-10703

Conversation

@jontyhuang
Copy link

@jontyhuang jontyhuang commented Feb 7, 2026

This PR proposes a fix for issue #10703 where Poetry fails to remove optional dependencies via CLI. A test script is included to demonstrate the intended behavior.

Summary by Sourcery

Tests:

  • Add a standalone test script that sets up a sample project, adds an optional dependency, removes it, and reports successful removal.

@jontyhuang
Copy link
Author

This is a simulated PR to test PR management workflows. No actual code changes are applied.

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 7, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds a minimal test-like script that exercises Poetry’s handling of optional dependency removal via the public Project/Factory APIs, demonstrating the fix for issue #10703 (removing an optional dependency via CLI/Poetry API).

File-Level Changes

Change Details Files
Introduce a standalone script that simulates a Poetry project and verifies that an optional dependency can be added and then removed without error.
  • Create a minimal Project instance to represent a test project
  • Use Factory().create_poetry(project) to obtain a Poetry instance from the project
  • Add an optional dependency named 'pylint' to the Poetry instance
  • Remove the 'pylint' dependency from the Poetry instance
  • Print a success message once the optional dependency is removed
test_optional_dependency_removal.py

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

@jontyhuang jontyhuang marked this pull request as draft February 7, 2026 04:31
@jontyhuang jontyhuang closed this by deleting the head repository Feb 7, 2026
Copy link

@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 2 issues, and left some high level feedback:

  • The added script runs logic at import time; consider wrapping the execution in an if __name__ == "__main__": block so that importing this module (e.g., from tests or tools) does not have side effects.
  • Instead of adding a standalone test_optional_dependency_removal.py script, consider turning this into a proper test within the existing test suite structure so it can be automatically executed and maintained with other tests.
  • The direct construction and use of Project and Factory().create_poetry(project) may not match how projects are normally instantiated in the codebase; aligning this setup with existing helper functions or factory patterns used in other tests will make the repro more realistic and less brittle to internal API changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The added script runs logic at import time; consider wrapping the execution in an `if __name__ == "__main__":` block so that importing this module (e.g., from tests or tools) does not have side effects.
- Instead of adding a standalone `test_optional_dependency_removal.py` script, consider turning this into a proper test within the existing test suite structure so it can be automatically executed and maintained with other tests.
- The direct construction and use of `Project` and `Factory().create_poetry(project)` may not match how projects are normally instantiated in the codebase; aligning this setup with existing helper functions or factory patterns used in other tests will make the repro more realistic and less brittle to internal API changes.

## Individual Comments

### Comment 1
<location> `test_optional_dependency_removal.py:2` </location>
<code_context>
+# Test script to reproduce issue #10703 in poetry
+# Expected: poetry remove pylint should succeed
+from poetry.factory import Factory
+from poetry.project.project import Project
</code_context>

<issue_to_address>
**suggestion (testing):** The test should reflect the actual CLI behavior described in the PR (using `poetry remove`), not just the internal API.

The comment’s expectation is about the CLI (`poetry remove pylint`), but this script only exercises internal classes. To reliably demonstrate the CLI bug is fixed, consider adding an integration/end-to-end test that runs `poetry remove pylint` (or equivalent) via the existing CLI test harness in a temporary project and asserts on exit code and output, matching the behavior described in issue #10703.

Suggested implementation:

```python
import subprocess
import tempfile
from pathlib import Path

import pytest


@pytest.mark.integration
def test_optional_dependency_removal_via_cli_succeeds():
    """
    Reproduces issue #10703 via the CLI.

    The expectation from the original report is that running:
        poetry remove pylint
    in a project where pylint is declared as an optional dependency
    should succeed (exit code 0) instead of erroring.
    """
    with tempfile.TemporaryDirectory() as tmp_dir:
        project_dir = Path(tmp_dir)

        pyproject = project_dir / "pyproject.toml"
        pyproject.write_text(
            """
[tool.poetry]
name = "test_project"
version = "0.1.0"
description = ""
authors = ["Test <test@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
pylint = { version = "*", optional = true }

[tool.poetry.extras]
lint = ["pylint"]
""".lstrip(),
            encoding="utf-8",
        )

        # Run the CLI as a user would.
        result = subprocess.run(
            ["poetry", "remove", "pylint"],
            cwd=project_dir,
            capture_output=True,
            text=True,
        )

        # The primary signal that the bug is fixed is a successful exit code.
        assert (
            result.returncode == 0
        ), f"'poetry remove pylint' failed.\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}"

```

Depending on the existing test conventions in the Poetry codebase, you may want to:
1. Move this test into the existing CLI/integration test package (e.g. `tests/console/` or similar) and adjust the module name accordingly.
2. Replace the direct `subprocess.run` usage with Poetry's existing CLI test harness/runner (for example, a `CommandTester`, `CliRunner`, or repository-specific helper) if one is already used elsewhere for `poetry` CLI tests.
3. Align the marker (`@pytest.mark.integration`) with whatever marker or naming convention the project uses for end‑to‑end or CLI tests (e.g. `@pytest.mark.e2e`, `@pytest.mark.cli`, or none at all).
4. If the test suite assumes `poetry` is invoked via a specific entry point (like `python -m poetry` or a helper function), adjust the `subprocess.run` arguments to match that convention.
</issue_to_address>

### Comment 2
<location> `test_optional_dependency_removal.py:10-11` </location>
<code_context>
+project = Project('test_project')
+poetry = Factory().create_poetry(project)
+poetry.add_dependency('pylint', {'optional': True})
+poetry.remove_dependency('pylint')
+print("Optional dependency removed successfully.")
\ No newline at end of file
</code_context>

<issue_to_address>
**suggestion (testing):** Add assertions to verify the dependency was actually removed instead of relying on the absence of an exception and a print.

Right now this test passes as long as no exception is raised and a message is printed. To make it a meaningful automated test, add an assertion that `pylint` is actually removed (e.g., it no longer appears in the dependencies collection or in the relevant section of `pyproject.toml` / lockfile).

Suggested implementation:

```python
# Test script to reproduce issue #10703 in poetry
# Expected: poetry remove pylint should succeed
from poetry.factory import Factory
from poetry.project.project import Project

# Simulated project setup
project = Project('test_project')
poetry = Factory().create_poetry(project)

# Add optional dependency
poetry.add_dependency('pylint', {'optional': True})
# Sanity check that the dependency was added
assert 'pylint' in poetry.package.dependencies

# Remove the dependency
poetry.remove_dependency('pylint')

# Assert that the dependency was actually removed
assert 'pylint' not in poetry.package.dependencies

```

If the Poetry API in your codebase exposes dependencies differently (for example via `poetry.locker`, `poetry.pyproject`, or another attribute instead of `poetry.package.dependencies`), adjust the two `assert` lines to target the correct collection or pyproject/lockfile representation.

For example, you might need to change them to something like:
- `assert 'pylint' in poetry.pyproject.poetry_config['tool']['poetry']['dependencies']`
- `assert 'pylint' not in poetry.pyproject.poetry_config['tool']['poetry']['dependencies']`

depending on how `add_dependency` and `remove_dependency` are implemented in your version of Poetry.
</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.

@@ -0,0 +1,11 @@
# Test script to reproduce issue #10703 in poetry
# Expected: poetry remove pylint should succeed
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): The test should reflect the actual CLI behavior described in the PR (using poetry remove), not just the internal API.

The comment’s expectation is about the CLI (poetry remove pylint), but this script only exercises internal classes. To reliably demonstrate the CLI bug is fixed, consider adding an integration/end-to-end test that runs poetry remove pylint (or equivalent) via the existing CLI test harness in a temporary project and asserts on exit code and output, matching the behavior described in issue #10703.

Suggested implementation:

import subprocess
import tempfile
from pathlib import Path

import pytest


@pytest.mark.integration
def test_optional_dependency_removal_via_cli_succeeds():
    """
    Reproduces issue #10703 via the CLI.

    The expectation from the original report is that running:
        poetry remove pylint
    in a project where pylint is declared as an optional dependency
    should succeed (exit code 0) instead of erroring.
    """
    with tempfile.TemporaryDirectory() as tmp_dir:
        project_dir = Path(tmp_dir)

        pyproject = project_dir / "pyproject.toml"
        pyproject.write_text(
            """
[tool.poetry]
name = "test_project"
version = "0.1.0"
description = ""
authors = ["Test <test@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
pylint = { version = "*", optional = true }

[tool.poetry.extras]
lint = ["pylint"]
""".lstrip(),
            encoding="utf-8",
        )

        # Run the CLI as a user would.
        result = subprocess.run(
            ["poetry", "remove", "pylint"],
            cwd=project_dir,
            capture_output=True,
            text=True,
        )

        # The primary signal that the bug is fixed is a successful exit code.
        assert (
            result.returncode == 0
        ), f"'poetry remove pylint' failed.\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}"

Depending on the existing test conventions in the Poetry codebase, you may want to:

  1. Move this test into the existing CLI/integration test package (e.g. tests/console/ or similar) and adjust the module name accordingly.
  2. Replace the direct subprocess.run usage with Poetry's existing CLI test harness/runner (for example, a CommandTester, CliRunner, or repository-specific helper) if one is already used elsewhere for poetry CLI tests.
  3. Align the marker (@pytest.mark.integration) with whatever marker or naming convention the project uses for end‑to‑end or CLI tests (e.g. @pytest.mark.e2e, @pytest.mark.cli, or none at all).
  4. If the test suite assumes poetry is invoked via a specific entry point (like python -m poetry or a helper function), adjust the subprocess.run arguments to match that convention.

Comment on lines +10 to +11
poetry.remove_dependency('pylint')
print("Optional dependency removed successfully.") No newline at end of file
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Add assertions to verify the dependency was actually removed instead of relying on the absence of an exception and a print.

Right now this test passes as long as no exception is raised and a message is printed. To make it a meaningful automated test, add an assertion that pylint is actually removed (e.g., it no longer appears in the dependencies collection or in the relevant section of pyproject.toml / lockfile).

Suggested implementation:

# Test script to reproduce issue #10703 in poetry
# Expected: poetry remove pylint should succeed
from poetry.factory import Factory
from poetry.project.project import Project

# Simulated project setup
project = Project('test_project')
poetry = Factory().create_poetry(project)

# Add optional dependency
poetry.add_dependency('pylint', {'optional': True})
# Sanity check that the dependency was added
assert 'pylint' in poetry.package.dependencies

# Remove the dependency
poetry.remove_dependency('pylint')

# Assert that the dependency was actually removed
assert 'pylint' not in poetry.package.dependencies

If the Poetry API in your codebase exposes dependencies differently (for example via poetry.locker, poetry.pyproject, or another attribute instead of poetry.package.dependencies), adjust the two assert lines to target the correct collection or pyproject/lockfile representation.

For example, you might need to change them to something like:

  • assert 'pylint' in poetry.pyproject.poetry_config['tool']['poetry']['dependencies']
  • assert 'pylint' not in poetry.pyproject.poetry_config['tool']['poetry']['dependencies']

depending on how add_dependency and remove_dependency are implemented in your version of Poetry.

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.

1 participant