Source code for deprecat.classic

"""
Classic deprecation warning
===========================

Classic ``@deprecat`` decorator to deprecate python classes, functions, methods or kwargs.

"""
import functools
import inspect
import platform
import warnings

import wrapt

try:
    # If the C extension for wrapt was compiled and wrapt/_wrappers.pyd exists, then the
    # stack level that should be passed to warnings.warn should be 2. However, if using
    # a pure python wrapt, a extra stacklevel is required.
    import wrapt._wrappers

    _routine_stacklevel = 2
    _class_stacklevel = 2
except ImportError:
    _routine_stacklevel = 3
    if platform.python_implementation() == "PyPy":
        _class_stacklevel = 2
    else:
        _class_stacklevel = 3

string_types = (type(b''), type(u''))


[docs] class ClassicAdapter(wrapt.AdapterFactory): """ Classic adapter is used to get the deprecation message according to the wrapped object type: class, function, standard method, static method, or class method. This is the base class of the :class:`~deprecat.sphinx.SphinxAdapter` class which is used to update the wrapped object docstring. You can also inherit this class to change the deprecation message. Parameters ---------- reason: str Reason for deprecation of this method or class. version: str Version of your project which deprecates this method or class. remove_version: str Version of your project which removes this method or class. action: str A warning filter used to specify the deprecation warning. Can be one of "error", "ignore", "always", "default", "module", or "once". If ``None`` or empty, the the global filtering mechanism is used. deprecated_args: dict Dictionary in the following format to deprecate `x` and `y` deprecated_args = {'x': {'reason': 'some reason','version': '1.0'}, 'y': {'reason': 'another reason','version': '2.0'}} category: class The warning category to use for the deprecation warning. By default, the category class is :class:`~DeprecationWarning`, you can inherit this class to define your own deprecation warning category. """ def __init__(self, reason="", version="", remove_version="", action=None, deprecated_args=None, category=DeprecationWarning): self.reason = reason self.version = version self.remove_version = remove_version self.action = action self.category = category self.deprecated_args = deprecated_args super(ClassicAdapter, self).__init__()
[docs] def get_deprecated_msg(self, wrapped, instance, kwargs): """ Get the deprecation warning message for the user. Parameters ---------- wrapped: object Wrapped class or function. instance: object The object to which the wrapped function was bound when it was called. kwargs: The kwargs of the wrapped function. Returns ------- The warning message. """ if instance is None: if inspect.isclass(wrapped): fmt = "Call to deprecated class {name}." else: fmt = "Call to deprecated function (or staticmethod) {name}." else: if inspect.isclass(instance): fmt = "Call to deprecated class method {name}." else: fmt = "Call to deprecated method {name}." if self.deprecated_args is None: name = wrapped.__name__ if self.reason != "": fmt += " ({reason})" if self.version != "": fmt += "\n-- Deprecated since version {version}." if self.remove_version != "": fmt += "\n-- Will be removed in version {remove_version}." return {f'{name}': fmt.format(name=name, reason=self.reason, version=self.version, remove_version=self.remove_version)} if self.deprecated_args is not None: self.argstodeprecate = set(self.deprecated_args.keys()).intersection(kwargs) if len(self.argstodeprecate)!=0: warningargs={} #store deprecation message for each argument for arg in self.argstodeprecate: name = arg fmt = "Call to deprecated Parameter {name}." r='' v='' rv='' if self.deprecated_args[arg].get('reason') is not None: r = self.deprecated_args[arg]['reason'] fmt += " ({reason})" if self.deprecated_args[arg].get('version') is not None: v = self.deprecated_args[arg]['version'] fmt += "\n-- Deprecated since v{version}." if self.deprecated_args[arg].get('remove_version') is not None: rv = self.deprecated_args[arg]['remove_version'] fmt += "\n-- Will be removed in version {remove_version}." warningargs[arg] = fmt.format(name=name, reason=r, version=v, remove_version=rv) else: name="" if name=="": return None else: return warningargs
[docs] def __call__(self, wrapped): """ Decorate your class or function. Parameters ---------- wrapped: object Wrapped class or function. Returns ------- Decorated class or function. """ if inspect.isclass(wrapped): old_new1 = wrapped.__new__ def wrapped_cls(cls, *args, **kwargs): msg = self.get_deprecated_msg(wrapped=wrapped, instance=None, kwargs=kwargs) for key in msg.keys(): message = msg[key] #create a warning for every deprecated argument if self.action: with warnings.catch_warnings(): warnings.simplefilter(self.action, self.category) warnings.warn(message, category=self.category, stacklevel=_class_stacklevel) else: warnings.warn(message, category=self.category, stacklevel=_class_stacklevel) if old_new1 is object.__new__: return old_new1(cls) # actually, we don't know the real signature of *old_new1* return old_new1(cls, *args, **kwargs) wrapped.__new__ = staticmethod(wrapped_cls) return wrapped
[docs] def deprecat(*args, **kwargs): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used. """ if args and isinstance(args[0], string_types): kwargs['reason'] = args[0] args = args[1:] if args and not callable(args[0]): raise TypeError(repr(type(args[0]))) if args: action = kwargs.get('action') category = kwargs.get('category', DeprecationWarning) adapter_cls = kwargs.pop('adapter_cls', ClassicAdapter) adapter = adapter_cls(**kwargs) wrapped = args[0] if inspect.isclass(wrapped): wrapped = adapter(wrapped) return wrapped elif inspect.isroutine(wrapped): @wrapt.decorator(adapter=adapter) def wrapper_function(wrapped_, instance_, args_, kwargs_): msg = adapter.get_deprecated_msg(wrapped_, instance_, kwargs_) if msg: for key in msg.keys(): message = msg[key] if action: with warnings.catch_warnings(): warnings.simplefilter(action, category) warnings.warn(message, category=category, stacklevel=_routine_stacklevel) else: warnings.warn(message, category=category, stacklevel=_routine_stacklevel) return wrapped_(*args_, **kwargs_) return wrapper_function(wrapped) else: raise TypeError(repr(type(wrapped))) return functools.partial(deprecat, **kwargs)