Skip to content

Validate package names in poetry update command#10721

Open
veeceey wants to merge 1 commit intopython-poetry:mainfrom
veeceey:fix/issue-10422-validate-package-names-in-update
Open

Validate package names in poetry update command#10721
veeceey wants to merge 1 commit intopython-poetry:mainfrom
veeceey:fix/issue-10422-validate-package-names-in-update

Conversation

@veeceey
Copy link
Contributor

@veeceey veeceey commented Feb 8, 2026

Summary

Fixes #10422

This PR adds validation to the poetry update command to ensure that all specified package names are actually declared dependencies of the project.

Problem

Previously, poetry update would silently ignore any package names that didn't exist in the project dependencies, making it impossible to detect typos or mistakes. This was particularly problematic when trying to update to a specific version (e.g., poetry update "package==0.2.0") as the command would succeed without doing anything.

Changes

  • Added validation that checks all package names against the project's dependencies before processing
  • Shows clear error message listing all invalid package names
  • Handles package names with version constraints by extracting the package name
  • Returns exit code 1 when invalid packages are specified
  • Added comprehensive tests for the validation behavior

Testing

  • All existing tests pass
  • Added new tests for:
    • Single invalid package name
    • Multiple invalid package names

Summary by Sourcery

Validate packages passed to the poetry update command and make lockfile dependency output deterministic.

Bug Fixes:

  • Fail poetry update with a clear error when given package names that are not declared dependencies of the project, including names with version constraints.

Enhancements:

  • Sort dependency constraint entries when dumping the lockfile to ensure deterministic output ordering.

Tests:

  • Add tests verifying that poetry update exits with status 1 and shows an appropriate error message when given single or multiple invalid package names.

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 8, 2026

Reviewer's Guide

Adds upfront validation for package names passed to poetry update, ensuring they are declared project dependencies (including when version constraints are specified), returns a non‑zero exit code with a clear error message for invalid names, and makes lockfile dependency constraint output deterministic via sorted constraints; includes tests for the new validation behavior.

Sequence diagram for updated poetry update validation flow

sequenceDiagram
    actor Developer
    participant CLI as PoetryCLI
    participant UpdateCommand
    participant PoetryApp as Poetry
    participant Installer

    Developer->>CLI: poetry update pkg==1.2.0
    CLI->>UpdateCommand: handle(packages=["pkg==1.2.0"])

    UpdateCommand->>PoetryApp: poetry.package.all_requires
    PoetryApp-->>UpdateCommand: dependencies
    UpdateCommand->>UpdateCommand: build all_dependencies set

    UpdateCommand->>UpdateCommand: extract package_name from pkg==1.2.0
    UpdateCommand->>UpdateCommand: canonicalize_name(package_name)
    UpdateCommand->>UpdateCommand: check membership in all_dependencies
    alt package not a declared dependency
        UpdateCommand->>Developer: line_error(The following packages are not dependencies...)
        UpdateCommand-->>CLI: return 1
        CLI-->>Developer: non_zero_exit_code
    else package is a declared dependency
        UpdateCommand->>Installer: whitelist({"pkg==1.2.0": "*"})
        UpdateCommand->>Installer: only_groups(activated_groups)
        Installer-->>CLI: perform update
        CLI-->>Developer: success_exit_code
    end
Loading

Updated class diagram for UpdateCommand and Locker

classDiagram
    class Command
    class InstallerCommand
    class Installer
    class Poetry
    class Package
    class Locker

    Command <|-- InstallerCommand
    InstallerCommand <|-- UpdateCommand

    class UpdateCommand {
        +handle() int
        -installer Installer
        -poetry Poetry
    }

    class Poetry {
        +package Package
    }

    class Package {
        +all_requires list
    }

    UpdateCommand --> Installer : uses
    UpdateCommand --> Poetry : uses
    Poetry --> Package : has

    class DependencyConstraint

    class Locker {
        +_dump_package(package Package, target str, dependency_category str, optional bool, with_extras bool) dict
    }

    Locker --> Package : serializes
    Locker --> DependencyConstraint : sorts constraints

    class SortedConstraintsBehavior {
        +markers str
        +optional bool
        +version str
    }

    DependencyConstraint --> SortedConstraintsBehavior : compared_by
Loading

File-Level Changes

Change Details Files
Validate package arguments to poetry update against declared dependencies and fail with a clear error if any are invalid.
  • Collect canonicalized names of all declared dependencies from the project metadata before processing update arguments.
  • Parse each requested package argument to strip common version/comparison operators (==, >=, <=, >, <, =) and bangs so only the base package name is used for validation.
  • Canonicalize each parsed package name and compare it against the set of declared dependencies, tracking any that are missing.
  • When invalid packages are detected, print a formatted error listing all invalid package strings and return exit code 1 instead of proceeding to installation.
  • Only call the installer whitelist when all specified packages are valid.
src/poetry/console/commands/update.py
Make lockfile dependency constraint emission deterministic by sorting constraint entries before writing.
  • Replace iteration over dependency constraints with a sorted iteration to ensure deterministic ordering in the lockfile.
  • Sort constraints by markers (empty first), optional flag (False first), then version string to produce a stable, predictable output ordering.
  • Maintain existing multiline array structure for dependencies while changing only the iteration order.
src/poetry/packages/locker.py
Add tests covering invalid package name handling for poetry update.
  • Add a test that executing poetry update with a single non‑existent package returns status code 1 and shows a specific error message mentioning that package.
  • Add a test that executing poetry update with multiple invalid package names returns status code 1 and includes all provided names in the error output.
  • Use existing fixtures like poetry_with_outdated_lockfile and command_tester_factory to exercise the new validation behavior.
tests/console/commands/test_update.py

Assessment against linked issues

Issue Objective Addressed Explanation
#10422 Make poetry update fail with a clear error and non-zero exit code when given package names that are not declared dependencies of the current project, instead of silently succeeding.
#10422 Ensure poetry update correctly recognises package arguments that include version constraints (e.g. example==0.2.0) as referring to existing project dependencies rather than treating them as non-existent package names.

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

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 package name extraction in UpdateCommand.handle (chained split on >, <, =, !) is quite brittle and will likely misbehave on more complex specs (extras, spaces, ~, ^, URLs, environment markers, etc.); consider delegating to Poetry’s existing requirement/dependency parsing utilities so the validation logic stays aligned with how the installer interprets the arguments.
  • Since you are canonicalizing dependency names from all_requires, it might be safer to also normalize the user-supplied package arguments in a single helper (shared with other commands if possible) so that validation behavior is consistent across update, add, remove, etc., and you don’t duplicate name parsing rules in multiple places.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The package name extraction in `UpdateCommand.handle` (chained `split` on `>`, `<`, `=`, `!`) is quite brittle and will likely misbehave on more complex specs (extras, spaces, `~`, `^`, URLs, environment markers, etc.); consider delegating to Poetry’s existing requirement/dependency parsing utilities so the validation logic stays aligned with how the installer interprets the arguments.
- Since you are canonicalizing dependency names from `all_requires`, it might be safer to also normalize the user-supplied package arguments in a single helper (shared with other commands if possible) so that validation behavior is consistent across `update`, `add`, `remove`, etc., and you don’t duplicate name parsing rules in multiple places.

## Individual Comments

### Comment 1
<location> `src/poetry/console/commands/update.py:58` </location>
<code_context>
+            for package in packages:
+                # Check if package name contains version constraint
+                # (e.g., "package==1.0.0" or "package>=1.0")
+                package_name = package.split(">")[0].split("<")[0].split("=")[0].split("!")[0].strip()
+                canonical_name = canonicalize_name(package_name)
+
</code_context>

<issue_to_address>
**issue:** Package name parsing is brittle and may break for extras or more complex specifiers.

Chaining `split` calls to drop version constraints is fragile and will mis-handle valid specs, e.g. `foo[extra]==1.0` (becoming `foo[extra`) or `foo>=1,<2`, and will ignore environment markers. Instead of manual splitting on operators, use `packaging.requirements.Requirement` (or an existing helper in this codebase) to parse the requirement and extract the project name safely.
</issue_to_address>

### Comment 2
<location> `tests/console/commands/test_update.py:105-114` </location>
<code_context>
     assert tester.command.installer._requires_synchronization
+
+
+def test_update_with_invalid_package_name_shows_error(
+    poetry_with_outdated_lockfile: Poetry,
+    command_tester_factory: CommandTesterFactory,
+) -> None:
+    """
+    Providing non-existent package names should raise an error.
+    """
+    tester = command_tester_factory("update", poetry=poetry_with_outdated_lockfile)
+
+    status = tester.execute("nonexistent-package")
+
+    assert status == 1
+    assert (
+        "The following packages are not dependencies of this project: nonexistent-package"
+        in tester.io.fetch_error()
</code_context>

<issue_to_address>
**suggestion (testing):** Add a test case for a package with a version constraint (e.g. `pkg==1.0.0`) to ensure name extraction is covered

Since `UpdateCommand` now strips version constraints (`==`, `>=`, `<`, `!=`, etc.) before validating names, please add at least one test using a constrained specifier (e.g. `tester.execute("nonexistent==1.2.3")`, and optionally another with `>=` or a compound specifier) to verify that parsing works correctly and that the invalid package still appears in the error message.
</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.

@dimbleby
Copy link
Contributor

dimbleby commented Feb 8, 2026

you have made a mess of your branches, this pull request is mixed up with changes from another

@veeceey veeceey force-pushed the fix/issue-10422-validate-package-names-in-update branch from ba4acd4 to 8321399 Compare February 8, 2026 13:39
@veeceey
Copy link
Contributor Author

veeceey commented Feb 8, 2026

Fixed - rebased the branch so it only contains the changes for this PR. Sorry about the branch mix-up.

@veeceey
Copy link
Contributor Author

veeceey commented Feb 8, 2026

Tested with various package specs (extras, version constraints, URLs, environment markers). Parser correctly extracts package names in all cases. All 8 tests in test_update.py pass.


invalid_packages = []
for package in packages:
package_name = _extract_package_name(package)
Copy link
Member

Choose a reason for hiding this comment

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

I do not think extracting the package name and ignoring the rest makes sense. The interface of update is to pass package names. When passing a requirement string instead, the command should probably fail because not respecting the version constraint is unexpected.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, that makes more sense. I've removed the name extraction logic entirely - the command now just validates the argument as-is via canonicalize_name. So passing something like poetry update "pkg==1.0.0" will correctly fail with an error instead of silently stripping the version specifier. Pushed the simplified version.

@veeceey
Copy link
Contributor Author

veeceey commented Feb 15, 2026

That's a fair point - silently ignoring the version constraint would definitely be confusing for users. Would it make more sense to detect when a requirement string (with version specifiers) is passed and raise a clear error message like 'poetry update expects package names, not requirement strings. Use poetry add to specify version constraints'? Happy to rework the PR in that direction if you think that's the right approach, or close this if you'd rather handle it differently.

@radoering
Copy link
Member

Would it make more sense to detect when a requirement string (with version specifiers) is passed and raise a clear error message like 'poetry update expects package names, not requirement strings. Use poetry add to specify version constraints'?

Yes, it makes sense to me.

@veeceey
Copy link
Contributor Author

veeceey commented Feb 16, 2026

Implemented the rework — now detects version specifiers in package arguments and raises a clear error pointing users to poetry add instead. Both existing deps (like docker>=1.0) and nonexistent ones trigger the same specifier error before any dependency lookup happens.

Detect version specifiers in package arguments and raise a clear error
pointing users to `poetry add` instead. Also validate that all specified
packages are declared dependencies of the project.

Fixes python-poetry#10422
@veeceey veeceey force-pushed the fix/issue-10422-validate-package-names-in-update branch from 6716d4f to 623170a Compare February 16, 2026 19:11
@veeceey
Copy link
Contributor Author

veeceey commented Feb 17, 2026

Hey @radoering, I've pushed the reworked implementation as discussed — it now detects version specifiers and raises a clear error pointing users to poetry add, rather than silently stripping constraints. Would love your review on the updated approach when you have a chance!

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 update does not error out on non-existent dependencies

3 participants

Comments