【发布时间】:2017-09-16 04:44:33
【问题描述】:
我有与here 相同的问题,但错误地关闭了 作为another related question的副本:
Python 库如何以它自己的方式引发异常? 它没有在回溯中公开的代码?动机是实现它 清楚库函数被错误地调用:违规 来电者的线路应该看起来应该承担责任,而不是 图书馆内(故意和正确地)提出的行 例外。
正如 Ian 在对已结束问题的评论中指出的那样,这不是 就像问你如何调整调用者中的代码来改变 回溯的出现方式。
我失败的尝试如下。
在标记为QUESTION 的行,我尝试修改
tb,例如tb.tb_frame = tb.tb_frame.f_back 但这会导致
AttributeError: readonly attribute。我也试图创建一个
具有与tb 相同属性的鸭子类型对象,但在
reraise(),与TypeError: __traceback__ must be a traceback or None。
(我试图通过子类化traceback 来战胜它,但遇到了TypeError: type 'traceback' is not an acceptable base type)。
调整traceback 对象本身在任何情况下都可能是这个X 的错误Y - 也许还有其他策略?
假设 Alice 编写了以下库:
import sys
# home-made six-esque Python {2,3}-compatible reraise() definition
def reraise( cls, instance, tb=None ): # Python 3 definition
raise ( cls() if instance is None else instance ).with_traceback( tb )
try:
Exception().with_traceback
except: # fall back to Python 2 definition
exec( 'def reraise( cls, instance, tb=None ): raise cls, instance, tb' )
# has to be wrapped in exec because this would be a syntax error in Python 3.0
def LibraryFunction( a ):
if isinstance( a, (int, float) ):
return a + 1
else:
err = TypeError( "expected int or float, got %r" % a )
RaiseFromAbove( err ) # the traceback should NOT show this line
# because this function knows that it is operating
# correctly and that the caller is at fault
def RaiseFromAbove( exception, levels=1 ):
# start by raising and immediately catching the exception
# so that we can get a traceback from sys.exc_info()
try:
raise( exception )
except:
cls, instance, tb = sys.exc_info()
for i in range( levels + 1 ):
pass # QUESTION: how can we manipulate tb here, to remove its deepest levels?
reraise( cls, instance, tb )
现在,假设 Alice 发布了库,而 Bob 下载了它。 Bob 编写了如下调用它的代码:
from AlicesLibrary import LibraryFunction
def Foo():
LibraryFunction( 'invalid input' ) # traceback should reach this line but go no deeper
Foo()
关键是,由于没有有效的RaiseFromAbove,因此回溯将显示异常源自 Alice 库的第 17 行。因此,Bob(或其中一个重要的 Bob 子群)将给 Alice 发送电子邮件说“嘿,你的代码在第 17 行被破坏了。”但事实上,LibraryFunction() 确切地知道它在发出异常时做了什么。 Alice 可以尽最大努力重新表述异常,以尽可能清楚地表明该库被错误地调用,但回溯将注意力从这一事实中移开。实际犯错的地方是 Bob 代码的第 4 行。此外,Alice 的代码知道这一点,因此允许 Alice 的代码将责任归咎于它所属的地方并不是权力错位。因此,为了尽可能提高透明度并减少支持流量,回溯的深度不应超过 Bob 代码的第 4 行,Bob 不必自己编写此行为的代码。
mattbornski 提供了一个“你不应该这样做”的答案here,我认为这忽略了一个重要的点。当然,如果你说“这不是我的错”并推卸责任,你不知道你一定是把责任推到了正确的地方。但是您确实知道您 (LibraryFunction) 已经努力对您收到的输入参数进行显式类型检查,并且该检查已成功(从某种意义上说,检查本身没有引发异常),结果为负。当然,Bob 的代码可能没有“错误”,因为它可能没有生成无效输入——也许 Bob 只是从其他地方传递了这个参数。但不同的是,他在没有检查的情况下通过了它。如果 Bob 努力进行检查,并且检查代码本身没有引发异常,那么 Bob 也应该随时联系RaiseFromAbove,从而帮助他的代码的用户。
【问题讨论】:
-
IMO 你不能。如果您可以控制引用的打印方式,则可以隐藏一些帧,参考:mail.python.org/pipermail/python-list/2012-October/632386.html
-
将路径反映到您的库中的回溯到底有什么问题?如果没有完整的回溯,您将如何调试自己的库代码中的错误?如果调用者传入了错误的数据,您可以提前验证并抛出异常early,但仍在您的库中。但是不要仅仅因为您不相信使用您的库的开发人员知道如何读取回溯而诉诸黑客攻击。
-
异常不应归咎于责备——引发异常有很多正当理由。我不认为非初学者 Python 开发人员通常认为异常 = 库中的错误。如果您提出信息异常,开发人员应该能够理解。
-
如果可能的话,它可以掩盖库代码中的真正错误。这样的错误功能会使图书馆很快变得非常不受欢迎。只需记录您的库函数验证其输入并引发明显的自定义异常 -
InvalidArgumentsError或类似 - 如果它们无效。 -
您为什么不正确地记录您的图书馆?我的意思是,如果 bob 想要使用开源库并且既不阅读代码也不阅读文档,这意味着键盘和椅子之间存在问题。如果鲍勃读到“无效输入会引发异常”之类的内容,我认为鲍勃不会责怪爱丽丝。
标签: python exception exception-handling