New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional parenthesis for @command
and @option
#127
Comments
I know they added it some time ago but I don't like this pattern:
I know some popular libraries like pytest use it (but this is probably the least magical thing about pytest), but I'd gladly avoid it. A good reason to add it is to be consistent with Click. And that's a pretty good reason. Still, I'd rather provide a good error message as the original issue and PR proposed. This is somewhat needed now that the syntax is allowed in Click. |
I might change my mind on this, given that even the standard library embraces this pattern (e.g. (*) it's basically overloading with radically different return types, something similar to Union return types, which I consider code smell. |
Oh yes. I agree with you sentiment. Implementing these naked decorators is a chore, even if it makes them beautiful from the user point of view. That's why I classify them as nice-to-have, not critical. I don't even have a clue on how to implement them! 😅 Good point on |
When you don't care about static typing and IDE autocompletion, it's pretty easy to implement. One can write a write a generic decorator, but you'll lose auto-completion: from functools import wraps
from typing import Any, Callable, TypeVar
AnyCallable = Callable[..., Any]
F = TypeVar('F', bound=AnyCallable)
"""Type variable for a Callable."""
Decorator = Callable[[F], F]
DecoratorFactory = Callable[..., Decorator[F]]
def allow_missing_parenthesis(dec_factory):
@wraps(dec_factory)
def new_factory(*args, **kwargs):
if args and callable(args[0]):
return dec_factory(*args[1:], **kwargs)(args[0])
return dec_factory(*args, **kwargs)
return new_factory
#
# Usage example
#
@allow_missing_parenthesis
def decorator_factory(name="<default name>") -> Decorator[F]:
"""A dummy decorator factory."""
def decorator(f: F) -> F:
@wraps(f)
def wrapper(*args, **kwargs):
f(*args, **kwargs)
print(f"I'm {name}.")
print("---")
return wrapper
return decorator
@decorator_factory
def without_parenthesis(msg="[@decorator_factory]"):
print(msg)
@decorator_factory()
def empty_args(msg="[@decorator_factory()]"):
print(msg)
@decorator_factory("janluke")
def with_args(msg='[@decorator_factory("janluke")]'):
print(msg)
without_parenthesis()
empty_args()
with_args() Integrating the feature into Cloup and make all mypy type checks pass is more complicated. I won't do that work for such a cosmetic feature (which I don't even want or like). |
Thanks @janluke for the code example! I'm quite bad at decorators so it took me a while to digest and integrate. I took the liberty to reuse some of your proposed code to add to This helped me to clone Thanks again @janluke ! 🤗 |
Since Click 8.1.0, we can use
command
andgroup
decorators without parenthesis. See:Nothing important here but a nice little nitpick. Not certain if it is worth considering for integration in
cloup
.The text was updated successfully, but these errors were encountered: