我遇到的问题与here有问题但错误地关闭了 作为another related question的副本:
Python库如何以自己的方式引发异常 代码它没有暴露在追溯?动机就是成功 清除库函数调用不正确:违规 呼叫者中的一行似乎应该承担责任,而不是 图书馆内部(故意,正确地)提出的 异常。
正如伊恩对封闭式问题的评论所指出的,这是不 与询问如何调整调用者中的代码进行更改相同 回溯的出现方式。
我的失败尝试如下。
在标记为QUESTION
的行上,我尝试修改了属性
tb
,例如tb.tb_frame = tb.tb_frame.f_back
但这导致了
AttributeError: readonly attribute
。我也试图创建一个
具有与tb
相同属性的duck-typed对象,但在此期间失败
reraise()
,TypeError: __traceback__ must be a traceback or None
。
(我试图通过子类化traceback
来智胜这一点,但TypeError: type 'traceback' is not an acceptable base type
已得到满足。
对于这个X,调整traceback
对象本身可能是错误的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行。因此,鲍勃(或那里的Bobs的一个重要子群)将通过电子邮件发送给爱丽丝说'嘿,你的代码在第17行被破坏了。"但事实上,LibraryFunction()
确切知道它在发布异常时所做的工作。 Alice可以尽力重新说明异常,以尽可能清楚地表明库被错误地称为,但追溯会引起注意力。实际犯错的地方是Bob代码的第4行。此外,Alice的代码知道这个,因此不允许Alice的代码将责任分配到它所属的位置。因此,为了获得最大可能的透明度并减少支持流量,回溯应该不会超过Bob代码的第4行,没有 Bob必须自己编写此行为。
mattbornski提供了一个"你不应该想要这样做"回答here,我认为这一点很重要。当然,如果你说"那不是我的错,"转移责任,你不知道你必须把责任推到正确的地方。但是你做知道你(LibraryFunction
)已经努力对你所交付的输入参数进行显式类型检查,并且这项检查已成功(在某种意义上说支票本身没有引发异常),结果为负。可以肯定的是,Bob的代码可能不会出现错误"也许它没有产生无效的输入 - 也许Bob只是从其他地方传递了这个论点。但不同的是,他已经在没有检查的上传递了它。如果Bob努力进行检查,并且检查代码本身没有引发异常,那么Bob也可以随意RaiseFromAbove
,从而帮助用户修改代码。
答案 0 :(得分:1)
您可以像this answer中一样重新定义sys.excepthook
函数:
import os
import sys
import traceback
def RaiseFromAbove( exception ):
sys.excepthook = print_traceback
raise( exception )
def print_traceback(exc_type, exc_value, tb):
for i, (frame, _) in enumerate(traceback.walk_tb(tb)):
# for example:
if os.path.basename(frame.f_code.co_filename) == 'AlicesLibrary.py':
limit = i
break
else:
limit = None
traceback.print_exception(exc_type, exc_value, tb, limit=limit, chain=False)
这需要用于walk_tb
答案 1 :(得分:1)
我认为你的X的Pythonic Y会改变你的代码,以便故障本身引发异常。在这种情况下,不是进行显式类型检查然后调用异常引发方法,而是练习EAFP和只需将1 添加到它们传入的任何内容中,如果它不起作用,就会引发错误。如果由于某种原因需要明确限制这两种类型,请将assert isinstance(a, (int, float)), "LibraryFunction() requires an int or float argument"
添加到函数的开头,并让类型验证引发错误。如果Alice库中stacktrace底部的代码行不是异常的“真实”源,请不要试图隐藏代码行,重构代码本身,以便stacktrace告诉你实际发生了什么错误。这将使Alice的代码更容易维护,Bob可以将堆栈追回到他自己的代码中,以查看他的问题在哪里开始。
答案 2 :(得分:0)
对此问题没有好的/直接/权威的答案。我在“需要权威参考”类别下发放了奖金,最接近的是Martijn的评论,即:
我怀疑了。因此,除非/直到任何人能够真正提供对此方法不可能的权威性参考,否则我将在此处将其作为“已接受”的答案发布。
但是我不接受它对Python来说不是一个值得的愿望清单项目。这个问题已经产生了相当多的“你不应该想要这样做”的情绪,我仍然不同意:
当然,Bob 应学会正确阅读追溯,但是让他更容易让他这样做是错误的 - 帮助Alice帮助他指导注意到正确的地方?鲍勃天真地接触爱丽丝并报告她的代码中的错误的情景是夸大(尽管可能)的例子,以明确这一点。更可能的是,他只会有一个不必要的2秒暂停,因为他认为“第17行的问题......哦,等等,不,来电者是问题”。但为什么不放过他,让编程用户体验更流畅?在我看来,Python的理念就是围绕消除这种摩擦。
当然,任何推定的RaiseFromAbove
可以被爱丽丝滥用或以其他方式滥用,因此可能会使Bob更加困惑而不是更少。对我而言,这是一个虚假的争论,因为它同样适用于Alice可以做出的任何其他不明智的编码决策,实际上也适用于Python和其他语言中已经存在的许多强大功能。根据说明和安全警告,正确使用时,应根据其值判断一个尖锐工具的价值。
无论如何,赏金期限即将来临,如果我什么都不做,我相信一半的赏金会回到最高投票的答案。目前这是Tore的,但对我而言,只需添加1并让Bob执行侦探工作的想法与我所驾驶的相反:这使得它看起来像更多就像Alice的代码中存在问题一样。鲍勃可能会从追踪问题的智力练习中成为更好的程序员,但他可能很匆忙,无论如何,通过这种逻辑,我们都会在裸机上进行编程。因此,我会将奖励奖励给yinnonsanders的答案,不是因为它是一个完整而令人满意的解决方案,而是因为它至少与问题的精神一致,并且可能在某些情况下有效。