67

我正在使用 python 来评估一些测量数据。由于许多可能的结果,很难处理或可能的组合。有时在评估过程中会发生错误。这通常是索引错误,因为我超出了测量数据的范围。

很难找出问题发生在代码中的哪个位置。如果我知道在哪一行引发了错误,那将有很大帮助。如果我使用以下代码:

try:
    result = evaluateData(data)
except Exception, err:
    print ("Error: %s.\n" % str(err))

不幸的是,这只告诉我存在索引错误。我想了解有关异常的更多详细信息(代码行、变量等)以了解发生了什么。可能吗?

谢谢你。

4

8 回答 8

95

解决方案,打印文件名、行号、行本身和异常描述:

import linecache
import sys

def PrintException():
    exc_type, exc_obj, tb = sys.exc_info()
    f = tb.tb_frame
    lineno = tb.tb_lineno
    filename = f.f_code.co_filename
    linecache.checkcache(filename)
    line = linecache.getline(filename, lineno, f.f_globals)
    print 'EXCEPTION IN ({}, LINE {} "{}"): {}'.format(filename, lineno, line.strip(), exc_obj)


try:
    print 1/0
except:
    PrintException()

输出:

EXCEPTION IN (D:/Projects/delme3.py, LINE 15 "print 1/0"): integer division or modulo by zero
于 2013-11-28T10:50:36.500 回答
37

要简单地获取您可以使用的行号sys,如果您想要更多,请尝试使用traceback模块。

import sys    
try:
    [][2]
except IndexError:
    print("Error on line {}".format(sys.exc_info()[-1].tb_lineno))

打印

Error on line 3

traceback模块文档中的示例

import sys, traceback

def lumberjack():
    bright_side_of_death()

def bright_side_of_death():
    return tuple()[0]

try:
    lumberjack()
except IndexError:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print "*** print_tb:"
    traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
    print "*** print_exception:"
    traceback.print_exception(exc_type, exc_value, exc_traceback,
                              limit=2, file=sys.stdout)
    print "*** print_exc:"
    traceback.print_exc()
    print "*** format_exc, first and last line:"
    formatted_lines = traceback.format_exc().splitlines()
    print formatted_lines[0]
    print formatted_lines[-1]
    print "*** format_exception:"
    print repr(traceback.format_exception(exc_type, exc_value,
                                          exc_traceback))
    print "*** extract_tb:"
    print repr(traceback.extract_tb(exc_traceback))
    print "*** format_tb:"
    print repr(traceback.format_tb(exc_traceback))
    print "*** tb_lineno:", exc_traceback.tb_lineno
于 2013-01-25T09:53:29.580 回答
20

我使用traceback简单而健壮的:

import traceback

try:
    raise ValueError()
except:
    print(traceback.format_exc())

出去:

Traceback (most recent call last):
  File "catch.py", line 4, in <module>
    raise ValueError()
ValueError
于 2018-11-11T13:43:12.117 回答
15

最简单的方法就是使用:

import traceback
try:
    <blah>
except IndexError:
    traceback.print_exc()

或者如果使用日志记录:

import logging
try:
    <blah>
except IndexError as e:
    logging.exception(e)
于 2017-12-19T00:28:07.863 回答
3

为您提供调用堆栈中最后一项的文件、行号和异常

from sys import exc_info
from traceback import format_exception


def print_exception():
    etype, value, tb = exc_info()
    info, error = format_exception(etype, value, tb)[-2:]
    print(f'Exception in:\n{info}\n{error}')

try:
    1 / 0
except:
    print_exception()

印刷

Exception in:
   File "file.py", line 12, in <module>
    1 / 0

ZeroDivisionError: division by zero

于 2021-03-01T03:43:58.310 回答
1

我建议使用 python 日志库,它有两个有用的方法在这种情况下可能会有所帮助。

  1. logging.findCaller()

    • findCaller(stack_info=False) - 只报告导致异常的前一个调用者的行号
    • findCaller(stack_info=True) - 报告前一个调用者的行号和堆栈,导致引发异常
  2. logging.logException()

    • 报告引发异常的 try/except 块中的行和堆栈

有关更多信息,请查看 api https://docs.python.org/3/library/logging.html

于 2019-05-14T22:57:01.590 回答
0

我总是使用这个片段

import sys, os

try:
    raise NotImplementedError("No error")
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print(exc_type, fname, exc_tb.tb_lineno)

对于不同的视图和可能的问题,您可以参考当我捕获异常时,如何获取类型、文件和行号?

于 2020-08-24T10:55:19.900 回答
0

所有的解决方案都解决了 OPs 问题,但是,如果一个人持有一个特定的错误实例并且最后一个回溯堆栈不会这样做,那么它们就不够了——下面给出了一个角落场景示例。

__traceback__在这种情况下,可以使用引发异常实例的魔法属性。这是一个系统回溯对象(公开为types.TracebackType,请参阅Types)而不是 module。这个回溯实例的行为与预期的一样(但检查总是很好):

from collections import deque
from typing import (List, )
errors: List[Exception] = deque([], 5)
    
def get_raised(error_cls: type, msg: str) -> Exception:
# for debugging, an exception instance that is not raised
# ``__traceback__`` has ``None``
    try:
        raise error_cls(msg)
    except Exception as error:
        return error

error = get_raised(NameError, 'foo')
errors.append(error)

error = get_raised(ValueError, 'bar')
errors.append(error)

try:
    raise get_raised(TypeError, 'bar')
except Exception as error:
    errors.append(error)

现在,我可以查看第一个错误的详细信息:

import types

traceback = errors[0].__traceback__  # inadvisable name due to module
line_no: int = traceback.tb_lineno
frame: types.FrameType = traceback.tb_frame
previous: Union[type(None), types.TracebackType] = traceback.tb_next
filename: str = frame.f_code.co_filename

回溯的前一个错误对于第二个错误是无,尽管前面的错误如预期的那样,但对于第三个错误,其中两次引发错误,它不是。

下面只是一个测试,在上下文中没有任何意义。如果在 webapp 的视图中引发异常(一种 500 状态的事件)被捕获并存储以供承认检查类似于 Setry.io(但免费),那么这种情况很有用。这是一个最小的示例,主页/将引发错误,该错误被捕获并且路由errors将列出它们。这是以非常集中的方式使用 Pyramid(多文件更好),没有日志记录或身份验证,并且错误日志记录可能更适合管理员检查,类似于 Sentry.io。

from pyramid.config import Configurator
from waitress import serve
from collections import deque
# just for typehinting:
from pyramid.request import Request
from pyramid.traversal import DefaultRootFactory
from pyramid.router import Router
import types
from typing import (List, )


def home_view(context: DefaultRootFactory, request: Request) -> dict:
    raise NotImplementedError('I forgot to fill this')
    return {'status': 'ok'}  # never reached.

def caught_view(error: Exception, request: Request) -> dict:
    """
    Exception above is just type hinting.
    This is controlled by the context argument in 
    either the ``add_exception_view`` method of config,
    or the ``exception_view_config`` decorator factory (callable class)
    """
    # this below is a simplification as URLDecodeError is an attack (418)
    request.response.status = 500 
    config.registry.settings['error_buffer'].append(error)
    #logging.exception(error) # were it set up.
    #slack_admin(format_error(error))  # ditto
    return {'status': 'error',  'message': 'The server crashed!!'}

def format_error(error: Exception) -> str:
    traceback = error.__traceback__  # inadvisable name due to module
    frame: types.FrameType = traceback.tb_frame
    return f'{type(error).__name__}: {error}' +\
           f'at line {traceback.tb_lineno} in file {frame.f_code.co_filename}'

def error_view(context: DefaultRootFactory, request: Request) -> dict:
    print(request.registry.settings['error_buffer'])
    return {'status': 'ok', 
            'errors':list(map(format_error, request.registry.settings['error_buffer']))
           }
    
with Configurator(settings=dict()) as config:
    config.add_route('home', '/')
    config.add_route('errors', '/errors')
    config.add_view(home_view, route_name='home', renderer='json')
    config.add_view(error_view, route_name='errors', renderer='json')
    config.add_exception_view(caught_view, context=Exception, renderer='json')
    config.registry.settings['error_buffer']: List[Exception] = deque([], 5)  
    # not in config.registry.settings, not JSON serialisable
    # config.add_request_method
    app  : Router = config.make_wsgi_app()

port = 6969
serve(app, port=port)
于 2022-02-11T13:14:36.293 回答