gurney alex
gurney alex

Reputation: 13645

Deprecate usage of a class as a parent class in Python

I'm working with a Python 2.x framework, and a recent version of the framework has moved some widely used base classes from module A to module B (and the classes have been renamed to a clearer names in the process). Module A defines a backward compatible identifiers for the new class names.

B.py:

class BaseClass(object):
    __metaclass__ = framework_meta # handles registration etc.

A.py:

import B
oldbase = B.BaseClass

Now in order to help people migrate their code, I would like to be able to issue a DeprecationWarning (using warnings.warn) whenever code using the framework defines a class deriving from A.oldbase telling the programmer to directly inherit from B.BaseClass.

I expect this can be achieved with a metaclass. I tried to declare a new metaclass deriving from the framework metaclass

class deprecated_base_class(framework_meta):
    def __new__(meta, name, bases, attrs):
        warning = '%(class)s is deprecated'
        for b in bases:
            warning =  getattr(b, '__deprecation_warning__', None) or warning
        warn(warning % {'class': name}, DeprecationWarning, stacklevel=2)
        return super(deprecated_base_class, meta).__new__(meta, name, bases, attrs)

together with:

A.py:

class oldbase(B.BaseClass):
    __metaclass__ = deprecated_base_class
    __deprecation_warning__ = 'class oldbase is deprecated. Use B.BaseClass instead'

clientcode.py

class FooBar(oldbase):
    pass

The problem I have now, is that I get a DeprecationWarning for the definition of oldbase. How can I fix this?

Upvotes: 7

Views: 591

Answers (1)

ecatmur
ecatmur

Reputation: 157464

You want to display the warning if any of the bases are deprecated:

class deprecated_base_class(framework_meta):
    def __new__(meta, name, bases, attrs):
        for b in bases:
            if isinstance(b, deprecated_base_class):
                warning = getattr(b, '__deprecation_warning__', '%(class)s is deprecated')
                warn(warning % {'class': b.__name__}, DeprecationWarning, stacklevel=2)
        return super(deprecated_base_class, meta).__new__(meta, name, bases, attrs)

Upvotes: 2

Related Questions