1import functools
2import warnings
3
4__all__ = ["deprecated"]
5
6
7class deprecated:
8 """Decorator to mark a function or class as deprecated.
9
10 Issue a warning when the function is called/the class is instantiated and
11 adds a warning to the docstring.
12
13 The optional extra argument will be appended to the deprecation message
14 and the docstring. Note: to use this with the default value for extra, put
15 in an empty of parentheses:
16
17 >>> from sklearn.utils import deprecated
18 >>> deprecated()
19 <sklearn.utils.deprecation.deprecated object at ...>
20
21 >>> @deprecated()
22 ... def some_function(): pass
23
24 Parameters
25 ----------
26 extra : str, default=''
27 To be added to the deprecation messages.
28 """
29
30 # Adapted from https://wiki.python.org/moin/PythonDecoratorLibrary,
31 # but with many changes.
32
33 def __init__(self, extra=""):
34 self.extra = extra
35
36 def __call__(self, obj):
37 """Call method
38
39 Parameters
40 ----------
41 obj : object
42 """
43 if isinstance(obj, type):
44 return self._decorate_class(obj)
45 elif isinstance(obj, property):
46 # Note that this is only triggered properly if the `property`
47 # decorator comes before the `deprecated` decorator, like so:
48 #
49 # @deprecated(msg)
50 # @property
51 # def deprecated_attribute_(self):
52 # ...
53 return self._decorate_property(obj)
54 else:
55 return self._decorate_fun(obj)
56
57 def _decorate_class(self, cls):
58 msg = "Class %s is deprecated" % cls.__name__
59 if self.extra:
60 msg += "; %s" % self.extra
61
62 new = cls.__new__
63
64 def wrapped(cls, *args, **kwargs):
65 warnings.warn(msg, category=FutureWarning)
66 if new is object.__new__:
67 return object.__new__(cls)
68 return new(cls, *args, **kwargs)
69
70 cls.__new__ = wrapped
71
72 wrapped.__name__ = "__new__"
73 wrapped.deprecated_original = new
74
75 return cls
76
77 def _decorate_fun(self, fun):
78 """Decorate function fun"""
79
80 msg = "Function %s is deprecated" % fun.__name__
81 if self.extra:
82 msg += "; %s" % self.extra
83
84 @functools.wraps(fun)
85 def wrapped(*args, **kwargs):
86 warnings.warn(msg, category=FutureWarning)
87 return fun(*args, **kwargs)
88
89 # Add a reference to the wrapped function so that we can introspect
90 # on function arguments in Python 2 (already works in Python 3)
91 wrapped.__wrapped__ = fun
92
93 return wrapped
94
95 def _decorate_property(self, prop):
96 msg = self.extra
97
98 @property
99 @functools.wraps(prop)
100 def wrapped(*args, **kwargs):
101 warnings.warn(msg, category=FutureWarning)
102 return prop.fget(*args, **kwargs)
103
104 return wrapped
105
106
107def _is_deprecated(func):
108 """Helper to check if func is wrapped by our deprecated decorator"""
109 closures = getattr(func, "__closure__", [])
110 if closures is None:
111 closures = []
112 is_deprecated = "deprecated" in "".join(
113 [c.cell_contents for c in closures if isinstance(c.cell_contents, str)]
114 )
115 return is_deprecated