追莫名其妙的bugs利器~mark~

转自:https://www.jianshu.com/p/a8cb5375171a

 

Python Traceback详解

 

刚接触Python的时候,简单的异常处理已经可以帮助我们解决大多数问题,但是随着逐渐地深入,我们会发现有很多情况下简单的异常处理已经无法解决问题了,如下代码,单纯的打印异常所能提供的信息会非常有限。

def func1():
raise Exception("--func1 exception--") def main():
try:
func1()
except Exception as e:
print e if __name__ == '__main__':
main()

执行后输出如下:

--func1 exception--

通过示例,我们发现普通的打印异常只有很少量的信息(通常是异常的value值),这种情况下我们很难定位在哪块代码出的问题,以及如何出现这种异常。那么到底要如何打印更加详细的信息呢?下面我们就来一一介绍。

sys.exc_info和traceback object

Python程序的traceback信息均来源于一个叫做traceback object的对象,而这个traceback object通常是通过函数sys.exc_info()来获取的,先来看一个例子:

import sys

def func1():
raise NameError("--func1 exception--") def main():
try:
func1()
except Exception as e:
exc_type, exc_value, exc_traceback_obj = sys.exc_info()
print "exc_type: %s" % exc_type
print "exc_value: %s" % exc_value
print "exc_traceback_obj: %s" % exc_traceback_obj if __name__ == '__main__':
main()

执行后输出如下:

exc_type: <type 'exceptions.NameError'>
exc_value: --func1 exception--
exc_traceback_obj: <traceback object at 0x7faddf5d93b0>

通过以上示例我们可以看出,sys.exc_info()获取了当前处理的exception的相关信息,并返回一个元组,元组的第一个数据是异常的类型(示例是NameError类型),第二个返回值是异常的value值,第三个就是我们要的traceback object.

有了traceback object我们就可以通过traceback module来打印和格式化traceback的相关信息,下面我们就来看下traceback module的相关函数。

traceback module

Python的traceback module提供一整套接口用于提取,格式化和打印Python程序的stack traces信息,下面我们通过例子来详细了解下这些接口:

print_tb

import sys
import traceback def func1():
raise NameError("--func1 exception--") def main():
try:
func1()
except Exception as e:
exc_type, exc_value, exc_traceback_obj = sys.exc_info()
traceback.print_tb(exc_traceback_obj) if __name__ == '__main__':
main()

输出:

File "<ipython-input-23-52bdf2c9489c>", line 11, in main
func1()
File "<ipython-input-23-52bdf2c9489c>", line 6, in func1
raise NameError("--func1 exception--")

这里我们可以发现打印的异常信息更加详细了,下面我们了解下print_tb的详细信息:

traceback.print_tb(tb[, limit[, file]])
  • tb: 这个就是traceback object, 是我们通过sys.exc_info获取到的
  • limit: 这个是限制stack trace层级的,如果不设或者为None,就会打印所有层级的stack trace
  • file: 这个是设置打印的输出流的,可以为文件,也可以是stdout之类的file-like object。如果不设或为None,则输出到sys.stderr。

print_exception

import sys
import traceback def func1():
raise NameError("--func1 exception--") def func2():
func1() def main():
try:
func2()
except Exception as e:
exc_type, exc_value, exc_traceback_obj = sys.exc_info()
traceback.print_exception(exc_type, exc_value, exc_traceback_obj, limit=2, file=sys.stdout) if __name__ == '__main__':
main()

输出:

Traceback (most recent call last):
File "<ipython-input-24-a68061acf52f>", line 13, in main
func2()
File "<ipython-input-24-a68061acf52f>", line 9, in func2
func1()
NameError: --func1 exception--

看下定义:

traceback.print_exception(etype, value, tb[, limit[, file]])
  • 跟print_tb相比多了两个参数etype和value,分别是exception type和exception value,加上tb(traceback object),正好是sys.exc_info()返回的三个值
  • 另外,与print_tb相比,打印信息多了开头的"Traceback (most...)"信息以及最后一行的异常类型和value信息
  • 还有一个不同是当异常为SyntaxError时,会有"^"来指示语法错误的位置

print_exc

print_exc是简化版的print_exception, 由于exception type, value和traceback object都可以通过sys.exc_info()获取,因此print_exc()就自动执行exc_info()来帮助获取这三个参数了,也因此这个函数是我们的程序中最常用的,因为它足够简单

import sys
import traceback def func1():
raise NameError("--func1 exception--") def func2():
func1() def main():
try:
func2()
except Exception as e:
traceback.print_exc(limit=1, file=sys.stdout) if __name__ == '__main__':
main()

输出(由于limit=1,因此只有一个层级被打印出来):

Traceback (most recent call last):
File "<ipython-input-25-a1f5c73b97c4>", line 13, in main
func2()
NameError: --func1 exception--

定义如下:

traceback.print_exc([limit[, file]])
  • 只有两个参数,够简单

format_exc

import logging
import sys
import traceback
logger = logging.getLogger("traceback_test") def func1():
raise NameError("--func1 exception--") def func2():
func1() def main():
try:
func2()
except Exception as e:
logger.error(traceback.format_exc(limit=1, file=sys.stdout)) if __name__ == '__main__':
main()

从这个例子可以看出有时候我们想得到的是一个字符串,比如我们想通过logger将异常记录在log里,这个时候就需要format_exc了,这个也是最常用的一个函数,它跟print_exc用法相同,只是不直接打印而是返回了字符串。

traceback module中还有一些其它的函数,但因为并不常用,就不在展开来讲,感兴趣的同学可以看下参考链接中的文档。

获取线程中的异常信息

通常情况下我们无法将多线程中的异常带回主线程,所以也就无法打印线程中的异常,而通过上边学到这些知识,我们可以对线程做如下修改,从而实现捕获线程异常的目的。
以下示例来自weidong的博客文章,稍有修改(见参考链接)

import threading
import traceback def my_func():
raise BaseException("thread exception") class ExceptionThread(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None):
"""
Redirect exceptions of thread to an exception handler.
"""
threading.Thread.__init__(self, group, target, name, args, kwargs, verbose)
if kwargs is None:
kwargs = {}
self._target = target
self._args = args
self._kwargs = kwargs
self._exc = None def run(self):
try:
if self._target:
self._target()
except BaseException as e:
import sys
self._exc = sys.exc_info()
finally:
#Avoid a refcycle if the thread is running a function with
#an argument that has a member that points to the thread.
del self._target, self._args, self._kwargs def join(self):
threading.Thread.join(self)
if self._exc:
msg = "Thread '%s' threw an exception: %s" % (self.getName(), self._exc[1])
new_exc = Exception(msg)
raise new_exc.__class__, new_exc, self._exc[2] t = ExceptionThread(target=my_func, name='my_thread')
t.start()
try:
t.join()
except:
traceback.print_exc()

输出如下:

Traceback (most recent call last):
File "/data/code/testcode/thread_exc.py", line 43, in <module>
t.join()
File "/data/code/testcode/thread_exc.py", line 23, in run
self._target()
File "/data/code/testcode/thread_exc.py", line 5, in my_func
raise BaseException("thread exception")
Exception: Thread 'my_thread' threw an exception: thread exception

这样我们就得到了线程中的异常信息。

参考链接

traceback官方文档

weidong's blog

 
 

[转] Python Traceback详解的更多相关文章

  1. Python Collections详解

    Python Collections详解 collections模块在内置数据结构(list.tuple.dict.set)的基础上,提供了几个额外的数据结构:ChainMap.Counter.deq ...

  2. python生成器详解

    1. 生成器 利用迭代器(迭代器详解python迭代器详解),我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成.但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记 ...

  3. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  4. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

  5. Python 递归函数 详解

    Python 递归函数 详解   在函数内调用当前函数本身的函数就是递归函数   下面是一个递归函数的实例: 第一次接触递归函数的人,都会被它调用本身而搞得晕头转向,而且看上面的函数调用,得到的结果会 ...

  6. python线程详解

    #线程状态 #线程同步(锁)#多线程的优势在于可以同时运行多个任务,至少感觉起来是这样,但是当线程需要共享数据时,可能存在数据不同步的问题. #threading模块#常用方法:'''threadin ...

  7. python数据类型详解(全面)

    python数据类型详解 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8.字典9.日期 1.字符串1.1.如何在Python中使用字符串a.使用单引号(')用单引号括起来表示字 ...

  8. 转 python数据类型详解

    python数据类型详解 目录 1.字符串 2.布尔类型 3.整数 4.浮点数 5.数字 6.列表 7.元组 8.字典 9.日期 1.字符串 1.1.如何在Python中使用字符串 a.使用单引号(' ...

  9. python多线程详解

    目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 二.线程实现 threading模块 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 互斥锁 递归锁 信号量 ...

随机推荐

  1. Go语言反射规则

    Go语言反射规则 - The Laws of Reflection 转:http://my.oschina.net/qbit/blog/213720 原文地址:http://blog.golang.o ...

  2. python要点简要-一日精通python

    Python2.x是历史,Python3.x是这门语言的现在和未来.Python2和3大约90%的地方都很相似的. 1.注释 单行注释:在注释的前面输入#号,如下: # 这是一条注释 多行注释:使用三 ...

  3. Python继承扩展内置类

    继承最有趣的应用是给内置类添加功能,在之前的Contact类中,我们将联系人添加到所有联系人的列表里,如果想通过名字来搜索,那么就可以在Contact类添加一个方法用于搜索,但是这种方法实际上属于列表 ...

  4. ECharts基础

    echarts: js引用:<script type="text/javascript" src="js/echarts.js"></scri ...

  5. Python基础01

    Python基础 1.Python介绍 2.安装 3.Hello World程序 4.变量 5.用户输入 6.表达式if ...else语句 7.表达式for 循环 8.表达式while 循环 9.数 ...

  6. Visual Studio连接到TFS

    我在学校自己使用git,公司使用VSS,然后这个项目又使用TFS.Visual Studio连接到TFS是这样滴 1.点连接到团队项目 2.添加TFS服务器的url,写到你的http:XXX/tfs就 ...

  7. 如何优雅地使用Sublime Text3

    此文非原创,出处见文章结尾. 一.Sublime Text 3插件安装 优雅使用Sublime Text,插件则是不可缺少的存在:而插件的备份就显得非常的重要(譬如:各平台同步:更换系统/电脑,迅速使 ...

  8. C++回顾day02---<引用>---待补充

    一:引用概念---引用就是为一个变量起一个别名 每个变量都是指向一块内存空间的标识,引用就是重新设置一个标识,但是这个标识还是指向同一个内存空间 和指针类似(其实引用本质就是使用了一个常指针 cons ...

  9. 9.组合模式(Composite Pattern)

    动机(Motivate):    组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素 ...

  10. C#编程思想(持续更新)

    1.将约束的参数先用变量保存,一定不变的设为const,在使用时不直接填入数字而是使用这些变量,这样可以很大程度上方便后续参数的修改 2.字段先用属性封装后,所有的调用都使用属性而不是字段 3.要返回 ...