浅谈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 写入csv乱码问题解决方法
Oct 23 Python
CentOS中升级Python版本的方法详解
Jul 10 Python
利用Pandas读取文件路径或文件名称包含中文的csv文件方法
Jul 04 Python
python读写csv文件实例代码
Jul 05 Python
python3.5 cv2 获取视频特定帧生成jpg图片
Aug 28 Python
Python Process多进程实现过程
Oct 22 Python
利用OpenCV和Python实现查找图片差异
Dec 19 Python
Python实现FLV视频拼接功能
Jan 21 Python
详解如何修改jupyter notebook的默认目录和默认浏览器
Jan 24 Python
Python字符串的15个基本操作(小结)
Feb 03 Python
基于Python的EasyGUI学习实践
May 07 Python
Python爬虫实战之爬取携程评论
Jun 02 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
laravel 根据不同组织加载不同视图的实现
2019/10/14 PHP
JS 对象介绍
2010/01/20 Javascript
基于json的jquery地区联动效果代码
2011/07/06 Javascript
实用的JS正则表达式(手机号码/IP正则/邮编正则/电话等)
2013/01/11 Javascript
js判断背景图片是否加载成功使用img的width实现
2013/05/29 Javascript
在页面中js获取光标/鼠标的坐标及光标的像素坐标
2013/11/11 Javascript
解决jQuery动态获取手机屏幕高和宽的问题
2014/05/07 Javascript
Javascript随机标签云代码实例
2016/06/21 Javascript
基于angularjs实现图片放大镜效果
2016/08/31 Javascript
JavaScript比较两个数组的内容是否相同(推荐)
2017/05/02 Javascript
Vue的实例、生命周期与Vue脚手架(vue-cli)实例详解
2017/12/27 Javascript
js遍历添加栏目类添加css 再点击其它删除css【推荐】
2018/06/12 Javascript
详解使用uni-app开发微信小程序之登录模块
2019/05/09 Javascript
Element Popover 弹出框的使用示例
2020/07/26 Javascript
[02:36]DOTA2上海特锦赛 回忆电竞生涯的重要瞬间
2016/03/25 DOTA
[03:04]2018年度DOTA2玩家最喜爱的主播-完美盛典
2018/12/16 DOTA
[00:14]护身甲盾
2019/03/06 DOTA
python正则表达式re模块详解
2014/06/25 Python
Python中关键字nonlocal和global的声明与解析
2017/03/12 Python
python实现对csv文件的列的内容读取
2018/07/04 Python
Python大数据之从网页上爬取数据的方法详解
2019/11/16 Python
Jupyter打开图形界面并画出正弦函数图像实例
2020/04/24 Python
Django多数据库联用实现方法解析
2020/11/12 Python
微信小程序之html5 canvas绘图并保存到系统相册
2019/06/20 HTML / CSS
香港化妆品经销商:我的公主
2016/08/05 全球购物
波兰家具和室内装饰品购物网站:Vivre
2018/04/10 全球购物
英国DIY汽车维修配件网站:DIY Car Service Parts
2019/08/30 全球购物
JD Sports荷兰:英国领先的运动时尚零售商
2020/03/13 全球购物
后勤服务中心总经理工作职责
2014/03/03 职场文书
十八届三中全会感言
2014/03/10 职场文书
大学生社会实践方案
2014/05/11 职场文书
2014党的群众路线教育实践活动总结报告
2014/10/31 职场文书
抄袭同学作业检讨书1000字
2014/11/20 职场文书
2014年党委工作总结
2014/11/22 职场文书
关于颐和园的导游词
2015/01/30 职场文书
Python进阶学习之带你探寻Python类的鼻祖-元类
2021/05/08 Python