Project

General

Profile

Evolution #4324

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

Added by Daniel Dehennin over 7 years ago. Updated about 7 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 7 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 7 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 7 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 1 year ago

MA cartridge - see #4324

History

#1 Updated by Daniel Dehennin over 7 years ago

  • Description updated (diff)

#2 Updated by Daniel Dehennin over 7 years ago

  • Description updated (diff)

#3 Updated by Daniel Dehennin over 7 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 7 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 7 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 7 years ago

Il serait pratique d'avoir également le "return" dans les logs.

#7 Updated by Daniel Dehennin about 7 years ago

  • Status changed from Nouveau to Résolu
  • % Done changed from 0 to 100

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

  • Status changed from Résolu to Fermé

à ressortir quand on en aura besoin :)

Also available in: Atom PDF