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相关的异常为止。

猜你喜欢

转载自blog.csdn.net/nitibu/article/details/48392679