|
10 | 10 | _builtin_deprecated = None |
11 | 11 |
|
12 | 12 |
|
13 | | -def deprecated(*, alt_name=None, message=None): |
14 | | - """Marks a function or class as deprecated. |
| 13 | +def deprecated(message, *, category=DeprecationWarning, stacklevel=1): |
| 14 | + """Compatibility wrapper for Python <3.13. |
15 | 15 |
|
16 | | - Emits a DeprecationWarning whenever the decorated function is called |
17 | | - or the decorated class is instantiated. |
18 | | -
|
19 | | - Parameters |
20 | | - ---------- |
21 | | - alt_name : str, optional |
22 | | - Name of the recommended alternative. |
23 | | - message : str, optional |
24 | | - Custom deprecation message. If None, a default message is generated. |
25 | | -
|
26 | | - Returns |
27 | | - ------- |
28 | | - decorator : function |
29 | | - Decorator that wraps the deprecated object. |
30 | | -
|
31 | | - Examples |
32 | | - -------- |
33 | | - .. code-block:: python |
34 | | -
|
35 | | - from diffpy._deprecations import deprecated |
36 | | -
|
37 | | - # ------------------------------ |
38 | | - # Deprecated function |
39 | | - # ------------------------------ |
40 | | - @deprecated(alt_name="new_function") |
41 | | - def old_function(x, y): |
42 | | - return x + y |
43 | | -
|
44 | | - def new_function(x, y): |
45 | | - return x + y |
46 | | -
|
47 | | - # Usage |
48 | | - old_function(1, 2) # Emits DeprecationWarning |
49 | | - new_function(1, 2) # No warning |
50 | | -
|
51 | | - # ------------------------------ |
52 | | - # Deprecated class |
53 | | - # ------------------------------ |
54 | | - @deprecated(alt_name="NewAtom") |
55 | | - class OldAtom: |
56 | | - def __init__(self, symbol): |
57 | | - self.symbol = symbol |
58 | | -
|
59 | | - # Usage |
60 | | - a = OldAtom("C") # Emits DeprecationWarning |
61 | | - atom = NewAtom("C") # No warning |
| 16 | + Matches the Python 3.13 warnings.deprecated API exactly. |
62 | 17 | """ |
63 | | - if _builtin_deprecated: |
64 | | - return _builtin_deprecated |
| 18 | + # If Python 3.13 implementation exists, delegate to it |
| 19 | + if _builtin_deprecated is not None: |
| 20 | + return _builtin_deprecated( |
| 21 | + message, category=category, stacklevel=stacklevel |
| 22 | + ) |
65 | 23 |
|
66 | | - def decorator(obj): |
67 | | - name = getattr(obj, "__name__", repr(obj)) |
68 | | - msg = message or ( |
69 | | - f"'{name}' is deprecated. Use '{alt_name}' instead." |
70 | | - if alt_name |
71 | | - else f"'{name}' is deprecated." |
| 24 | + # Validate message type like Python 3.13 does |
| 25 | + if not isinstance(message, str): |
| 26 | + raise TypeError( |
| 27 | + f"Expected an object of type str for 'message', not " |
| 28 | + f"{type(message).__name__!r}" |
72 | 29 | ) |
73 | 30 |
|
| 31 | + def decorator(obj): |
| 32 | + # Set __deprecated__ attribute (required by PEP 702) |
| 33 | + setattr(obj, "__deprecated__", message) |
| 34 | + |
| 35 | + # Must support functions AND classes |
74 | 36 | if callable(obj): |
75 | 37 |
|
76 | 38 | @functools.wraps(obj) |
77 | 39 | def wrapper(*args, **kwargs): |
78 | | - warnings.warn(msg, DeprecationWarning, stacklevel=2) |
| 40 | + warnings.warn(message, category, stacklevel=stacklevel + 1) |
79 | 41 | return obj(*args, **kwargs) |
80 | 42 |
|
81 | 43 | return wrapper |
82 | | - else: |
83 | | - raise TypeError( |
84 | | - "deprecated decorator can only be applied to functions or " |
85 | | - "classes" |
86 | | - ) |
| 44 | + |
| 45 | + raise TypeError( |
| 46 | + "deprecated decorator can only be applied to functions or classes" |
| 47 | + ) |
87 | 48 |
|
88 | 49 | return decorator |
0 commit comments