簡體   English   中英

如何將python回溯限制為特定文件

[英]How to limit python traceback to specific files

我寫了很多使用外部庫的Python代碼。 我經常寫一個bug,當我運行代碼時,我會在Python控制台中獲得一個很長的回溯。 99.999999%的時間是由於我的代碼中的編碼錯誤,而不是因為包中的錯誤。 但是跟蹤一直到程序包代碼中的錯誤行,要么需要大量滾動回溯來查找我編寫的代碼,要么回溯是如此深入到我自己的代碼所沒有的包中。甚至出現在追溯中。

有沒有辦法“黑盒子”包裝代碼,或以某種方式只顯示我的代碼中的追溯線? 我希望能夠向系統指定我想要追溯的目錄或文件。

為了打印自己的堆棧跟蹤,您需要自己處理所有未處理的異常; 這就是sys.excepthook如何變得方便。

此函數的簽名是sys.excepthook(type, value, traceback) ,其作用是:

此函數打印出sys.stderr的給定回溯和異常。

因此,只要您可以使用追溯並且僅提取您關心的部分,您應該沒問題。 測試框架經常這樣做; 它們具有自定義assert函數,這些函數通常不會出現在回溯中,換句話說,它們會跳過屬於測試框架的幀。 此外,在這些情況下,測試通常也由測試框架啟動。

最終得到一個如下所示的回溯:

[ custom assert code ] + ... [ code under test ] ... + [ test runner code ]

如何識別您的代碼。

您可以在代碼中添加全局:

__mycode = True

然后識別幀:

def is_mycode(tb):
  globals = tb.tb_frame.f_globals
  return globals.has_key('__mycode')

如何提取你的幀。

  1. 跳過與您無關的幀(例如自定義斷言代碼)
  2. 確定代碼中有多少幀 - > length
  3. 提取length

     def mycode_traceback_levels(tb): length = 0 while tb and is_mycode(tb): tb = tb.tb_next length += 1 return length 

示例處理程序

def handle_exception(type, value, tb):
  # 1. skip custom assert code, e.g.
  # while tb and is_custom_assert_code(tb):
  #   tb = tb.tb_next
  # 2. only display your code
  length = mycode_traceback_levels(tb)
  print ''.join(traceback.format_exception(type, value, tb, length))

安裝處理程序:

sys.excepthook = handle_exception

接下來是什么?

您可以調整length以添加一個或多個級別,如果您仍然需要有關失敗在您自己的代碼之外的位置的一些信息。

另見https://gist.github.com/dnozay/b599a96dc2d8c69b84c6

正如其他人建議的那樣,你可以使用sys.excepthook

此函數打印出sys.stderr的給定回溯和異常。

當引發異常並且未被捕獲時,解釋使用三個參數調用sys.excepthook ,異常類,異常實例和回溯對象。 在交互式會話中,這發生在控制返回到提示之前; 在Python程序中,這發生在程序退出之前。 可以通過為sys.excepthook分配另一個三參數函數來自定義這種頂級異常的處理

(強調我的)

可以根據指定的目錄過濾extract_tb (或traceback模塊中的類似函數)提取的traceback

兩個可以提供幫助的功能:

from os.path import join, abspath
from traceback import extract_tb, format_list, format_exception_only

def spotlight(*show):
    ''' Return a function to be set as new sys.excepthook.
        It will SHOW traceback entries for files from these directories. '''
    show = tuple(join(abspath(p), '') for p in show)

    def _check_file(name):
        return name and name.startswith(show)

    def _print(type, value, tb):
        show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
        fmt = format_list(show) + format_exception_only(type, value)
        print(''.join(fmt), end='', file=sys.stderr)

    return _print

def shadow(*hide):
    ''' Return a function to be set as new sys.excepthook.
        It will HIDE traceback entries for files from these directories. '''
    hide = tuple(join(abspath(p), '') for p in hide)

    def _check_file(name):
        return name and not name.startswith(hide)

    def _print(type, value, tb):
        show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
        fmt = format_list(show) + format_exception_only(type, value)
        print(''.join(fmt), end='', file=sys.stderr)

    return _print

它們都使用traceback.extract_tb 它返回“從traceback對象中提取的”預處理“堆棧跟蹤條目”列表 ; 所有這些都是traceback.FrameSummary (一個命名的元組)的實例。 每個traceback.FrameSummary對象都有一個filename字段,用於存儲相應文件的絕對路徑。 我們檢查它是否以作為單獨函數參數提供的任何目錄路徑開始,以確定我們是否需要排除該條目(或保留它)。


這是一個例子

標准庫中的enum模塊不允許重用密鑰,

import enum
enum.Enum('Faulty', 'a a', module=__name__)

產量

Traceback (most recent call last):
  File "/home/vaultah/so/shadows/main.py", line 23, in <module>
    enum.Enum('Faulty', 'a a', module=__name__)
  File "/home/vaultah/cpython/Lib/enum.py", line 243, in __call__
    return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
  File "/home/vaultah/cpython/Lib/enum.py", line 342, in _create_
    classdict[member_name] = member_value
  File "/home/vaultah/cpython/Lib/enum.py", line 72, in __setitem__
    raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'a'

我們可以將堆棧跟蹤條目限制為我們的代碼(在/home/vaultah/so/shadows/main.py中 )。

import sys, enum
sys.excepthook = spotlight('/home/vaultah/so/shadows')
enum.Enum('Faulty', 'a a', module=__name__)

import sys, enum
sys.excepthook = shadow('/home/vaultah/cpython/Lib')
enum.Enum('Faulty', 'a a', module=__name__)

給出相同的結果:

  File "/home/vaultah/so/shadows/main.py", line 22, in <module>
    enum.Enum('Faulty', 'a a', module=__name__)
TypeError: Attempted to reuse key: 'a'

有一種方法可以排除所有站點目錄(安裝第三方軟件包的位置 - 請參閱site.getsitepackages

import sys, site, jinja2
sys.excepthook = shadow(*site.getsitepackages())
jinja2.Template('{%}')
# jinja2.exceptions.TemplateSyntaxError: unexpected '}'
#     Generates ~30 lines, but will only display 4

注意:不要忘記從SYS .__ excepthook__恢復sys.excepthook。 不幸的是,您將無法使用上下文管理器“修補”它。

traceback.extract_tb(tb)將以格式(file,line_no,type,error_statement)返回錯誤幀元組,您可以使用它來格式化回溯。 另請參閱https://pymotw.com/2/sys/exceptions.html

import sys
import traceback

def handle_exception(ex_type, ex_info, tb):
    print ex_type, ex_info, traceback.extract_tb(tb)

sys.excepthook = handle_exception

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM