File: deprecation.py

package info (click to toggle)
python-advanced-alchemy 1.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 3,708 kB
  • sloc: python: 25,811; makefile: 162; javascript: 123; sh: 4
file content (109 lines) | stat: -rw-r--r-- 3,911 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import inspect
from functools import wraps
from typing import Callable, Literal, Optional
from warnings import warn

from typing_extensions import ParamSpec, TypeVar

__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: Optional[str] = None,
    alternative: Optional[str] = None,
    info: Optional[str] = None,
    pending: bool = False,
) -> None:
    """Warn about a call to a (soon to be) deprecated function.

    Args:
        version: Advanced Alchemy version where the deprecation will occur
        deprecated_name: Name of the deprecated function
        removal_in: Advanced Alchemy version where the deprecated function will be removed
        alternative: Name of a function that should be used instead
        info: Additional information
        pending: Use :class:`warnings.PendingDeprecationWarning` instead of :class:`warnings.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}")  # pyright: ignore[reportUnknownMemberType]
    else:
        parts.append(f"{access_type} deprecated {kind} {deprecated_name!r}")  # pyright: ignore[reportUnknownMemberType]

    parts.extend(  # pyright: ignore[reportUnknownMemberType]
        (
            f"Deprecated in advanced-alchemy {version}",
            f"This {kind} will be removed in {removal_in or 'the next major version'}",
        ),
    )
    if alternative:
        parts.append(f"Use {alternative!r} instead")  # pyright: ignore[reportUnknownMemberType]

    if info:
        parts.append(info)  # pyright: ignore[reportUnknownMemberType]

    text = ". ".join(parts)  # pyright: ignore[reportUnknownArgumentType]
    warning_class = PendingDeprecationWarning if pending else DeprecationWarning

    warn(text, warning_class, stacklevel=2)


def deprecated(
    version: str,
    *,
    removal_in: Optional[str] = None,
    alternative: Optional[str] = None,
    info: Optional[str] = None,
    pending: bool = False,
    kind: Optional[Literal["function", "method", "classmethod", "property"]] = 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: Advanced Alchemy version where the deprecation will occur
        removal_in: Advanced Alchemy version where the deprecated function will be removed
        alternative: Name of a function that should be used instead
        info: Additional information
        pending: Use :class:`warnings.PendingDeprecationWarning` instead of :class:`warnings.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