Skip to content

Instantly share code, notes, and snippets.

@dolph
Created June 26, 2019 20:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dolph/b0bf1e6889643608eb634e00124ee6cd to your computer and use it in GitHub Desktop.
Save dolph/b0bf1e6889643608eb634e00124ee6cd to your computer and use it in GitHub Desktop.
class deprecated(object):
"""A decorator to mark callables as deprecated.
This decorator logs a deprecation message when the callable it decorates is
used and modify's the callable's docstring. The message will include the
release where the callable was deprecated.
Examples:
1. Specifying the required deprecated release
>>> @deprecated(as_of="June 22, 2018")
... def a(): pass
2. Specifying a replacement:
>>> @deprecated(as_of="v3.5.2", in_favor_of='f()')
... def b(): pass
"""
_deprecated_msg_with_alternative = _(
'%(what)s is deprecated as of %(as_of)s in favor of %(in_favor_of)s.')
_deprecated_msg_with_no_alternative = _(
'%(what)s is deprecated as of %(as_of)s. It will not be superseded.')
def __init__(self, as_of, in_favor_of=None, remove_in=2, what=None, kwarg=False):
"""Initialize decorator
:param as_of: the release deprecating the callable.
:param in_favor_of: the replacement for the callable (optional)
:param remove_in: an integer specifying how many releases to wait
before removing (default: 2)
:param what: name of the thing being deprecated (default: the
callable's name)
:param kwarg: true if the thing being deprecated is a kwarg (default: false)
"""
self.as_of = as_of
self.in_favor_of = in_favor_of
self.remove_in = remove_in
self.what = what
self.kwarg = kwarg
def __call__(self, func_or_cls):
if not self.what:
self.what = func_or_cls.__name__ + '()'
msg, details = self._build_message()
# Append to the callable's docstr.
func_or_cls.__doc__ += msg
if inspect.isfunction(func_or_cls):
@six.wraps(func_or_cls)
def wrapped(*args, **kwargs):
if self.kwarg and self.what in kwargs:
report_deprecated_feature(LOG, msg, details)
return func_or_cls(*args, **kwargs)
return wrapped
elif inspect.isclass(func_or_cls):
orig_init = func_or_cls.__init__
# TODO(tsufiev): change `functools` module to `six` as
# soon as six 1.7.4 (with fix for passing `assigned`
# argument to underlying `functools.wraps`) is released
# and added to the oslo-incubator requrements
@functools.wraps(orig_init, assigned=('__name__', '__doc__'))
def new_init(self, *args, **kwargs):
report_deprecated_feature(LOG, msg, details)
orig_init(self, *args, **kwargs)
func_or_cls.__init__ = new_init
return func_or_cls
else:
raise TypeError('deprecated can be used only with functions or '
'classes')
def _build_message(self):
details = dict(what=self.what,
as_of=self.as_of)
if self.in_favor_of:
details['in_favor_of'] = self.in_favor_of
msg = self._deprecated_msg_with_alternative
else:
msg = self._deprecated_msg_with_no_alternative
return msg, details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment