Source code for secml.core.decorators

import warnings
import functools

__all__ = ["deprecated"]


[docs]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: >>> from secml.core.decorators import deprecated >>> deprecated() # doctest: +ELLIPSIS <secml.core.decorators.deprecated object at ...> >>> @deprecated() ... def some_function(): pass Parameters ---------- version : str Version since which the function or class is deprecated. extra : str, optional Extra text to be added to the deprecation messages. Notes ----- Adapted from: - https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/utils/deprecation.py - https://wiki.python.org/moin/PythonDecoratorLibrary """ def __init__(self, version, extra=''): self.extra = extra self.version = version def __call__(self, obj): """Call method. Parameters ---------- obj : class or function The object to decorate. Can be a class or a function. """ if isinstance(obj, type): return self._decorate_class(obj) else: return self._decorate_fun(obj) def _decorate_class(self, cls): """Decorate class clf.""" msg = "class `{:}` is deprecated since version {:}".format( cls.__name__, self.version) if self.extra: msg += "; %s" % self.extra warnings.filterwarnings( 'once', message=msg, category=DeprecationWarning) # FIXME: we should probably reset __new__ for full generality init = cls.__init__ def wrapped(*args, **kwargs): warnings.warn(msg, category=DeprecationWarning) return init(*args, **kwargs) cls.__init__ = wrapped wrapped.__name__ = '__init__' wrapped.deprecated_original = init cls.__doc__ = self._update_doc(cls.__doc__) return cls def _decorate_fun(self, fun): """Decorate function fun.""" msg = "function `{:}` is deprecated since version {:}".format( fun.__name__, self.version) if self.extra: msg += "; %s" % self.extra warnings.filterwarnings( 'once', message=msg, category=DeprecationWarning) @functools.wraps(fun) def wrapped(*args, **kwargs): warnings.warn(msg, category=DeprecationWarning) return fun(*args, **kwargs) wrapped.__doc__ = self._update_doc(wrapped.__doc__) # 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 _update_doc(self, olddoc): """Update the docstring of the class/function adding 'Deprecated since version XX' + the extra optional text.""" newdoc = ".. deprecated:: {:}".format(self.version) if self.extra: newdoc = "%s\n %s" % (newdoc, self.extra) if olddoc: newdoc = "%s\n\n%s" % (newdoc, olddoc) else: # A docstring, even empty, is required for correct visualization newdoc = "%s\n%s" % (newdoc, '""""""') return newdoc