import functools import warnings __all__ = ["deprecated"] class deprecated: """Decorator to mark a function or class as deprecated. Issue a warning when the function is called/the class is instantiated and adds a warning to the docstring. The optional extra argument will be appended to the deprecation message and the docstring. Note: to use this with the default value for extra, put in an empty of parentheses: Examples -------- >>> from sklearn.utils import deprecated >>> deprecated() >>> @deprecated() ... def some_function(): pass Parameters ---------- extra : str, default='' To be added to the deprecation messages. """ # Adapted from https://wiki.python.org/moin/PythonDecoratorLibrary, # but with many changes. def __init__(self, extra=""): self.extra = extra def __call__(self, obj): """Call method Parameters ---------- obj : object """ if isinstance(obj, type): return self._decorate_class(obj) elif isinstance(obj, property): # Note that this is only triggered properly if the `deprecated` # decorator is placed before the `property` decorator, like so: # # @deprecated(msg) # @property # def deprecated_attribute_(self): # ... return self._decorate_property(obj) else: return self._decorate_fun(obj) def _decorate_class(self, cls): msg = "Class %s is deprecated" % cls.__name__ if self.extra: msg += "; %s" % self.extra new = cls.__new__ def wrapped(cls, *args, **kwargs): warnings.warn(msg, category=FutureWarning) if new is object.__new__: return object.__new__(cls) return new(cls, *args, **kwargs) cls.__new__ = wrapped wrapped.__name__ = "__new__" wrapped.deprecated_original = new return cls def _decorate_fun(self, fun): """Decorate function fun""" msg = "Function %s is deprecated" % fun.__name__ if self.extra: msg += "; %s" % self.extra @functools.wraps(fun) def wrapped(*args, **kwargs): warnings.warn(msg, category=FutureWarning) return fun(*args, **kwargs) # Add a reference to the wrapped function so that we can introspect # on function arguments in Python 2 (already works in Python 3) wrapped.__wrapped__ = fun return wrapped def _decorate_property(self, prop): msg = self.extra @property @functools.wraps(prop) def wrapped(*args, **kwargs): warnings.warn(msg, category=FutureWarning) return prop.fget(*args, **kwargs) return wrapped def _is_deprecated(func): """Helper to check if func is wrapped by our deprecated decorator""" closures = getattr(func, "__closure__", []) if closures is None: closures = [] is_deprecated = "deprecated" in "".join( [c.cell_contents for c in closures if isinstance(c.cell_contents, str)] ) return is_deprecated # TODO: remove in 1.7 def _deprecate_Xt_in_inverse_transform(X, Xt): """Helper to deprecate the `Xt` argument in favor of `X` in inverse_transform.""" if X is not None and Xt is not None: raise TypeError("Cannot use both X and Xt. Use X only.") if X is None and Xt is None: raise TypeError("Missing required positional argument: X.") if Xt is not None: warnings.warn( "Xt was renamed X in version 1.5 and will be removed in 1.7.", FutureWarning, ) return Xt return X