简体   繁体   English

带模块名称的Python回溯

[英]Python traceback with module names

Stack traces in Python show file paths. Python中的堆栈跟踪显示文件路径。 Is there any way to get them to show fully qualified function names instead? 有没有办法让他们显示完全合格的功能名称?

Example: 例:

class Foo(object):
    def bar(self):
        raise Exception, "Some error."

def inner():
    return Foo().bar()

def outer():
    return inner()

I'd like my output to look like this: 我希望我的输出看起来像这样:

In __main__.Foo.bar ("scratchpad.py", line 3)
   __main__.inner ("scratchpad.py", line 6)
   __main__.outer ("scratchpad.py", line 9)
Exception: Some error.

If it changes anything, I'm using Python 2.7. 如果它改变了什么,我正在使用Python 2.7。


Here's what I have so far: 这是我到目前为止所拥有的:

import sys

class Foo(object):
    def bar(self):
        raise Exception, "Dummy value."

def inner():
    "Inner function..."
    return Foo().bar()

def outer():
    return inner()


try:
    outer()
except Exception, error:
    traceback = sys.exc_info()[2]
    while traceback is not None:
        frame = traceback.tb_frame
        print 'Name', frame.f_globals['__name__']+'.'+frame.f_code.co_name
        docs = frame.f_code.co_consts[0]
        if docs and docs != -1: # docs can be None or -1.
            print 'Docs "%s"' % docs
        print 'Args', frame.f_code.co_argcount
        print 'File "%s", line %d' % (frame.f_code.co_filename, frame.f_lineno)
        print
        traceback = traceback.tb_next

When I run it, it prints 当我运行它时,它会打印出来

$ python pretty-stack.py
Name __main__.<module>
Args 0
File "pretty-stack.py", line 28

Name __main__.outer
Args 0
File "pretty-stack.py", line 14

Name __main__.inner
Docs "Inner function..."
Args 0
File "pretty-stack.py", line 11

Name __main__.bar
Args 1
File "pretty-stack.py", line 7

It's almost there, but I have trouble with important use cases. 它几乎就在那里,但我遇到了重要的用例问题。 For example, I can't get the class name Foo for Foo.bar() . 例如,我无法获得Foo.bar()的类名Foo

There's no direct way to access symbols from a traceback since only "code objects" are accessible and, as the Python docs on code objects say: 没有直接的方法来从回溯中访问符号,因为只有“代码对象”是可访问的,并且正如代码对象上的Python文档所说:

Unlike function objects, code objects are immutable and contain no references (directly or indirectly) to mutable objects. 与函数对象不同,代码对象是不可变的,并且不包含(直接或间接)可变对象的引用。

It seems that to retrieve the modules, functions and classes involved in the traceback, we need to search for them. 似乎要检索回溯中涉及的模块,函数和类,我们需要搜索它们。


I've got an experimental version that seems to work. 我有一个似乎有效的实验版本。 This implementation is based on walking the module referenced by the code object to find the function or method that references the code object in question. 此实现基于遍历代码对象引用的模块,以查找引用相关代码对象的函数或方法。

from collections import namedtuple
import inspect
import sys

from nested.external import Foo

def inner(a, b='qux'):
    "Inner function..."
    return Foo().bar()

def outer(a, *args, **kwds):
    return inner(a)

def resolve_signature(function):
    """Get a formatted string that looks like the function's signature."""
    prgs, vrgs, kwds, defs = inspect.getargspec(function)
    arguments = []
    if defs:
        for name in prgs[:len(defs)-1]:
            arguments.append(name)
        for i,name in enumerate(prgs[len(defs)-1]):
            arguments.append('%s=%r'%(name,defs[i]))
    else:
        arguments.extend(prgs)
    if vrgs:
        arguments.append('*'+vrgs)
    if kwds:
        arguments.append('**'+kwds)
    return '('+', '.join(arguments)+')'


def resolve_scope(module_name, code):
    """Resolve the scope name for a code object provided its module name."""
    # Resolve module.
    module = sys.modules.get(module_name, None)
    if not module:
        return '<hidden-module>' + '.' + code.co_name + '(?)'

    # Check module's functions.
    symbols = inspect.getmembers(module, inspect.isfunction)
    for symbol_name,symbol_info in symbols:
        if symbol_info.func_code is code:
            scope = module_name + '.'
            return scope + code.co_name + resolve_signature(symbol_info)

    # Check module's classes.
    symbols = inspect.getmembers(module, inspect.isclass)
    for symbol_name,symbol_info in symbols:
        # Check class' methods.
        members = inspect.getmembers(symbol_info, inspect.ismethod)
        for method_name,method_info in members:
            if method_info.__func__.func_code is code:
                scope = module_name + '.' + symbol_name + '.'
                return scope + code.co_name + resolve_signature(method_info)

    # Default to the thing's name.  This usually happens
    # when resolving the stack frame for module-level code.
    return code.co_name

Frame = namedtuple('Frame', ['call', 'file', 'line', 'help'])

def pretty_stack(traceback=None):
    """Returns a simple stack frame."""
    frames = []
    if traceback is None:
        traceback = sys.exc_info()[2]
    while traceback is not None:
        frame = traceback.tb_frame
        call = resolve_scope(frame.f_globals['__name__'], frame.f_code)
        path = frame.f_code.co_filename.replace('\\', '/')
        line = frame.f_lineno
        docs = frame.f_code.co_consts[0]
        if docs == -1:
            docs = None
        frames.append(Frame(call, path, line, docs))
        traceback = traceback.tb_next
    return frames

try:
    outer(1)
except Exception, error:
    frames = pretty_stack()
    for frame in frames:
        print frame.call
        print '  -> "%s", line %d.' % (frame.file, frame.line)
        if frame.help:
            print frame.help
        print

When I run this, I get something like: 当我运行这个时,我会得到类似的东西:

$ python pretty-stack.py
<module>
  -> "pretty-stack.py", line 84.

__main__.outer(a, *args, **kwds)
  -> "pretty-stack.py", line 14.

__main__.inner(a='qux')
  -> "pretty-stack.py", line 11.
Inner function...

nested.external.Foo.bar(self)
  -> "C:/Users/acaron/Desktop/nested/external.py", line 3.

Note that this even prints function signatures and doc strings, which may help during debugging. 请注意,这甚至会打印函数签名和doc字符串,这在调试期间可能会有所帮助。

It may not work as expected in presence of descriptors and whatnot, I haven't tried very elaborate use cases. 在描述符和诸如此类的情况下,它可能无法正常工作,我没有尝试过非常复杂的用例。 If you can find a case in which it doesn't work, let me know and I'll try to patch it. 如果你能找到一个不起作用的案例,请告诉我,我会尝试修补它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM