Debugging:获取调用函数的文件名和行号?

我当前在Python建立相当复杂的系统,当我调试时,我经常在几个脚本中放置简单的打印语句。要保持概述,我经常还要打印出打印语句所在的文件名和行号。我当然可以手动做,或者用这样的东西:

from inspect import currentframe, getframeinfo

print getframeinfo(currentframe()).filename + ':' + str(getframeinfo(currentframe()).lineno) + ' - ', 'what I actually want to print out here'

哪些打印出类似的东西:

filenameX.py:273 - what I actually want to print out here

让它更简单,我希望能够做些什么:

print debuginfo(), 'what I actually want to print out here'

我把它进入某处的函数,然后尝试过:

from debugutil import debuginfo
print debuginfo(), 'what I actually want to print out here'
print debuginfo(), 'and something else here'

不幸的是,我得到:

debugutil.py:3 - what I actually want to print out here
debugutil.py:3 - and something else here

打印出我定义函数的文件名和行号,而不是我呼叫的行debuginfo().这很明显,因为代码位于debugutil.pyfile。

So my question is actually: 如何获取调用此debugInfo()函数的文件名和行号?

回答 6

  1. 赞同 93

    The function inspect.stack()returns列表frame记录从呼叫者开始并搬出,您可以用来获取所需信息:

    from inspect import getframeinfo, stack
    
    def debuginfo(message):
        caller = getframeinfo(stack()[1][0])
        print("%s:%d - %s" % (caller.filename, caller.lineno, message)) # python3 syntax print
    
    def grr(arg):
        debuginfo(arg)      # <-- stack()[1][0] for this line
    
    grr("aargh")            # <-- stack()[2][0] for this line
    

    Output.:

    example.py:8 - aargh
    

    Zero Piraeus
    谢谢你的帮助!我将在我正在努力的每个项目中使用它!你先生,让我的生活变得更好,我现在可以正式称之为“真棒”!Kramer65
    This is great! There was a function in django which I was not able to figure out where it was being called from.SANKALP
    cool答案。这是我正在寻找的。堆栈()的伎俩很好......谢谢。!哈兰纳
    谢谢这个!我也使用了内省,但它非常迅速使用工作补丁。如果您包含,甚至更好从__future__导入print_function,absolute_import使用没有打印陈述。Christian Tismer
    我把它放在al.py和b.py中的ran grr()。控制台输出作为路径的。明明
  2. 赞同 8

    If you put your trace code in another function, and call that from your main code, then you need to make sure you get the stack information from the grandparent, not the parent or the trace function itself

    Below是3级深度系统的例子,以进一步阐明我的意思。我的主函数调用跟踪函数,该函数调用另一个函数来完成工作。

    ######################################
    
    import sys, os, inspect, time
    time_start = 0.0                    # initial start time
    
    def trace_libary_init():
        global time_start
    
        time_start = time.time()      # when the program started
    
    def trace_library_do(relative_frame, msg=""):
        global time_start
    
        time_now = time.time()
    
            # relative_frame is 0 for current function (this one), 
            # 1 for direct parent, or 2 for grand parent.. 
    
        total_stack         = inspect.stack()                   # total complete stack
        total_depth         = len(total_stack)                  # length of total stack
        frameinfo           = total_stack[relative_frame][0]    # info on rel frame
        relative_depth      = total_depth - relative_frame      # length of stack there
    
            # Information on function at the relative frame number
    
        func_name           = frameinfo.f_code.co_name
        filename            = os.path.basename(frameinfo.f_code.co_filename)
        line_number         = frameinfo.f_lineno                # of the call
        func_firstlineno    = frameinfo.f_code.co_firstlineno
    
        fileline            = "%s:%d" % (filename, line_number)
        time_diff           = time_now - time_start
    
        print("%13.6f %-20s %-24s %s" % (time_diff, fileline, func_name, msg))
    
    ################################
    
    def trace_do(msg=""):
        trace_library_do(1, "trace within interface function")
        trace_library_do(2, msg)
        # any common tracing stuff you might want to do...
    
    ################################
    
    def main(argc, argv):
        rc=0
        trace_libary_init()
        for i in range(3):
            trace_do("this is at step %i" %i)
            time.sleep((i+1) * 0.1)         # in 1/10's of a second
        return rc
    
    rc=main(sys.argv.__len__(), sys.argv)
    sys.exit(rc)
    

    This will print something like:

    $ python test.py 
        0.000005 test.py:39           trace_do         trace within interface func
        0.001231 test.py:49           main             this is at step 0
        0.101541 test.py:39           trace_do         trace within interface func
        0.101900 test.py:49           main             this is at step 1
        0.302469 test.py:39           trace_do         trace within interface func
        0.302828 test.py:49           main             this is at step 2
    

    顶部的TRACE_LIBRARY_DO()函数是您可以删除库的示例,然后从其他跟踪功能调用它。相对深度值控制Python堆栈中的条目被打印。

    我在该函数中显示了少量其他有趣的值,如函数的开始,总堆栈深度和文件的完整路径。我没有显示它,但函数中的全局和局部变量也可以在检查中提供,以及全部堆栈跟踪到您的所有其他功能。我上面显示的信息有足够的信息来制作分层调用/返回时序迹线。它实际上并不重要,进一步从这里创建自己的源级调试器的主要部分 - 并且它主要只是坐在那里等待使用。

    我确定有人将对象使用Inspect Structures返回的数据使用内部字段,因为可能会对您提供同样的访问功能。但我通过在Python调试器中通过这种类型的代码逐步找到它们,并且他们至少在这里工作。我正在运行Python 2.7.12,如果您运行不同的版本,您的结果可能非常可能。

    在任何情况下,我强烈建议您将检查代码导入您自己的一些Python代码,并查看它可以提供的内容 - 特别是如果您可以在良好的Python调试器中单次代码。您将在Python如何工作,从而了解语言的好处,以及窗帘后面发生的事情。

    使用时间戳的源级别跟踪是增强您对您的代码所做的了解的好方法,尤其是在更多的动态实时环境中。关于这种类型的跟踪代码的伟大事物是,一旦写了它,您就不需要调试器支持来查看它。

    PDB
  3. 赞同 1

    将您发布到函数的代码:

    from inspect import currentframe, getframeinfo
    
    def my_custom_debuginfo(message):
        print getframeinfo(currentframe()).filename + ':' + str(getframeinfo(currentframe()).lineno) + ' - ', message
    

    然后按照您想要使用它:

    # ... some code here ...
    my_custom_debuginfo('what I actually want to print out here')
    # ... more code ...
    

    I recommend you put that function in a separate module, that way you can reuse it every time you need it.

    Raydel Miranda
    That's what I already did, and then it prints out the file name and line number of the module in which the function is, not where I call the function. Any other ideas?Kramer65
  4. 赞同 1

    发现这个问题有点相关的问题,但我想要更多细节Re:执行(并且我不想安装整个呼叫图包)。

    If you want more detailed information, you can retrieve a full traceback with the standard library module TraceBack.,并删除堆栈对象(元组列表)traceback.extract_stack()打印出来traceback.print_stack().这更适合我的需求,希望它有助于别人!

    Crepoat
  5. 赞同 1

    PrintStack.Package现在可以为您做到这一点:

    import printstack
    
    print('Hello!')
    
    # Hello!          File "/link/to/your/file.py", line 3, in <module>
    

    PyCharm将自动制作文件链接可点击/可疑。

    座位pip install printstack.

    101
  6. 赞同 1

    使用字符串插值并显示调用者的函数名称更新到已接受的答案。

    import inspect
    def debuginfo(message):
        caller = inspect.getframeinfo(inspect.stack()[1][0])
        print(f"{caller.filename}:{caller.function}:{caller.lineno} - {message}")
    

    bsalita