gpt4 book ai didi

python - 找出异常上下文

转载 作者:太空狗 更新时间:2023-10-29 17:06:40 31 4
gpt4 key购买 nike

tlndr:如何在函数中判断它是否是从 except block 调用的(直接/间接)。 python2.7/cpython.

我使用 python 2.7 并尝试为我的自定义异常类提供类似于 py3 的 __context__ 的内容:

class MyErr(Exception):
def __init__(self, *args):
Exception.__init__(self, *args)
self.context = sys.exc_info()[1]
def __str__(self):
return repr(self.args) + ' from ' + repr(self.context)

这似乎工作正常:

try:
1/0
except:
raise MyErr('bang!')

#>__main__.MyErr: ('bang!',) from ZeroDivisionError('integer division or modulo by zero',)

有时我需要在异常 block 之外引发 MyErr。这也很好:

raise MyErr('just so')

#>__main__.MyErr: ('just so',) from None

但是,如果在此之前已经处理过异常,则它被错误地设置为 MyErr 的上下文:

try:
print xxx
except Exception as e:
pass

# ...1000 lines of code....
raise MyErr('look out')

#>__main__.MyErr: ('look out',) from NameError("name 'xxx' is not defined",) <-- BAD

我想原因是sys.exc_info只返回“最后”而不是“当前”异常:

This function returns a tuple of three values that give information about the exception that is currently being handled. <...> Here, “handling an exception” is defined as “executing or having executed an except clause.”

所以,我的问题是:如何判断解释器正在执行except子句(而不是它在过去执行过).换句话说:有没有办法知道 MyErr.__init__ 中是否有 except 在堆栈上?

我的应用程序不可移植,欢迎任何特定于 CPython 的 hack。

最佳答案

这是用 CPython 2.7.3 测试的:

$ python myerr.py 
MyErr('bang!',) from ZeroDivisionError('integer division or modulo by zero',)
MyErr('nobang!',)

只要魔术异常是在 except 子句的范围内直接创建的,它就可以工作。不过,一些额外的代码可以解除该限制。

import sys
import opcode

SETUP_EXCEPT = opcode.opmap["SETUP_EXCEPT"]
SETUP_FINALLY = opcode.opmap["SETUP_FINALLY"]
END_FINALLY = opcode.opmap["END_FINALLY"]

def try_blocks(co):
"""Generate code positions for try/except/end-of-block."""
stack = []
code = co.co_code
n = len(code)
i = 0
while i < n:
op = ord(code[i])
if op in (SETUP_EXCEPT, SETUP_FINALLY):
stack.append((i, i + ord(code[i+1]) + ord(code[i+2])*256))
elif op == END_FINALLY:
yield stack.pop() + (i,)
i += 3 if op >= opcode.HAVE_ARGUMENT else 1

class MyErr(Exception):
"""Magic exception."""

def __init__(self, *args):
callee = sys._getframe(1)
try:
in_except = any(i[1] <= callee.f_lasti < i[2] for i in try_blocks(callee.f_code))
finally:
callee = None

Exception.__init__(self, *args)
self.cause = sys.exc_info()[1] if in_except else None

def __str__(self):
return "%r from %r" % (self, self.cause) if self.cause else repr(self)

if __name__ == "__main__":
try:
try:
1/0
except:
x = MyErr('bang!')
raise x
except Exception as exc:
print exc

try:
raise MyErr('nobang!')
except Exception as exc:
print exc
finally:
pass

请记住,“显式优于隐式”,所以如果你问我,这会更好:

try:

except Exception as exc:
raise MyErr("msg", cause=exc)

关于python - 找出异常上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19234134/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com