Python中的“内部异常”(及其回溯)是什么?
我之前主要用C#编程,最近才开始学习Python。当出现异常时,我通常想把它包裹在另一个异常里,这样可以添加更多信息,同时还想看到完整的错误追踪信息。在C#中这很简单,但在Python中该怎么做呢?
比如在C#中,我会这样做:
try
{
ProcessFile(filePath);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to process file " + filePath, ex);
}
在Python中,我可以做类似的事情:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file ' + filePath, e)
...但这样就会丢失内部异常的追踪信息!
编辑:我希望能看到两个异常的消息和两个追踪信息,并且能够把它们关联起来。也就是说,我想在输出中看到异常X发生在这里,然后异常Y发生在那儿——就像在C#中那样。这在Python 2.6中可能吗?看起来根据Glenn Maynard的回答,我目前能做到的最好是:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]
这包括了两个消息和两个追踪信息,但没有显示哪个异常在追踪信息中发生在哪里。
9 个回答
Python 3 引入了一个叫做 raise
... from
子句 的功能,可以用来链接异常。虽然 Glenn 的回答 对于 Python 2.7 很有帮助,但它只使用了原始异常的追踪信息,丢掉了错误信息和其他细节。下面是一些 Python 2.7 的例子,它们在保留其他细节的同时,把当前环境的信息添加到原始异常的错误信息中。
已知异常类型
try:
sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
_, ex, traceback = sys.exc_info()
message = "Connecting to '%s': %s." % (config['connection'],
ex.strerror)
raise IOError, (ex.errno, message), traceback
这种 raise
语句 的用法是,首先写出异常类型,接着用一个元组提供异常类构造函数的参数,最后是追踪信息。如果你使用的是早于 Python 2.2 的版本,可以查看 sys.exc_info()
的警告。
任何异常类型
这里有另一个更通用的例子,适合你不知道代码可能会捕获什么类型的异常的情况。不过缺点是,它会丢失异常类型,只会抛出一个 RuntimeError。你需要导入 traceback
模块。
except Exception:
extype, ex, tb = sys.exc_info()
formatted = traceback.format_exception_only(extype, ex)[-1]
message = "Importing row %d, %s" % (rownum, formatted)
raise RuntimeError, message, tb
修改消息
如果异常类型允许你添加上下文信息,这里还有一个选项。你可以修改异常的消息,然后重新抛出它。
import subprocess
try:
final_args = ['lsx', '/home']
s = subprocess.check_output(final_args)
except OSError as ex:
ex.strerror += ' for command {}'.format(final_args)
raise
这样会生成以下的堆栈追踪信息:
Traceback (most recent call last):
File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
s = subprocess.check_output(final_args)
File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
process = Popen(stdout=PIPE, *popenargs, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']
你可以看到它显示了 check_output()
被调用的那一行,但异常消息现在包含了命令行的信息。
Python 3
在Python 3中,你可以这样做:
try:
raise MyExceptionToBeWrapped("I have twisted my ankle")
except MyExceptionToBeWrapped as e:
raise MyWrapperException("I'm not in a good shape") from e
这样做会得到类似这样的结果:
Traceback (most recent call last):
...
MyExceptionToBeWrapped: ("I have twisted my ankle")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
MyWrapperException: ("I'm not in a good shape")
Python 2
这很简单;把错误追踪信息作为第三个参数传给 raise。
import sys
class MyException(Exception): pass
try:
raise TypeError("test")
except TypeError, e:
raise MyException(), None, sys.exc_info()[2]
在捕获一个异常后再抛出另一个异常时,记得总是这样做。