Source code for chempy.util.deprecation

# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)

import warnings


[docs]class Deprecation(object): """ Decorator factory for deprecating functions or classes. This class represent deprecations of functions or classes and is designed to be used with the ``warnings`` library. Parameters ---------- last_supported_version : str, optional Version string, e.g. ``'0.2.1'``. will_be_missing_in : str, optional Version string, e.g. ``'0.3.0'``. use_instead : object or str, optional Function or class to use instead or descriptive string. issue : str, optional issues_url : callback, optional Converts issue to url, e.g. ``lambda s: 'https://github.com/user/repo/\ issues/%s/' % s.lstrip('gh-')``. warning: DeprecationWarning, optional Any subclass of DeprecationWarning, tip: you may invoke: ``warnings.simplefilter('once', MyWarning)`` at module init. Examples -------- >>> import warnings >>> warnings.simplefilter("error", DeprecationWarning) >>> @Deprecation() ... def f(): ... return 1 ... >>> f() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DeprecationWarning: f is deprecated. >>> @Deprecation(last_supported_version='0.4.0') ... def some_old_function(x): ... return x*x - x ... >>> Deprecation.inspect(some_old_function).last_supported_version '0.4.0' >>> @Deprecation(will_be_missing_in='1.0') ... class ClumsyClass(object): ... pass ... >>> Deprecation.inspect(ClumsyClass).will_be_missing_in '1.0' >>> warnings.resetwarnings() Notes ----- :class:`DeprecationWarning` is ignored by default. Use custom warning and filter appropriately. Alternatively, run python with ``-W`` flag or set the appropriate environment variable: :: $ python -c 'import warnings as w; w.warn("X", DeprecationWarning)' $ python -Wd -c 'import warnings as w; w.warn("X", DeprecationWarning)' -c:1: DeprecationWarning: X $ export PYTHONWARNINGS=d $ python -c 'import warnings as w; w.warn("X", DeprecationWarning)' -c:1: DeprecationWarning: X """ _deprecations = {} def __init__(self, last_supported_version=None, will_be_missing_in=None, use_instead=None, issue=None, issues_url=None, warning=DeprecationWarning): if last_supported_version is not None and\ not isinstance(last_supported_version, (str, tuple, list)) and\ callable(last_supported_version): raise ValueError("last_supported_version not str, tuple or list") self.last_supported_version = last_supported_version self.will_be_missing_in = will_be_missing_in self.use_instead = use_instead self.issue = issue self.issues_url = issues_url self.warning = warning self.warning_message = self._warning_message_template() @classmethod
[docs] def inspect(cls, obj): """ Get the :class:`Deprecation` instance of a deprecated function. """ return cls._deprecations[obj]
def _warning_message_template(self): msg = '%(func_name)s is deprecated' if self.last_supported_version is not None: msg += ' since (not including) % s' % self.last_supported_version if self.will_be_missing_in is not None: msg += ', it will be missing in %s' % self.will_be_missing_in if self.issue is not None: if self.issues_url is not None: msg += self.issues_url(self.issue) else: msg += ' (see issue %s)' % self.issue if self.use_instead is not None: try: msg += '. Use %s instead' % self.use_instead.__name__ except AttributeError: msg += '. Use %s instead' % self.use_instead return msg + '.' def __call__(self, wrapped): """ Decorates function to be deprecated """ msg = self.warning_message % {'func_name': wrapped.__name__} wrapped_doc = wrapped.__doc__ or '' if hasattr(wrapped, '__mro__'): # wrapped is a class class _Wrapper(wrapped): __doc__ = msg + '\n\n' + wrapped_doc def __init__(_self, *args, **kwargs): warnings.warn(msg, self.warning, stacklevel=2) wrapped.__init__(_self, *args, **kwargs) else: # wrapped is a function def _Wrapper(*args, **kwargs): warnings.warn(msg, self.warning, stacklevel=2) return wrapped(*args, **kwargs) _Wrapper.__doc__ = msg + '\n\n' + wrapped_doc self._deprecations[_Wrapper] = self _Wrapper.__name__ = wrapped.__name__ _Wrapper.__module__ = wrapped.__module__ return _Wrapper