Projet

Général

Profil

Evolution #4324

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

Ajouté par Daniel Dehennin il y a plus de 11 ans. Mis à jour il y a plus de 11 ans.

Statut:
Fermé
Priorité:
Normal
Assigné à:
developpeurs_eole
Catégorie:
-
Début:
23/10/2012
Echéance:
% réalisé:

100%

Temps passé:
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'], {})

Demandes liées

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

Révisions associées

Révision a3dc6fde (diff)
Ajouté par Daniel Dehennin il y a plus de 11 ans

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

Révision 1d79be1b (diff)
Ajouté par Daniel Dehennin il y a plus de 11 ans

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

Révision 86597828 (diff)
Ajouté par Daniel Dehennin il y a plus de 11 ans

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

Révision b08f0be8 (diff)
Ajouté par yllen il y a presque 6 ans

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

#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 :)

Formats disponibles : Atom PDF