3

How could I deprecate a class and his subclasses in Python?

Currently I thought that __init__() would work, but it doesn't since it won't be called if I won't call super() on subclasses.

Edit: Okai some infos are missing on my question.

I know how to use warn.warning()

Also I don't wanted to use a Decorater. I wanted to just use it on one class and if the class is invoked it should warn the user.

Christian Schmitt
  • 837
  • 16
  • 47

1 Answers1

4

You are looking for warnings.warn(message[, category[, stacklevel]])

Issue a warning, or maybe ignore it or raise an exception. The category argument, if given, must be a warning category class (see above); it defaults to UserWarning. Alternatively message can be a Warning instance, in which case category will be ignored and message.class will be used. In this case the message text will be str(message). This function raises an exception if the particular warning issued is changed into an error by the warnings filter see above.

From here:

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
Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331