Как распечатать функции, как они называются - PullRequest
57 голосов
/ 29 ноября 2011

При отладке скрипта Python мне бы очень хотелось знать весь стек вызовов для всей моей программы. Идеальная ситуация была бы, если бы существовал флаг командной строки для python, который заставлял бы Python печатать все имена функций по мере их вызова (я проверил man Python2.7, но не нашел ничего подобного).

Из-за количества функций в этом сценарии я бы предпочел не добавлять оператор печати в начало каждой функции и / или класса, если это возможно.

Промежуточным решением было бы использовать отладчик PyDev, установить пару точек останова и проверить стек вызовов на наличие заданных точек в моей программе, поэтому я пока воспользуюсь этим подходом.

Я бы все же предпочел видеть полный список всех функций, вызываемых на протяжении всей жизни программы, если такой метод существует.

Ответы [ 9 ]

89 голосов
/ 29 ноября 2011

Вы можете сделать это с помощью функции трассировки (обратитесь к Spacedman для улучшения исходной версии этого, чтобы отследить возвраты и использовать некоторые хорошие отступы):

def tracefunc(frame, event, arg, indent=[0]):
      if event == "call":
          indent[0] += 2
          print("-" * indent[0] + "> call function", frame.f_code.co_name)
      elif event == "return":
          print("<" + "-" * indent[0], "exit function", frame.f_code.co_name)
          indent[0] -= 2
      return tracefunc

import sys
sys.settrace(tracefunc)

main()   # or whatever kicks off your script

Обратите внимание, что объект кода функции обычно имеет то же имя, что и связанная функция, но не всегда, поскольку функции могут создаваться динамически. К сожалению, Python не отслеживает объекты функций в стеке (я иногда мечтал представить патч для этого). Тем не менее, это, безусловно, "достаточно хорошо" в большинстве случаев.

Если это становится проблемой, вы можете извлечь «настоящее» имя функции из исходного кода - Python отслеживает имя файла и номер строки - или попросить сборщик мусора выяснить, какой объект функции ссылается на объект кода. Может быть несколько функций, совместно использующих объект кода, но любое из их имен может быть достаточно хорошим.

Возвращаясь к этому вопросу четыре года спустя, я должен упомянуть, что в Python 2.6 и более поздних версиях вы можете повысить производительность, используя sys.setprofile() вместо sys.settrace(). Та же функция трассировки может быть использована; просто функция профиля вызывается только при входе или выходе из функции, поэтому то, что находится внутри функции, выполняется на полной скорости.

11 голосов
/ 08 июля 2014

Другой хороший инструмент, о котором следует знать, - это модуль trace :

$ cat foo.py
def foo():
   bar()

def bar():
   print "in bar!"

foo()

$ python -m trace --listfuncs foo.py
in bar!

functions called:
filename: /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/trace.py, modulename: trace, funcname: _unsettrace
filename: foo.py, modulename: foo, funcname: 
filename: foo.py, modulename: foo, funcname: bar
filename: foo.py, modulename: foo, funcname: foo

$python -m trace --trace foo.py
 --- modulename: foo, funcname: 
foo.py(1): def foo():
foo.py(4): def bar():
foo.py(7): foo()
 --- modulename: foo, funcname: foo
foo.py(2):    bar()
 --- modulename: foo, funcname: bar
foo.py(5):    print "in bar!"
in bar!
 --- modulename: trace, funcname: _unsettrace
trace.py(80):         sys.settrace(None)

7 голосов
/ 29 ноября 2011

Есть несколько вариантов.Если отладчика недостаточно, вы можете установить функцию трассировки , используя sys.settrace().Эта функция будет по существу вызываться в каждой строке выполняемого кода Python, но ее легко идентифицировать вызовы функций - см. Связанную документацию.* модуль, хотя он не делает именно то, что вы просили.Обязательно изучите опцию --trackcalls.

5 голосов
/ 29 ноября 2011
import traceback
def foo():
    traceback.print_stack()
def bar():
    foo()
def car():
    bar():

car()
File "<string>", line 1, in <module>
File "C:\Python27\lib\idlelib\run.py", line 97, in main
  ret = method(*args, **kwargs)
File "C:\Python27\lib\idlelib\run.py", line 298, in runcode
    exec code in self.locals
File "<pyshell#494>", line 1, in <module>
File "<pyshell#493>", line 2, in car
File "<pyshell#490>", line 2, in bar
File "<pyshell#486>", line 2, in foo

traceback

2 голосов
/ 02 марта 2019

Инструмент hunter делает именно это и многое другое. Например, дано:

test.py

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

def bar(x):
    foo(x)

bar()

Вывод выглядит так:

$ PYTHONHUNTER='module="__main__"' python test.py
                                 test.py:1     call      => <module>()
                                 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    <= <module>: None

Он также предоставляет довольно гибкий синтаксис запроса, который позволяет указывать модуль, файл / lino, функцию и т. Д., Что помогает, поскольку вывод по умолчанию (который включает вызовы стандартных библиотечных функций) может быть довольно большим.

2 голосов
/ 21 ноября 2018

Я взял ответ kindall и построил его.

import sys


WHITE_LIST = ['trade']      # Look for these words in the file path.
EXCLUSIONS = ['<']          # Ignore <listcomp>, etc. in the function name.


def tracefunc(frame, event, arg):

    if event == "call":
        tracefunc.stack_level += 1

        unique_id = frame.f_code.co_filename+str(frame.f_lineno)
        if unique_id in tracefunc.memorized:
            return

        # Part of filename MUST be in white list.
        if any(x in frame.f_code.co_filename for x in WHITE_LIST) \
            and \
          not any(x in frame.f_code.co_name for x in EXCLUSIONS):

            if 'self' in frame.f_locals:
                class_name = frame.f_locals['self'].__class__.__name__
                func_name = class_name + '.' + frame.f_code.co_name
            else:
                func_name = frame.f_code.co_name

            func_name = '{name:->{indent}s}()'.format(
                    indent=tracefunc.stack_level*2, name=func_name)
            txt = '{: <40} # {}, {}'.format(
                    func_name, frame.f_code.co_filename, frame.f_lineno)
            print(txt)

            tracefunc.memorized.add(unique_id)

    elif event == "return":
        tracefunc.stack_level -= 1


tracefunc.memorized = set()
tracefunc.stack_level = 0


sys.setprofile(traceit.tracefunc)

Пример вывода:

API.getFills()                           # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 331
API._get_req_id()                        # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1053
API._wait_till_done()                    # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1026
---API.execDetails()                     # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1187
-------Fill.__init__()                   # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 256
--------Price.__init__()                 # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 237
-deserialize_order_ref()                 # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 644
--------------------Port()               # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 647
API.commissionReport()                   # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1118

Особенности:

  • Игнорирует внутренние функции языка Python.
  • Игнорирует повторные вызовы функций (необязательно).
  • Использует sys.setprofile () вместо sys.settrace () для скорости.
2 голосов
/ 29 ноября 2011

Вы можете использовать settrace, как описано здесь: Отслеживание кода Python . Используйте версию в конце страницы. Я вставляю код этой страницы в свой код, чтобы точно увидеть, какие строки выполняются, когда мой код выполняется. Вы также можете фильтровать так, чтобы вы видели только имена вызванных функций.

1 голос
/ 27 ноября 2018

Вы также можете использовать декоратор для определенных функций, которые вы хотите отследить (с их аргументами):

import sys
from functools import wraps

class TraceCalls(object):
    """ Use as a decorator on functions that should be traced. Several
        functions can be decorated - they will all be indented according
        to their call depth.
    """
    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

        # This is a class attribute since we want to share the indentation
        # level between different traced functions, in case they call
        # each other.
        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

Просто импортируйте этот файл и добавьте @TraceCalls () перед функцией / методом, который вы хотитеслед.

0 голосов
/ 04 июля 2019

здесь ответили https://stackoverflow.com/a/56897183/4039061

import inspect;print(*['\n\x1b[0;36;1m| \x1b[0;32;1m{:25}\x1b[0;36;1m| \x1b[0;35;1m{}'.format(str(x.function), x.filename+'\x1b[0;31;1m:'+str(x.lineno)+'\x1b[0m') for x in inspect.stack()])
...