105 votes

Comment puis-je imprimer les fonctions telles qu'elles sont appelées?

Lors du débogage d'un script Python, j'aimerais vraiment connaître la pile d'appels pour l'ensemble de mon programme. Une situation idéale serait s'il existait un drapeau en ligne de commande pour Python qui amènerait Python à imprimer tous les noms de fonction au fur et à mesure de leur appel (j'ai consulté man Python2.7, mais je n'ai rien trouvé de ce genre).

En raison du nombre de fonctions dans ce script, je préférerais ne pas ajouter une instruction d'impression au début de chaque fonction et/ou classe, si possible.

Une solution intermédiaire consisterait à utiliser le débogueur de PyDev, placer quelques points d'arrêt et vérifier la pile d'appels à des points donnés dans mon programme, donc j'utiliserai cette approche pour le moment.

J'aimerais toujours voir une liste complète de toutes les fonctions appelées tout au long de la vie du programme, si une méthode de ce genre existe.

7voto

Chris Hunt Points 2630

L'outil hunter fait exactement cela, et plus encore. Par exemple, en donné:

test.py:

def foo(x):
    print(f'foo({x})')

def bar(x):
    foo(x)

bar()

La sortie ressemble à:

$ PYTHONHUNTER='module="__main__"' python test.py
                                 test.py:1     call      => ()
                                 test.py:1     line         def foo(x):
                                 test.py:4     line         def bar(x):
                                 test.py:7     line         bar('abc')
                                 test.py:4     call         => bar(x='abc')
                                 test.py:5     line            foo(x)
                                 test.py:1     call            => foo(x='abc')
                                 test.py:2     line               print(f'foo({x})')
foo(abc)
                                 test.py:2     return          <= foo: None
                                 test.py:5     return       <= bar: None
                                 test.py:7     return    <= : None

Il offre également une syntaxe de requête assez flexible qui permet de spécifier le module, le fichier/numéro de ligne, la fonction, etc., ce qui est utile car la sortie par défaut (qui inclut les appels de fonctions de la bibliothèque standard) peut être assez volumineuse.

2voto

jeorgen Points 382

Vous pourriez utiliser settrace, tel qu'indiqué ici : Traçage de code python. Utilisez la version près de la fin de la page. J'insère le code de cette page dans mon code pour voir exactement quelles lignes sont exécutées lorsque mon code est en cours d'exécution. Vous pouvez également filtrer pour ne voir que les noms des fonctions appelées.

1voto

Vincent Fenet Points 366

Vous pouvez également utiliser un décorateur pour des fonctions spécifiques que vous souhaitez tracer (avec leurs arguments) :

import sys
from functools import wraps

class TraceCalls(object):
    """ Utilisez comme décorateur sur les fonctions à tracer. Plusieurs
        fonctions peuvent être décorées - elles seront toutes indentées en fonction
        de leur profondeur d'appel.
    """
    def __init__(self, stream=sys.stdout, indent_step=2, show_ret=False):
        self.stream = stream
        self.indent_step = indent_step
        self.show_ret = show_ret

        # Il s'agit d'un attribut de classe car nous voulons partager l'indentation
        # niveau entre les différentes fonctions tracées, au cas où elles s'appellent
        # mutuellement.
        TraceCalls.cur_indent = 0

    def __call__(self, fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            indent = ' ' * TraceCalls.cur_indent
            argstr = ', '.join(
                [repr(a) for a in args] +
                ["%s=%s" % (a, repr(b)) for a, b in kwargs.items()])
            self.stream.write('%s%s(%s)\n' % (indent, fn.__name__, argstr))

            TraceCalls.cur_indent += self.indent_step
            ret = fn(*args, **kwargs)
            TraceCalls.cur_indent -= self.indent_step

            if self.show_ret:
                self.stream.write('%s--> %s\n' % (indent, ret))
            return ret
        return wrapper

Il suffit d'importer ce fichier et d'ajouter un @TraceCalls() avant la fonction/méthode que vous souhaitez tracer.

1voto

ObjectNameDisplay Points 368

Variant de la réponse de kindall, renvoyez uniquement les fonctions appelées dans un package.

def tracefunc(frame, event, arg, indent=[0]):
    package_name = __name__.split('.')[0]

    if event == "call" and (package_name in str(frame)):
        indent[0] += 2
        print("-" * indent[0] + "> appel de la fonction", frame.f_code.co_name)
    return tracefunc

import sys
sys.settrace(tracefunc)

par exemple. Dans un package appelé Dog, cela ne devrait vous montrer que les fonctions appelées qui ont été définies dans le package Dog.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X