1. 程式人生 > >python unittest原始碼解析四----關於原始碼中的__unittest的用處

python unittest原始碼解析四----關於原始碼中的__unittest的用處

讀python的unittest原始碼的時候,發現每個檔案的開頭部分都有這樣一句話:

__unittest = True
我們知道,下劃線在python中含有特殊的意義,而雙下線(double underline scope)就表示私有的,

那麼這裡在每個檔案中定義一個私有的全域性變數__unittest是幹嘛用?__unittest是用來標誌異常是否發生在unittest的程式碼中。

敘述它的用途之前,我們先來認識一下python中的兩個內部型別:

Frame objects

Frame objects represent execution frames. They may occur in traceback objects(see below).

Special read-only attributes: f_back is to the previous stack frame(towards the caller), orNone if this is the bottom stack frame;f_code is the code object being executed in this frame;f_localsis the dictionary used to look up local variables;f_globals is used for global variables;f_builtins is used for built-in (intrinsic) names;f_restricted

is a flag indicating whether the function is executing inrestricted execution mode; f_lasti gives the precise instruction (thisis an index into the bytecode string of the code object).

Special writable attributes: f_trace, if not None, is a functioncalled at the start of each source code line (this is used by the debugger);f_exc_type

,f_exc_value,f_exc_traceback represent thelast exception raised in the parent frame provided another exception was everraised in the current frame (in all other cases they are None);f_linenois the current line number of the frame — writing to this from within a tracefunction jumps to the given line (only for the bottom-most frame). A debuggercan implement a Jump command (aka Set Next Statement) by writing to f_lineno.

Traceback objects

Traceback objects represent a stack trace of an exception. A traceback objectis created when an exception occurs.When the search for an exception handler unwinds the execution stack, at each unwound level a traceback object is inserted in front of the current traceback. When an exception handler isentered, the stack trace is made available to the program. (See sectionThe try statement.)It is accessible as sys.exc_traceback,and also as the third item of the tuple returned bysys.exc_info().The latter is the preferred interface, since it works correctly when the program isusing multiple threads. When the program contains no suitable handler, the stacktrace is written (nicely formatted) to the standard error stream; if theinterpreter is interactive, it is also made available to the user assys.last_traceback.

Special read-only attributes: tb_next is the next level in the stacktrace (towards the frame where the exception occurred), orNone if there isno next level;tb_frame points to the execution frame of the current level;tb_lineno gives the line number where the exception occurred;tb_lasti indicates the precise instruction. The line number and lastinstruction in the traceback may differ from the line number of its frame objectif the exception occurred in atry statement with no matching exceptclause or with a finally clause.

我用紅色字型標註了一下上面敘述中和我們下面所講內容有關的知識點。

result.py

    @failfast
    def addError(self, test, err):
        """Called when an error has occurred. 'err' is a tuple of values as
        returned by sys.exc_info().
        """
        self.errors.append((test, self._exc_info_to_string(err, test)))
        self._mirrorOutput = True
在執行單元測試的時候,如果你的用例執行出錯了,就會把錯誤記錄在result中,這裡就呼叫了上面的addError。

該方法中的_exc_info_to_string就是把當前的異常資訊轉換成string型別然後追加到result中。

這裡面有一個引數err,代表當前正在處理的異常資訊,由sys.exc_info()獲取。

sys.exc_info()

This function returns a tuple of three values that give information about theexception that is currently being handled.The information returned is specificboth to the current thread and to the current stack frame. If the current stack frame is not handling an exception, the information is taken from the callingstack frame, or its caller, and so on until a stack frame is found that ishandling an exception. Here, “handling an exception” is defined as “executingor having executed an except clause.” For any stack frame, only informationabout the most recently handled exception is accessible.

If no exception is being handled anywhere on the stack, a tuple containing threeNone values is returned. Otherwise, the values returned are(type,value,traceback).

exc_info返回的是一個tuple,其中第三個元素是一個traceback,前面我們已經講到Traceback物件是在發生異常的時候生成的,其中tb_frame指向了當前模組的異常區域。(這裡我把level翻譯成模組,從the execution frame of the current level理解這裡的level應該是指execution發生時所處的程式碼位置)
    def _exc_info_to_string(self, err, test):
        """Converts a sys.exc_info()-style tuple of values into a string."""
        exctype, value, tb = err
        # Skip test runner traceback levels
        while tb and self._is_relevant_tb_level(tb):
            tb = tb.tb_next
            。。。。。。
顯而易見,tb就是Traceback物件,上面使用while迴圈跳過了所有和unittest有關的異常,這裡有個關鍵方法----和我們標題中說道的__unittest息息相關:
    def _is_relevant_tb_level(self, tb):
        return '__unittest' in tb.tb_frame.f_globals
tb_frame指向了發生異常的區域,f_globals表示該模組中含有的全域性變數。所以通過判斷是否含有全域性變數__unittest,我們就可以知道該異常是不是屬於unittest中的異常。

那麼我們就回到上面的while迴圈,判斷tb是否是unittest相關的traceback,直到找到不是unittest相關的異常為止。