décorateurs dans la librairie standard python (@déconseillé spécifiquement)

Je dois marquer les routines comme étant obsolètes, mais apparemment il n’y a pas de décorateur de bibliothèque standard pour la dépréciation. Je connais des recettes pour cela et le module des avertissements, mais ma question est la suivante: pourquoi n’y a-t-il pas de 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 quelques extraits, modifiés 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 manipulation du filtre) peut entraîner une suppression des alertes.

Voici une autre solution:

Ce décorateur (une fabrique de décorateurs en fait) vous permet de donner un message de raison . Il est également plus utile d’aider le développeur à diagnostiquer le problème en indiquant le nom du fichier source et le numéro de ligne .

EDIT : Ce code utilise la recommandation de Zero: il remplace la ligne warnings.warn_explicit par warnings.warn(msg, category=DeprecationWarning, stacklevel=2) , qui imprime le site d’appel de la fonction plutôt que le site de définition de la 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 ssortingng_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, ssortingng_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 des fonctions , des méthodes et des 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:

  • Index du package Python (PyPi)
  • Site Web de GitHub
  • Lire les documents
  • EBook sur Lulu.com

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 

Je suppose que la raison en est que le code Python ne peut pas être traité statiquement (comme pour les compilateurs C ++), vous ne pouvez pas recevoir d’avertissement sur l’utilisation de certaines choses avant de l’utiliser. Je ne pense pas que ce soit une bonne idée de spammer l’utilisateur de votre script avec un tas de messages “Attention: 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 / cochera le commutateur pour indiquer que cette fonction a déjà été appelée et affichera le message uniquement lorsque le commutateur est 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 package de deprecation pour cela.

La bibliothèque de deprecation fournit un décorateur deprecated et un décorateur fail_if_not_removed pour vos tests.

Installation

 pip install deprecation 

Exemple d’utilisation

 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.

MISE À JOUR: Je pense que c’est mieux, quand on montre DeprecationWarning seulement la première fois pour chaque ligne de code et quand on peut 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 aller à la ligne dans PyCharm.

Augmenter cette réponse par Steven Vascellaro :

Si vous utilisez Anaconda, installez d’abord le package de 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.