捕获未引发的异常的文件和行号

时间:2014-11-28 19:05:02

标签: python exception

如果永远不会引发该对象,是否可以收集创建异常对象的文件和行号?

示例:

def compute_score_for_test(test):
  # Exceptions can be added to a test explicitly.
  if test_is_unscorable(test):
    test.add_error(UnscorableTestError('...'))

  ...

  # During certain operations, caught exceptions also get added to a test.
  try:
    ...
  except Exception as e:
    test.add_error(e)

... much later ...

for test in test_set:
  for error in test.errors:
    # Using print as a contrived example; we actually store these values in a database.
    print '[%s] %s (%s:%d)' % (
      error.__class__.__name__,
      error.message,
      error.file_name,    # <- ...somehow
      error.line_number,  #
    )

如果没有别的办法,可以选择将代码添加到test.add_error()

4 个答案:

答案 0 :(得分:0)

您可以使用inspect模块获取当前行,如下所示:

import inspect

def lineno():
    return inspect.currentframe().f_back.f_lineno

def filename():
    return inspect.currentframe().f_back.f_code.co_filename

您可以将这些信息存储在您的错误对象中(顺便说一句,不必是Exception,并且很可能是任何其他有意义的类型,因为您不会{{1} }它)。

如果您对raise函数感到好奇,lineno()给出当前帧,inspect.currentframe()给出父帧(即,函数被调用的行)和f_back显然给出了行号。

f_lineno功能以类似的方式运行。查看filename()模块文档,了解当前框架可以获得的所有内容。它实际上非常完整。

在你的情况下,我会选择类似的东西:

inspect

test.add_error(YourError(frame_info=get_frame_info())

答案 1 :(得分:0)

This SO answer显示了如何获取当前文件和行号,例如:

import sys

def LINE( back = 0 ):
    return sys._getframe( back + 1 ).f_lineno
def FILE( back = 0 ):

def foo():
  print "this is line number", LINE(), " of file ", FILE()

您可以将该信息放入错误对象中。

答案 2 :(得分:0)

另一种方法是使用Python日志记录模块(为这种东西制作),并使用其格式化选项。 https://docs.python.org/2/library/logging.html#logrecord-attributes

一个例子:

import logging
message_format = '[%(asctime)s] [%(levelname)s] [%(pathname)s:%(funcName)s:%(lineno)s]\n%(message)s\n'
logging.basicConfig(format=message_format)
logger = logging.getLogger('test')
logger.error('Testing 1, 2, 3...')

答案 3 :(得分:0)

这是我们最终采用的解决方案的简化版本。它处理捕获的异常以及将异常对象直接传递给add_error()

from inspect import getsourcefile, getfile, getlineno, stack
from sys import exc_info

...

def add_error(self, error):
    """
    Adds an error to the test.

    :type error: Exception
    """
    error.file = None
    error.line = None

    # Try to guess the file and line number where the error occurred.
    exc_tb = exc_info()[2]

    try:
        if exc_tb:
            # If we have traceback info (i.e., from a caught exception), add it to the error.
            error.file = (getsourcefile(exc_tb.tb_frame) or getfile(exc_tb.tb_frame))
            error.line = getlineno(exc_tb.tb_frame)
        else:
            #
            # Otherwise, find the caller in the current stack.
            #
            # stack()[1] = (<frame object>, filename, line_number, caller, context_line, pos_in_context_line)
            # Note that we pass 0 to `stack()` so that it doesn't return any context lines,
            #  since we're not interested in that.
            #
            (error.file, error.line) = stack(0)[1][1:3]
    except Exception as e:
        # Well, we tried.
        error.file = '(unable to determine due to %s %r)' % (e.__class__.__name__, e.message,)
    finally:
        #
        # Very important to make sure `exc_tb` gets deleted to prevent a circular reference.  Although Python's GC
        #   is smart enough to catch this and free the memory anyway, it's better not to create the circular
        #   reference in the first place.
        #
        # :see: https://docs.python.org/2/library/sys.html#sys.exc_info
        #
        del exc_tb

    self.errors.append(error)