Python __set_name__ 中的异常行为
Exception behaviour in Python __set_name__
我有一个 child class 正在利用 Python 3.6+ __set_name__
来确保拥有 classes 已经注释了字段的类型背着childclass。如果没有,则会引发异常。
但是,引发的任何异常总是被 Python 捕获,而引发 RuntimeError
。
例如:
class Child:
def __set_name__(self, owner, name):
raise Exception("OOPS!")
class Owner():
child = Child()
结果:
Traceback (most recent call last):
File "<stdin>", line 3, in __set_name__
Exception: OOPS!
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Error calling __set_name__ on 'Child' instance 'child' in 'Owner'
这很可能是预期的行为(无法找到对 __set_name__
异常的具体引用),但也可能表明预期是 __set_name__
永远不会出现异常。
如果在正确的条件下发生异常,我看到的行为不是问题。但是,鉴于我无法确定引发的异常是我的代码引发的异常,因此测试起来很棘手。
是否有更好的方法来引发异常以供测试,或者确实有一种简单的方法来检查 RuntimeError
包装的异常是否确实是我的代码引发的异常?
因此,由于您得到的是整个 "The above exception was the direct cause of the following exception",这意味着 type
(基本元类)中的某处基本上有以下效果:
try:
descr.__set_name__(A, 'attr')
except Exception as e:
raise RuntimeError(msg) from e
也就是说,它正在使用 raise new_exception from original_exception
,因此您应该能够使用 __cause__
属性反省原始异常是什么:
所以,观察:
In [1]: class Child:
...: def __set_name__(self, owner, name):
...: raise Exception("OOPS!")
...: try:
...: class Owner():
...: child = Child()
...: except RuntimeError as e:
...: err = e
...:
In [2]: err
Out[2]: RuntimeError("Error calling __set_name__ on 'Child' instance 'child' in 'Owner'")
In [3]: err.__cause__
Out[3]: Exception('OOPS!')
同样,我认为这些都没有记录在案,因此您可能依赖于实现细节。
Here is a link to the documentation 更详细地解释了这一点。
您可以访问包装异常的 __cause__
属性来检查是否由于您引发的异常而发生这种情况:
try:
class Child:
def __set_name__(self, owner, name):
raise Exception("OOPS!")
class Owner():
child = Child()
except RuntimeError as rte:
assert rte.__cause__.args[0] == "OOPS!" # or a more appropriate check
我有一个 child class 正在利用 Python 3.6+ __set_name__
来确保拥有 classes 已经注释了字段的类型背着childclass。如果没有,则会引发异常。
但是,引发的任何异常总是被 Python 捕获,而引发 RuntimeError
。
例如:
class Child:
def __set_name__(self, owner, name):
raise Exception("OOPS!")
class Owner():
child = Child()
结果:
Traceback (most recent call last):
File "<stdin>", line 3, in __set_name__
Exception: OOPS!
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Error calling __set_name__ on 'Child' instance 'child' in 'Owner'
这很可能是预期的行为(无法找到对 __set_name__
异常的具体引用),但也可能表明预期是 __set_name__
永远不会出现异常。
如果在正确的条件下发生异常,我看到的行为不是问题。但是,鉴于我无法确定引发的异常是我的代码引发的异常,因此测试起来很棘手。
是否有更好的方法来引发异常以供测试,或者确实有一种简单的方法来检查 RuntimeError
包装的异常是否确实是我的代码引发的异常?
因此,由于您得到的是整个 "The above exception was the direct cause of the following exception",这意味着 type
(基本元类)中的某处基本上有以下效果:
try:
descr.__set_name__(A, 'attr')
except Exception as e:
raise RuntimeError(msg) from e
也就是说,它正在使用 raise new_exception from original_exception
,因此您应该能够使用 __cause__
属性反省原始异常是什么:
所以,观察:
In [1]: class Child:
...: def __set_name__(self, owner, name):
...: raise Exception("OOPS!")
...: try:
...: class Owner():
...: child = Child()
...: except RuntimeError as e:
...: err = e
...:
In [2]: err
Out[2]: RuntimeError("Error calling __set_name__ on 'Child' instance 'child' in 'Owner'")
In [3]: err.__cause__
Out[3]: Exception('OOPS!')
同样,我认为这些都没有记录在案,因此您可能依赖于实现细节。
Here is a link to the documentation 更详细地解释了这一点。
您可以访问包装异常的 __cause__
属性来检查是否由于您引发的异常而发生这种情况:
try:
class Child:
def __set_name__(self, owner, name):
raise Exception("OOPS!")
class Owner():
child = Child()
except RuntimeError as rte:
assert rte.__cause__.args[0] == "OOPS!" # or a more appropriate check