decorators in the python standard lib (@deprecated specifically)

I need to mark routines as deprecated, but apparently there's no standard library decorator for deprecation. I am aware of recipes for it and the warnings module, but my question is: why is there no standard library decorator for this (common) task ?

Additional question: are there standard decorators in the standard library at all ?

8

7 Answers

Here's some snippet, modified from those cited by Leandro:

import warnings
import functools
def deprecated(func): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" @functools.wraps(func) def new_func(*args, **kwargs): warnings.simplefilter('always', DeprecationWarning) # turn off filter warnings.warn("Call to deprecated function {}.".format(func.__name__), category=DeprecationWarning, stacklevel=2) warnings.simplefilter('default', DeprecationWarning) # reset filter return func(*args, **kwargs) return new_func
# Examples
@deprecated
def some_old_function(x, y): return x + y
class SomeClass: @deprecated def some_old_method(self, x, y): return x + y

Because in some interpreters the first solution exposed (without filter handling) may result in a warning suppression.

8

Here is another solution:

This decorator (a decorator factory in fact) allow you to give a reason message. It is also more useful to help the developer to diagnose the problem by giving the source filename and line number.

EDIT: This code use Zero's recommendation: it replace warnings.warn_explicit line by warnings.warn(msg, category=DeprecationWarning, stacklevel=2), which prints the function call site rather than the function definition site. It makes debugging easier.

EDIT2: This version allow the developper to specify an optional "reason" message.

import functools
import inspect
import warnings
string_types = (type(b''), type(u''))
def deprecated(reason): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used. """ if isinstance(reason, string_types): # The @deprecated is used with a 'reason'. # # .. code-block:: python # # @deprecated("please, use another function") # def old_function(x, y): # pass def decorator(func1): if inspect.isclass(func1): fmt1 = "Call to deprecated class {name} ({reason})." else: fmt1 = "Call to deprecated function {name} ({reason})." @functools.wraps(func1) def new_func1(*args, **kwargs): warnings.simplefilter('always', DeprecationWarning) warnings.warn( fmt1.format(name=func1.__name__, reason=reason), category=DeprecationWarning, stacklevel=2 ) warnings.simplefilter('default', DeprecationWarning) return func1(*args, **kwargs) return new_func1 return decorator elif inspect.isclass(reason) or inspect.isfunction(reason): # The @deprecated is used without any 'reason'. # # .. code-block:: python # # @deprecated # def old_function(x, y): # pass func2 = reason if inspect.isclass(func2): fmt2 = "Call to deprecated class {name}." else: fmt2 = "Call to deprecated function {name}." @functools.wraps(func2) def new_func2(*args, **kwargs): warnings.simplefilter('always', DeprecationWarning) warnings.warn( fmt2.format(name=func2.__name__), category=DeprecationWarning, stacklevel=2 ) warnings.simplefilter('default', DeprecationWarning) return func2(*args, **kwargs) return new_func2 else: raise TypeError(repr(type(reason)))

You can use this decorator for functions, methods and classes.

Here is a simple example:

@deprecated("use another function")
def some_old_function(x, y): return x + y
class SomeClass(object): @deprecated("use another method") def some_old_method(self, x, y): return x + y
@deprecated("use another class")
class SomeOldClass(object): pass
some_old_function(5, 3)
SomeClass().some_old_method(8, 9)
SomeOldClass()

You'll get:

deprecated_example.py:59: DeprecationWarning: Call to deprecated function or method some_old_function (use another function). some_old_function(5, 3)
deprecated_example.py:60: DeprecationWarning: Call to deprecated function or method some_old_method (use another method). SomeClass().some_old_method(8, 9)
deprecated_example.py:61: DeprecationWarning: Call to deprecated class SomeOldClass (use another class). SomeOldClass()

EDIT3: This decorator is now part of the Deprecated library:

New stable release v1.2.13 🎉

7

As muon suggested, you can install the deprecation package for this.

The deprecation library provides a deprecated decorator and a fail_if_not_removed decorator for your tests.

Installation

pip install deprecation

Example Usage

import deprecation
@deprecation.deprecated(deprecated_in="1.0", removed_in="2.0", current_version=__version__, details="Use the bar function instead")
def foo(): """Do some stuff""" return 1

See for the full documentation.

2

I guess the reason is that Python code can't be processed statically (as it done for C++ compilers), you can't get warning about using some things before actually using it. I don't think that it's a good idea to spam user of your script with a bunch of messages "Warning: this developer of this script is using deprecated API".

Update: but you can create decorator which will transform original function into another. New function will mark/check switch telling that this function was called already and will show message only on turning switch into on state. And/or at exit it may print list of all deprecated functions used in program.

2

You can create a utils file

import warnings
def deprecated(message): def deprecated_decorator(func): def deprecated_func(*args, **kwargs): warnings.warn("{} is a deprecated function. {}".format(func.__name__, message), category=DeprecationWarning, stacklevel=2) warnings.simplefilter('default', DeprecationWarning) return func(*args, **kwargs) return deprecated_func return deprecated_decorator

And then import the deprecation decorator as follows:

from .utils import deprecated
@deprecated("Use method yyy instead")
def some_method()" pass
2

UPDATE: I think is better, when we show DeprecationWarning only first time for each code line and when we can send some message:

import inspect
import traceback
import warnings
import functools
import time
def deprecated(message: str = ''): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used first time and filter is set for show DeprecationWarning. """ def decorator_wrapper(func): @functools.wraps(func) def function_wrapper(*args, **kwargs): current_call_source = '|'.join(traceback.format_stack(inspect.currentframe())) if current_call_source not in function_wrapper.last_call_source: warnings.warn("Function {} is now deprecated! {}".format(func.__name__, message), category=DeprecationWarning, stacklevel=2) function_wrapper.last_call_source.add(current_call_source) return func(*args, **kwargs) function_wrapper.last_call_source = set() return function_wrapper return decorator_wrapper
@deprecated('You must use my_func2!')
def my_func(): time.sleep(.1) print('aaa') time.sleep(.1)
def my_func2(): print('bbb')
warnings.simplefilter('always', DeprecationWarning) # turn off filter
print('before cycle')
for i in range(5): my_func()
print('after cycle')
my_func()
my_func()
my_func()

Result:

before cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:45: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
aaa
aaa
aaa
aaa
after cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:47: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:48: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:49: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
Process finished with exit code 0

We can just click on the warning path and go to the line in PyCharm.

1

Python is a dynamically typed language. Not necessary declare the type to variable or argument type for function statically.

Since its dynamic every thing if processed at runtime. Even if a method is deprecated it will be known at runtime or during interpretation only.

use deprecation module to deprecate methods.

deprecation is a library that enables automated deprecations. It offers the deprecated() decorator to wrap functions, providing proper warnings both in documentation and via Python’s warnings system, as well as the deprecation.fail_if_not_removed() decorator for test methods to ensure that deprecated code is eventually removed.

Installing :

python3.10 -m pip install deprecation

Small demonstration:

import deprecation
@deprecation.deprecated(details="Use bar instead")
def foo(): print("Foo")
def bar(): print("Bar")
foo()
bar()

Output:

test.py: DeprecatedWarning: foo is deprecated. Use bar instead foo()
Foo
Bar

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like