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'], {})
Related issues
Associated revisions
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
History
#1 Updated by Daniel Dehennin over 10 years ago
- Description updated (diff)
#2 Updated by Daniel Dehennin over 10 years ago
- Description updated (diff)
#3 Updated by Daniel Dehennin over 10 years ago
- Description updated (diff)
Il ne faut pas supprimer les arguments sinon on modifie l’appel de fonction -> copy
#4 Updated by Daniel Dehennin over 10 years ago
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 Updated by Daniel Dehennin over 10 years ago
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 Updated by Emmanuel GARETTE about 10 years ago
Il serait pratique d'avoir également le "return" dans les logs.
#7 Updated by Daniel Dehennin about 10 years ago
- Status changed from Nouveau to Résolu
- % Done changed from 0 to 100
Appliqué par commit 8659782892ebcde565fe604536a5bc8d0b0136a7.
#8 Updated by Joël Cuissinat about 10 years ago
- Status changed from Résolu to Fermé
à ressortir quand on en aura besoin :)