# -*- 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