Make command decorators interchangeable with Click's - #238
Merged
Conversation
Click allows `@command(name, CommandCls)`; Cloup made every argument but `name`
keyword-only in 0.9.0, so the same call raised TypeError. That made Cloup's
decorators non-substitutable for Click's, which matters now that Cloup
re-exports Click's top-level namespace.
Move `cls` ahead of the keyword-only marker in the no-`cls` overloads and the
implementations, and add an overload for the positional form.
That overload repeats the keyword arguments of the keyword-`cls` overload rather
than collapsing them into `**kwargs` the way Click's does. Click's shape also
matches `name` positional plus `cls` by keyword, which would shadow the precise
overload and silently stop type-checking standard keyword arguments whenever a
`cls` was passed at all. Marking the parameters positional-only avoids that but
forces every implementation signature to become `(*args, **kwargs)`, since MyPy
rejects a named implementation under a positional-only overload. Repeating the
list keeps both call styles checked and leaves the implementations readable:
`command("n", MyCmd, hidden="yes")` is now reported, which Click itself does not
report.
Claude-Session: https://claude.ai/code/session_017ynEswQdUZFthFUdh7fKAW
Click has supported `@command` without parentheses since 8.1. Cloup raised a custom exception telling the user to add them, so the most common line in any Click application failed at import time when the import was switched to Cloup. Accept a callback in place of `name` in all four decorators, via a shared `_resolve_decorator_first_arg` helper, and add an overload for the bare form. The "you forgot parenthesis" error and its tests go away: the form it guarded against is now valid. The bare overload also drops both `# type: ignore[override]` on Group.command and Group.group. Click's base overloads are typed `(*args: Any, **kwargs: Any)`, which MyPy reads as an unspecified signature that any override may narrow -- but its first overload returns a command rather than a decorator, so an override had to cover that shape before MyPy would accept it. Pyrefly implements no such leniency, not even for a plain non-overloaded `(*args: Any, **kwargs: Any)`, so its two suppressions stay. Removing them would take a trailing `(*args: Any, **kwargs: Any)` overload, which would silence every keyword argument error on these two methods. Claude-Session: https://claude.ai/code/session_017ynEswQdUZFthFUdh7fKAW
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #238 +/- ##
==========================================
+ Coverage 96.87% 96.88% +0.01%
==========================================
Files 22 22
Lines 1633 1639 +6
==========================================
+ Hits 1582 1588 +6
Misses 51 51 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Starting with v4.0.0, Cloup will make an effort to be interchangeable with Click. This PR aligns Cloup with Click in how you can call command decorators. There are indeed two (undocumented) differences right now:
clscan be passed as 2nd positional argument; in Cloup, it must be a keyword argument.