Evolution #4324
Fournir un décorateur de traçage d’appel de fonction
Description
Voici une version modifié du décorateur trace
d’arv (arv:source:arv/lib/util.py?rev=e5bad5b#L16)
#!/usr/bin/python
import logging
def trace(hide_args=[], hide_kwargs=[]):
"""This is a decorator which can be used to trace functions calls
It can replace some positional and/or keyword arguments with some
'X' if they are present.
@param hide_args: List of positional argument indexes to replace if present
@type hide_args: C{list}
@param hide_kwargs: List of keyword argument names to replace if present
@type hide_kwargs: C{list}
"""
def tracedec(func):
def newFunc(*args, **kwargs):
# Do nothing if debug is not enabled
if logger.isEnabledFor(logging.DEBUG):
# Copy arguments
args_list = list(args)
args_dict = kwargs.copy()
for index in hide_args:
if index < len(args_list):
args_list[index] = 'X' * len( args_list[index] )
for keyname in hide_kwargs:
if keyname in args_dict:
args_dict[keyname] = 'X' * len( args_dict[keyname] )
logger.debug( "-> entering %s(%s, %s)" % (func.__name__, str(args_list), str(args_dict)) )
return func(*args, **kwargs)
newFunc.__name__ = func.__name__
newFunc.__doc__ = func.__doc__
newFunc.__dict__.update(func.__dict__)
return newFunc
return tracedec
@trace(hide_args=[2], hide_kwargs=['passwd'])
def machin(msg, name=None, passwd=None):
print "Bidule: %s" % msg
machin("truc", "user", passwd="secretpassword")
# -> entering machin(['truc', 'dad'], {'passwd': 'XXXXXX'})
machin("truc", "user", "secretpassword")
# -> entering machin(['truc', 'dad', 'XXXXXX'], {})
Demandes liées
Révisions associées
Ajout d’utilitaire d’inpection
Ces utilitaires seront utilisés principalement pour nos décorateurs
« deprecated » et « trace ».
- tests/test_inspect_utils.py: Tests unitaires pour « get_caller_infos »
et « format_caller ».
- pyeole/inspect_utils.py (get_caller_infos): Retourne un dictionnaire
contenant les informations de l’appelant.
(format_caller): Retourne les informations de l’appelant sous forme de
chaîne de caractères.
Ref: #4324 @1h
Ajout d’une bibliothèque de décorateurs
Cette bibliothèque permet de définir simplement de nouveaux décorateurs.
Ces décorateurs peuvent accepter des arguments nommés et être appelés avec des
parenthèses :
@decorateur(arg1=val1, arg2=val2)
def decorated():
pass
Si les arguments sont optionels, les parenthèses deviennent optionnelles
aussi :
@decorateur()
def decorated():
pass
ou
@decorateur
def decorated():
pass
- tests/test_decorators.py: Tests des décorateurs et des différents
méthodes d’appels.
- pyeole/decorator.py (EoleDecorator): Classe abstraite de décorateur.
(advice): Décorateur pour éxécuter du code avant, autour ou après une
fonction/méthode.
(deprecated): Implémentation du décorateur « deprecated » en utilisant
« advice » et en affichant des informations sur l’appelant de la
méthode dépréciée.
- pyeole/deprecation.py: Dépréciation de l’ancienne implémentation
de « deprecated » en faveur de pyeole.decorator.deprecated.
Ref: #4324 @13h
Ajout d’un décorateur traceur de fonction
Le traceur prend des arguments optionels afin de cacher certains
paramètres.
Il utilise un logger nommé d’après l’appelant de la fonction tracée.
- pyeole/log.py: Remise au propre de la documentation de la partie non
dépréciée.
(trace): Nouveau décorateur « advice » affichant le nom de la fonction
appellé et ses arguments en « before » et la valeur de retour
en « after ».
Fixes: #4324 @2h
MA cartridge - see #4324
Historique
#1 Mis à jour par Daniel Dehennin il y a plus de 11 ans
- Description mis à jour (diff)
#2 Mis à jour par Daniel Dehennin il y a plus de 11 ans
- Description mis à jour (diff)
#3 Mis à jour par Daniel Dehennin il y a plus de 11 ans
- Description mis à jour (diff)
Il ne faut pas supprimer les arguments sinon on modifie l’appel de fonction -> copy
#4 Mis à jour par Daniel Dehennin il y a plus de 11 ans
Voici une classe trace
permettant un appel sans les parenthèses si on ne passe aucun argument au décorateur :
#!/usr/bin/python
import logging
logger = logging.getLogger()
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
class trace(object):
"""This is a decorator which can be used to trace functions calls
It can replace some positional and/or keyword arguments with some
'X' if they are present.
"""
def __init__(self, f=None, hide_args=[], hide_kwargs=[]):
"""Initialize the trace decorator
When no argument are passed to the decorator, it can be called without the braces.
@param f: Function to decorate when there are no decorator arguments
@type f: C{function}
@param hide_args: List of positional argument indexes to replace if present
@type hide_args: C{list}
@param hide_kwargs: List of keyword argument names to replace if present
@type hide_kwargs: C{list}
"""
# func is used when using @trace decorator without braces
if f is not None and not callable(f):
raise TypeError('You must call trace decorator with keyword arguments')
self.function = f
self.hide_args = hide_args
self.hide_kwargs = hide_kwargs
def __log_call(self, f, *args, **kwargs):
"""Log function calls
"""
# Do nothing if debug is not enabled
if logger.isEnabledFor(logging.DEBUG):
# Copy arguments
args_list = list(args)
args_dict = kwargs.copy()
for index in self.hide_args:
if index < len(args_list):
args_list[index] = 'X' * len( args_list[index] )
for keyname in self.hide_kwargs:
if keyname in args_dict:
args_dict[keyname] = 'X' * len( args_dict[keyname] )
logger.debug( "-> entering %s(%s, %s)" % (f.__name__, str(args_list), str(args_dict)) )
def __call_with_args(self, f):
"""Call decorator with parameters
Return a wrapper since the call is done
"""
def wrapped_f(*args, **kwargs):
self.__log_call(f, *args, **kwargs)
f(*args, **kwargs)
return wrapped_f
def __call_without_args(self, *args, **kwargs):
"""Call decorator without parameter
The __call__ method of this object is not called until the decorated function is called itself.
"""
self.__log_call(self.function, *args, **kwargs)
self.function(*args, **kwargs)
def __call__(self, f, *args, **kwargs):
"""Log function calls
"""
if callable(f):
return self.__call_with_args(f)
else:
argslist = [f]
argslist.extend(args)
return self.__call_without_args(*argslist, **kwargs)
@trace(hide_args=[2], hide_kwargs=['passwd'])
def machin(msg, name=None, passwd=None):
print "Machin: %s" % msg
# Now braces are useless when no argument are passed to the decorator
@trace
def bidule(msg, name=None, passwd=None):
print "Bidule: %s" % msg
machin("MSG: args", "user", "secret")
# -> entering machin(['MSG: args', 'user', 'XXXXXX'], {})
bidule("MSG: args", "user", "secret")
# -> entering bidule(['MSG: args', 'user', 'secret'], {})
machin("MSG: kwargs", "user", passwd="secret")
# -> entering machin(['MSG: kwargs', 'user'], {'passwd': 'XXXXXX'})
bidule("MSG: kwargs", "user", passwd="secret")
# -> entering bidule(['MSG: kwargs', 'user'], {'passwd': 'secret'})
#5 Mis à jour par Daniel Dehennin il y a plus de 11 ans
Une petite documentation en 12 points pour comprender les décorateurs.
Un peu d’explication sur la différence entre les décorateurs avec et sans arguments.
#6 Mis à jour par Emmanuel GARETTE il y a plus de 11 ans
Il serait pratique d'avoir également le "return" dans les logs.
#7 Mis à jour par Daniel Dehennin il y a plus de 11 ans
- Statut changé de Nouveau à Résolu
- % réalisé changé de 0 à 100
Appliqué par commit 8659782892ebcde565fe604536a5bc8d0b0136a7.
#8 Mis à jour par Joël Cuissinat il y a plus de 11 ans
- Statut changé de Résolu à Fermé
à ressortir quand on en aura besoin :)