Development Tip

함수가 호출 될 때 인쇄하는 방법

yourdevel 2020. 12. 7. 21:00
반응형

함수가 호출 될 때 인쇄하는 방법


Python 스크립트를 디버깅 할 때 전체 프로그램에 대한 전체 호출 스택을 알고 싶습니다. 이상적인 상황은 파이썬이 호출 된 모든 함수 이름을 인쇄하도록하는 파이썬에 대한 명령 줄 플래그가있는 경우입니다 (나는 확인 man Python2.7했지만 이런 종류의 항목을 찾지 못했습니다).

이 스크립트의 함수 수가 많기 때문에 가능한 경우 각 함수 및 / 또는 클래스의 시작 부분에 print 문을 추가하지 않는 것이 좋습니다.

중간 해결책은 PyDev의 디버거를 사용하고 몇 개의 중단 점을 배치하고 내 프로그램에서 주어진 지점에 대한 호출 스택을 확인하는 것이므로 당분간이 접근 방식을 사용할 것입니다.

그러한 메소드가 존재한다면, 프로그램의 수명 내내 호출되는 모든 함수의 전체 목록을보고 싶습니다.


추적 함수를 사용하여이를 수행 할 수 있습니다 (반환을 추적하고 멋진 들여 쓰기를 사용하기 위해 원본 버전을 개선하기 위해 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

함수의 코드 객체는 일반적으로 관련 함수와 이름이 같지만 항상 그런 것은 아닙니다. 함수는 동적으로 생성 될 수 있기 때문입니다. 불행히도, 파이썬은 스택의 함수 객체를 추적하지 않습니다. (저는 때때로 이것을위한 패치를 제출하는 것에 대해 환상을 가지고 있습니다). 그래도 대부분의 경우 "충분히 충분"합니다.

이것이 문제가되는 경우 소스 코드에서 "실제"함수 이름을 추출하거나 (파이썬이 파일 이름과 줄 번호를 추적 함) 가비지 수집기에 어떤 함수 개체가 코드 개체를 참조하는지 알아 내도록 요청할 수 있습니다. 코드 객체를 공유하는 함수가 두 개 이상있을 수 있지만 이름으로 충분할 수 있습니다.

4 년 후 다시 돌아와서 파이썬 2.6 sys.setprofile()이상에서는 sys.settrace(). 동일한 추적 기능을 사용할 수 있습니다. 프로필 함수는 함수가 입력되거나 종료 될 때만 호출되므로 함수 내부에있는 것은 최고 속도로 실행됩니다.


알아 두어야 할 또 다른 좋은 도구는 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)


몇 가지 옵션이 있습니다. 디버거가 충분하지 않은 경우를 사용하여 추적 기능설정할 수 있습니다 sys.settrace(). 이 함수는 기본적으로 실행되는 모든 Python 코드 행에서 호출되지만 함수 호출을 쉽게 식별 할 수 있습니다. 링크 된 문서를 참조하십시오.

trace요청한대로 정확히 수행하지는 않지만 모듈에 관심이있을 수도 있습니다 . --trackcalls옵션 을 살펴보십시오 .


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

역 추적


여기에 설명 된대로 settrace를 사용할 수 있습니다. Tracing python code . 페이지 끝 부분에있는 버전을 사용하십시오. 해당 페이지의 코드를 코드에 삽입하여 코드가 실행될 때 정확히 어떤 행이 실행되는지 확인합니다. 호출 된 함수의 이름 만 볼 수 있도록 필터링 할 수도 있습니다.


나는 킨달의 대답을 받아 들여 그 위에 세웠다.

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.settrace () 대신 sys.setprofile ()을 사용합니다.

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

It also provides a pretty flexible query syntax that allows specifying module, file/lineno, function, etc which helps because the default output (which includes standard library function calls) can be pretty big.


You can also use a decorator for specific functions you want to trace (with their arguments):

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

Just import this file and add a @TraceCalls() before the function/method you want to trace.

참고URL : https://stackoverflow.com/questions/8315389/how-do-i-print-functions-as-they-are-called

반응형