当前位置:  开发笔记 > 编程语言 > 正文

Python中的"内部异常"(带回溯)?

如何解决《Python中的"内部异常"(带回溯)?》经验,为你挑选了5个好方法。

我的背景是在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]

这包括消息和两个回溯,但它不会显示回溯中发生的异常.



1> Alexei Tenit..:
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")


`raise ... from ...`确实是在Python 3中执行此操作的正确方法.这需要更多的upvotes.
@ogrisel你可以使用`future`包来实现这个目的:http://python-future.org/compatible_idioms.html#raising-exceptions例如`from future.utils import raise_`和`raise_(ValueError,None,sys. exc_info()[2])`.

2> Glenn Maynar..:
Python 2

这很简单; 将traceback作为第三个参数传递给raise.

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

在捕获一个异常并重新引发另一个异常时始终执行此操作.


Python 3添加了来自tb`和`.with_traceback(...)`的`raise E()
使用未来的软件包可以使用兼容Python2和3的方法:http://python-future.org/compatible_idioms.html#raising-exceptions例如`from future.utils import raise_`和`raise_(ValueError,None,sys.exc_info) ()[2])`.
`raise MyException(str(e)),...`等.
@GlennMaynard这是一个非常古老的问题,但是``raise``的中间参数是传递给异常的值(如果第一个参数是异常类而不是实例).因此,如果你想交换异常,而不是做``raise MyException(str(e)),None,sys.exc_info()[2]``,最好使用它:``raise MyException,e.args ,sys.exc_info()[2]``.
谢谢.这会保留回溯,但它会丢失原始异常的错误消息.如何查看消息和两个追溯?
@JossefHarush,很可能你的第一个参数不是异常类而是实例.无论如何,它已经差不多一年了,现在我根本不在乎是否使用`raise MyException(*e.args),None,sys.exc_info()[2]`或`raise MyException,e.args, sys.exc_info()[2]`.我目前的IDE建议使用前者.

3> Don Kirkby..:

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 
    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()调用的行,但异常消息现在包含命令行.



4> ilya n...:

Python 3.x中:

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

或者干脆

except Exception:
    raise MyException()

如果不处理,它将传播MyException但打印两个例外.

Python 2.x中:

raise Exception, 'Failed to process file ' + filePath, e

您可以通过终止__context__属性来阻止打印这两个例外.在这里,我使用它来编写一个上下文管理器来动态捕获和更改您的异常:(请参阅http://docs.python.org/3.1/library/stdtypes.html以扩展它们的工作方式)

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise


TypeError:raise:arg 3必须是回溯或None

5> ire_and_curs..:

我认为你不能在Python 2.x中做到这一点,但类似于这个功能的东西是Python 3的一部分.来自PEP 3134:

在今天的Python实现中,异常由三部分组成:类型,值和回溯.'sys'模块在三个并行变量exc_type,exc_value和exc_traceback中公开当前异常,sys.exc_info()函数返回这三个部分的元组,'raise'语句有一个三参数形式接受这三部分.操作异常通常需要并行传递这三个东西,这可能是单调乏味且容易出错的.此外,'except'语句只能提供对值的访问,而不是跟踪.将" traceback "属性添加到异常值可以从一个地方访问所有异常信息.

与C#的比较:

C#中的异常包含一个只读的"InnerException"属性,该属性可能指向另一个异常.它的文档[10]说"当一个异常X作为前一个异常Y的直接结果抛出时,X的InnerException属性应该包含对Y的引用." VM不自动设置此属性; 相反,所有异常构造函数都使用可选的'innerException'参数来显式设置它.' cause '属性实现与InnerException相同的目的,但是这个PEP提出了一种新的'raise'形式,而不是扩展所有异常的构造函数.C#还提供了一个GetBaseException方法,该方法直接跳转到InnerException链的末尾; 这个PEP建议没有模拟.

另请注意,Java,Ruby和Perl 5也不支持这种类型的东西.再次引用:

对于其他语言,当在'catch'/'rescue'或'finally'/'ensure'子句中发生另一个异常时,Java和Ruby都会丢弃原始异常.Perl 5缺乏内置的结构化异常处理.对于Perl 6,RFC编号88 [9]提出了一种异常机制,它在名为@@的数组中隐式保留链式异常.

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有