Подтвердить что ты не робот

Как печатать функции по мере их вызова

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

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

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

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

4b9b3361

Ответ 1

Вы можете сделать это с помощью функции трассировки (обратитесь к 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(). Та же функция трассировки может быть использована; просто функция профиля вызывается только при входе или выходе из функции, так что внутри функции выполняется на полной скорости.

Ответ 2

Еще один хороший инструмент, о котором нужно знать, - это модуль 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)

Ответ 3

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

Вы также можете быть заинтересованы в trace, хотя он не делает именно то, что вы просили. Обязательно загляните в опцию --trackcalls.

Ответ 4

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

проследить

Ответ 5

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

Ответ 6

Я взял добрый ответ и построил его.

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() для скорости.

Ответ 7

Инструмент 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, функцию и т.д., Что помогает, поскольку вывод по умолчанию (который включает вызовы стандартных библиотечных функций) может быть довольно большим.

Ответ 8

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

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() перед функцией/методом, который вы хотите отследить.

Ответ 9

здесь ответили fooobar.com/info/4696/...

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()])