Source code for wauth.deprecation
"""Deprecation utilities for WAuth.
Provides a decorator and helper functions to mark deprecated APIs
with proper warnings, enabling graceful migration across versions.
"""
import functools
import warnings
from typing import Any, Callable, TypeVar
from ._log import _debug, _error, _info, _warning
T = TypeVar("T", bound=Callable[..., Any])
[docs]
def deprecated(
since: str,
removal: str,
replacement: str | None = None,
reason: str = "",
) -> Callable[[T], T]:
"""Mark a function or method as deprecated.
Emits a ``DeprecationWarning`` on first use and logs the deprecation
via loguru.
Args:
since: Version when the deprecation was introduced (e.g., ``"1.6.0"``).
removal: Version when the API will be removed (e.g., ``"2.0.0"``).
replacement: Name of the replacement API, if any.
reason: Additional explanation for the deprecation.
Returns:
Decorated function that emits a warning on invocation.
Example:
>>> @deprecated(since="1.6.0", removal="2.0.0", replacement="get_secret")
... def old_method():
... pass
"""
message = (
f"Deprecated since v{since}. "
f"Will be removed in v{removal}."
)
if replacement:
message += f" Use ``{replacement}`` instead."
if reason:
message += f" {reason}"
def decorator(func: T) -> T:
warned: bool = False
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
nonlocal warned
if not warned:
warnings.warn(message, DeprecationWarning, stacklevel=2)
_warning(f"DEPRECATED: {func.__name__}() — {message}")
warned = True
return func(*args, **kwargs)
# Expose the deprecation message for introspection
wrapper.__deprecated_message__ = message # type: ignore[attr-defined]
return wrapper # type: ignore[return-value]
return decorator
[docs]
def warn_deprecated(
name: str,
since: str,
removal: str,
replacement: str | None = None,
) -> None:
"""Emit a deprecation warning without a decorator.
Useful for code paths that cannot be decorated (e.g., inside
conditional branches).
Args:
name: Name of the deprecated element.
since: Version when the deprecation was introduced.
removal: Version when it will be removed.
replacement: Name of the replacement, if any.
"""
message = f"{name} is deprecated since v{since} and will be removed in v{removal}."
if replacement:
message += f" Use {replacement} instead."
warnings.warn(message, DeprecationWarning, stacklevel=3)
_warning(f"DEPRECATED: {message}")