Project

General

Profile

Evolution #4324

Fournir un décorateur de traçage d’appel de fonction

Added by Daniel Dehennin over 10 years ago. Updated about 10 years ago.

Status:
Fermé
Priority:
Normal
Assigned To:
developpeurs_eole
Category:
-
Start date:
10/23/2012
Due date:
% Done:

100%

Spent time:
Distribution:
EOLE 2.4

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

Related to arv - Evolution #3655: En mode "debug", les mots de passe sont en clair dans les logs ARV Fermé 06/18/2012

Associated revisions

Revision a3dc6fde (diff)
Added by Daniel Dehennin about 10 years ago

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

Revision 1d79be1b (diff)
Added by Daniel Dehennin about 10 years ago

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

Revision 86597828 (diff)
Added by Daniel Dehennin about 10 years ago

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

Revision b08f0be8 (diff)
Added by yllen over 4 years ago

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

#8 Updated by Joël Cuissinat about 10 years ago

  • Status changed from Résolu to Fermé

à ressortir quand on en aura besoin :)

Also available in: Atom PDF