from __future__ import annotations import inspect from functools import wraps from typing import Callable, Literal, TypeVar from warnings import warn from typing_extensions import ParamSpec __all__ = ("deprecated", "warn_deprecation") T = TypeVar("T") P = ParamSpec("P") DeprecatedKind = Literal["function", "method", "classmethod", "attribute", "property", "class", "parameter", "import"] def warn_deprecation( version: str, deprecated_name: str, kind: DeprecatedKind, *, removal_in: str | None = None, alternative: str | None = None, info: str | None = None, pending: bool = False, ) -> None: """Warn about a call to a (soon to be) deprecated function. Args: version: Litestar version where the deprecation will occur deprecated_name: Name of the deprecated function removal_in: Litestar version where the deprecated function will be removed alternative: Name of a function that should be used instead info: Additional information pending: Use ``PendingDeprecationWarning`` instead of ``DeprecationWarning`` kind: Type of the deprecated thing """ parts = [] if kind == "import": access_type = "Import of" elif kind in {"function", "method"}: access_type = "Call to" else: access_type = "Use of" if pending: parts.append(f"{access_type} {kind} awaiting deprecation {deprecated_name!r}") else: parts.append(f"{access_type} deprecated {kind} {deprecated_name!r}") parts.extend( ( f"Deprecated in litestar {version}", f"This {kind} will be removed in {removal_in or 'the next major version'}", ) ) if alternative: parts.append(f"Use {alternative!r} instead") if info: parts.append(info) text = ". ".join(parts) warning_class = PendingDeprecationWarning if pending else DeprecationWarning warn(text, warning_class, stacklevel=2) def deprecated( version: str, *, removal_in: str | None = None, alternative: str | None = None, info: str | None = None, pending: bool = False, kind: Literal["function", "method", "classmethod", "property"] | None = None, ) -> Callable[[Callable[P, T]], Callable[P, T]]: """Create a decorator wrapping a function, method or property with a warning call about a (pending) deprecation. Args: version: Litestar version where the deprecation will occur removal_in: Litestar version where the deprecated function will be removed alternative: Name of a function that should be used instead info: Additional information pending: Use ``PendingDeprecationWarning`` instead of ``DeprecationWarning`` kind: Type of the deprecated callable. If ``None``, will use ``inspect`` to figure out if it's a function or method Returns: A decorator wrapping the function call with a warning """ def decorator(func: Callable[P, T]) -> Callable[P, T]: @wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> T: warn_deprecation( version=version, deprecated_name=func.__name__, info=info, alternative=alternative, pending=pending, removal_in=removal_in, kind=kind or ("method" if inspect.ismethod(func) else "function"), ) return func(*args, **kwargs) return wrapped return decorator