浅谈Python traceback的优雅处理


Posted in Python onAugust 31, 2018

刚接触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]])

  1. 跟print_tb相比多了两个参数etype和value,分别是exception type和exception value,加上tb(traceback object),正好是sys.exc_info()返回的三个值
  2. 另外,与print_tb相比,打印信息多了开头的"Traceback (most...)"信息以及最后一行的异常类型和value信息
  3. 还有一个不同是当异常为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官方文档 https://docs.python.org/2/library/traceback.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中执行shell命令的几个方法小结
Sep 18 Python
Python中第三方库Requests库的高级用法详解
Mar 12 Python
Python3下错误AttributeError: ‘dict’ object has no attribute’iteritems‘的分析与解决
Jul 06 Python
老生常谈Python startswith()函数与endswith函数
Sep 08 Python
Python2.7基于笛卡尔积算法实现N个数组的排列组合运算示例
Nov 23 Python
python实现BackPropagation算法
Dec 14 Python
Python去除、替换字符串空格的处理方法
Apr 01 Python
opencv python 图像去噪的实现方法
Aug 31 Python
基于python框架Scrapy爬取自己的博客内容过程详解
Aug 05 Python
基于python全局设置id 自动化测试元素定位过程解析
Sep 04 Python
python打印异常信息的两种实现方式
Dec 24 Python
详解Python中的编码问题(encoding与decode、str与bytes)
Sep 30 Python
python梯度下降法的简单示例
Aug 31 #Python
wxPython的安装与使用教程
Aug 31 #Python
python traceback捕获并打印异常的方法
Aug 31 #Python
基于python中theano库的线性回归
Aug 31 #Python
基于随机梯度下降的矩阵分解推荐算法(python)
Aug 31 #Python
python实现梯度下降算法
Mar 24 #Python
wtfPython—Python中一组有趣微妙的代码【收藏】
Aug 31 #Python
You might like
PHP的FTP学习(一)
2006/10/09 PHP
截获网站title标签之家内容的例子
2006/10/09 PHP
php与php MySQL 之间的关系
2009/07/17 PHP
PHP字符串函数系列之nl2br(),在字符串中的每个新行 (\n) 之前插入 HTML 换行符br
2011/11/10 PHP
PHP中的正则表达式函数介绍
2012/02/27 PHP
PHP正则表达式之定界符和原子介绍
2012/10/05 PHP
基于PHP对XML的操作详解
2013/06/07 PHP
PHP实现的最大正向匹配算法示例
2017/12/19 PHP
ExtJs的Date格式字符代码
2010/12/30 Javascript
图片动画横条广告带上下滚动的JS代码
2013/10/25 Javascript
使用jQuery的attr方法来修改onclick值
2014/07/07 Javascript
jQuery动态创建html元素的常用方法汇总
2014/09/05 Javascript
JS实现为表格动态添加标题的方法
2015/03/31 Javascript
Javascript实现飞动广告效果的方法
2015/05/25 Javascript
JavaScript基于原型链的继承
2016/06/22 Javascript
AngularJS实现根据变量改变动态加载模板的方法
2016/11/04 Javascript
微信小程序-getUserInfo回调的实例详解
2017/10/27 Javascript
微信小程序实现上传图片功能
2018/05/28 Javascript
Vue 路由切换时页面内容没有重新加载的解决方法
2018/09/01 Javascript
vue elementUI table 自定义表头和行合并的实例代码
2019/05/22 Javascript
JQuery实现简单的复选框树形结构图示例【附源码下载】
2019/07/16 jQuery
微信小程序之滑动页面隐藏和显示组件功能的实现代码
2020/06/19 Javascript
[11:01]2014DOTA2西雅图邀请赛 冷冷带你探秘威斯汀
2014/07/08 DOTA
使用Keras建立模型并训练等一系列操作方式
2020/07/02 Python
欧洲有机婴儿食品最大的市场:Organic Baby Food(供美国和加拿大)
2018/03/28 全球购物
回馈慈善的设计师太阳镜:DIFF eyewear
2019/10/17 全球购物
Fanatics官网:运动服装、球衣、运动装备
2020/10/12 全球购物
医学专业应届生的自我评价
2014/02/28 职场文书
运动会开幕式主持词
2014/03/28 职场文书
高考寄语大全
2014/04/08 职场文书
服务承诺书怎么写
2014/05/24 职场文书
网络研修心得体会
2016/01/08 职场文书
大学生自我鉴定怎么写
2019/05/07 职场文书
Python实现简单的猜单词
2021/06/15 Python
能让Python提速超40倍的神器Cython详解
2021/06/24 Python
gateway与spring-boot-starter-web冲突问题的解决
2021/07/16 Java/Android