I have a simple decorator constructed like so:
def public_api(stage: Literal["alpha", "beta", "stable"] = "stable"):
"""Decorate public APIs with versioning.
Parameters
----------
stage : {'alpha', 'beta', 'stable'}, default='stable'
Stage of the API.
"""
logger = logging.getLogger(__name__)
_pub_api_tracker = threading.local()
def decorator(wrapped: Callable):
@wrapt.decorator
def wrapper(wrapped, _instance, args, kwargs):
if stage != "stable" and not hasattr(_pub_api_tracker, "used"):
logger.info(
f"{stage.title()} API: {wrapped.__module__}.{wrapped.__qualname__} "
f"is in {stage} stage and may have breaking changes."
)
_pub_api_tracker.used = True
if stage == "stable":
# Verify that the function or method is fully type annotated
sig = inspect.signature(wrapped)
for param in sig.parameters.values():
if param.annotation is param.empty:
raise TypeError(f"Parameter '{param.name}' in {wrapped.__name__} is missing a type annotation.")
if sig.return_annotation is sig.empty:
raise TypeError(f"Return type of {wrapped.__name__} is missing a type annotation.")
return wrapped(*args, **kwargs)
return wrapper(wrapped)
return decorator
I can of course use the default argument of this decorator by applying it like so:
@public_api()
def my_func(input: str) -> str:
return input
However, it does not behave well when used like this:
@public_api
def my_func(input: str) -> str:
return input
Is there a way to make the latter work as well without losing all of the clean typing on the decorator?
I have a simple decorator constructed like so:
I can of course use the default argument of this decorator by applying it like so:
However, it does not behave well when used like this:
Is there a way to make the latter work as well without losing all of the clean typing on the decorator?