Skip to content

Instantly share code, notes, and snippets.

@mosdevly
Forked from Xion/deprecated.py
Created March 25, 2017 19:45
Show Gist options
  • Save mosdevly/011e76fb0bdd4dc3030dd304af324619 to your computer and use it in GitHub Desktop.
Save mosdevly/011e76fb0bdd4dc3030dd304af324619 to your computer and use it in GitHub Desktop.
@deprecated decorator for Python classes
import functools
import inspect
import os
import warnings
class _DeprecatedDecorator(object):
MESSAGE = "%s is @deprecated"
def __call__(self, symbol):
if not inspect.isclass(symbol):
raise TypeError("only classes can be @deprecated")
warnings.filterwarnings('default',
message=self.MESSAGE % r'\w+',
category=DeprecationWarning)
return self._wrap_class(symbol)
def _wrap_class(self, cls):
previous_ctor = cls.__init__
@functools.wraps(previous_ctor)
def new_ctor(*args, **kwargs):
self._warn(cls.__name__)
return previous_ctor(*args, **kwargs)
cls.__init__ = new_ctor
return cls
def _warn(self, name):
warnings.warn(self.MESSAGE % name, DeprecationWarning,
stacklevel=self._compute_stacklevel())
def _compute_stacklevel(self):
this_file, _ = os.path.splitext(__file__)
app_code_dir = self._get_app_code_dir()
def is_relevant(filename):
return filename.startswith(app_code_dir) and not \
filename.startswith(this_file)
stack = self._get_callstack()
stack.pop(0) # omit this function's frame
frame = None
try:
for i, frame in enumerate(stack, 1):
filename = frame.f_code.co_filename
if is_relevant(filename):
return i
finally:
del frame
del stack
return 0
def _get_app_code_dir(self):
import myapplication # root package for the app
app_dir = os.path.dirname(myapplication.__file__)
return os.path.join(app_dir, '') # ensure trailing slash
def _get_callstack(self):
frame = inspect.currentframe()
frame = frame.f_back # omit this function's frame
stack = []
try:
while frame:
stack.append(frame)
frame = frame.f_back
finally:
del frame
return stack
deprecated = _DeprecatedDecorator()
del _DeprecatedDecorator
@mosdevly
Copy link
Author

class PendingDeprecationDecorator(object):
    MESSAGE = "%s is @deprecated. %s"

    def __call__(self, cls):
        if not inspect.isclass(cls):
            raise TypeError("For class deprecation only.")

        warnings.filterwarnings('always',
                                message=self.MESSAGE % cls.__name__,
                                category=PendingDeprecationWarning)
        return self._wrap_class(cls)

    def _wrap_class(self, cls):
        previous_ctor = cls.__init__

        @wraps(previous_ctor, msg)
        def new_ctor(*args, **kwargs):
            self._warn(cls.__name__, msg)
            return previous_ctor(*args, **kwargs)

        cls.__init__ = new_ctor
        return cls

    def _warn(self, name, msg):
        frame = inspect.currentframe().f_back

        warnings.warn(self.MESSAGE % (name, msg),
                      category=PendingDeprecationWarning,
                      lineno=frame.f_lineno)


pending_deprecation = PendingDeprecationDecorator()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment