tantale / deprecated

Python @deprecated decorator to deprecate old python classes, functions or methods.
MIT License
298 stars 32 forks source link

Deprecated names? #44

Open AstraLuma opened 3 years ago

AstraLuma commented 3 years ago

(Feature request)

I would like to see a way to move a class, function, or method from one name to another, deprecating the old name.

So you could say:

class NewName:
    ...

OldName = deprecated_name(NewName)

I would say that it should raise a warning on direct use (ideally when the code referring to it is compiled, but implementing that would be unacceptably magical).

tantale commented 3 years ago

Thank you for your question.

Actually, you can already have a old function which is a deprecated version of a new function name, here is how you can do that:

from deprecated import deprecated

def new_function():
    pass

old_function = deprecated(reason=u"Use new_function() instead", version="2.3.0")(new_function)

if __name__ == '__main__':
    old_function()
    new_function()

You'll have a warning message only if you use the old function:

demo.py:12: DeprecationWarning: Call to deprecated function (or staticmethod) new_function. (Use new_function() instead) -- Deprecated since version 2.3.0.
  old_function()

For classes, it's another story because this is the __new__ method which is actually decorated. So, this is not the right way to do that. You may need module-level deprecation, which is a modern feature only available for Python 3.7+ (see: PEP 562 -- Module __getattr__ and __dir__.

I'll try to given you a workaround later.

tantale commented 3 years ago

A workaround for classes could be designed as follow: the "old" class can be redefined and inherit the "new" class. Then you need to implement a __new__ method which will receive the deprecation warning:

from deprecated import deprecated

class NewClass(object):
    pass

@deprecated(reason=u"Use NewClass instead", version="2.3.0")
class OldClass(NewClass):
    @classmethod
    def __new__(cls, *args, **kwargs):
        return super(OldClass, cls).__new__(cls)
        # or, if ``NewClass.__new__`` is defined:
        # return super(OldClass, cls).__new__(cls, *args, **kwargs)

if __name__ == '__main__':
    old_obj = OldClass()
    new_obj = NewClass()

You get:

demo.py:19: DeprecationWarning: Call to deprecated class OldClass. (Use NewClass instead) -- Deprecated since version 2.3.0.
  old_obj = OldClass()
tantale commented 3 years ago

I will leave this issue opened until I implement module-level deprecation.

tantale commented 1 year ago

Module-level depreciation is currently being developed. Stay tune.

benjamin-kirkbride commented 11 months ago

Adding these examples to the docs would be good too