Je dois marquer les routines comme obsolètes, mais apparemment, il n'y a pas de décorateur de bibliothèque standard pour la désapprobation. Je connais certaines recettes et le module des avertissements, mais ma question est la suivante: pourquoi n’existe-t-il aucun décorateur de bibliothèque standard pour cette tâche (commune)?
Question supplémentaire: existe-t-il des décorateurs standard dans la bibliothèque standard?
Voici un extrait, modifié de ceux cités par 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
Parce que, dans certains interprètes, la première solution exposée (sans traitement du filtre) peut entraîner la suppression des avertissements.
Voici une autre solution:
Ce décorateur (une usine de décorateurs en fait) vous permet de donner un message raison. Il est également plus utile d'aider le développeur à diagnostiquer le problème en donnant à la source filename et au numéro de la ligne.
EDIT: Ce code utilise la recommandation de Zero: il remplace warnings.warn_explicit
par warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
, qui affiche le site d'appel de fonction plutôt que le site de définition de fonction. Cela facilite le débogage.
EDIT2: cette version permet au développeur de spécifier un message optionnel "raison".
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)))
Vous pouvez utiliser ce décorateur pour functions, methods et classes.
Voici un exemple simple:
@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()
Tu auras:
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: Ce décorateur fait maintenant partie de la bibliothèque obsolète:
Nouvelle version stable v1.2.4 ????
Vous pouvez créer un fichier utils
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
Et puis importez le décorateur de dépréciation comme suit:
from .utils import deprecated
@deprecated("Use method yyy instead")
def some_method()"
pass
J'imagine que la raison en est que le code Python ne peut pas être traité de manière statique (comme c'est le cas pour les compilateurs C++), vous ne pouvez pas être averti d'utiliser certaines choses avant de les utiliser. Je ne pense pas que ce soit une bonne idée de spammer l'utilisateur de votre script avec un tas de messages "Avertissement: ce développeur de ce script utilise une API obsolète".
Mise à jour: mais vous pouvez créer un décorateur qui transformera la fonction originale en une autre. La nouvelle fonction marquera/vérifiera le commutateur indiquant que cette fonction a déjà été appelée et ne montrera le message que lorsque le commutateur sera activé. Et/ou à la sortie, il peut imprimer la liste de toutes les fonctions obsolètes utilisées dans le programme.
Comme suggéré par muon , vous pouvez installer le paquet deprecation
pour cela.
La bibliothèque
deprecation
fournit un décorateurdeprecated
et un décorateurfail_if_not_removed
pour vos tests.
pip install deprecation
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
Voir http://deprecation.readthedocs.io/ pour la documentation complète.
UPDATE: Je pense que c'est mieux, lorsque nous affichons DeprecationWarning uniquement pour la première fois pour chaque ligne de code et que nous pouvons envoyer un 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()
Résultat:
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
Nous pouvons simplement cliquer sur le chemin d'avertissement et accéder à la ligne dans PyCharm.
Augmenter cette réponse de Steven Vascellaro :
Si vous utilisez Anaconda, installez d’abord le paquet deprecation
:
conda install -c conda-forge deprecation
Puis collez le texte suivant en haut du fichier
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
Voir http://deprecation.readthedocs.io/ pour la documentation complète.