geo*_*org 15 python exception python-2.7
tlndr:如何在函数中告诉它是否从except块(直接/间接)调用.python2.7/CPython的.
我使用python 2.7并尝试__context__为我的自定义异常类提供类似于py3的东西:
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)
Run Code Online (Sandbox Code Playgroud)
这似乎工作正常:
try:
1/0
except:
raise MyErr('bang!')
#>__main__.MyErr: ('bang!',) from ZeroDivisionError('integer division or modulo by zero',)
Run Code Online (Sandbox Code Playgroud)
有时我需要MyErr在异常块之外被提升.这也很好:
raise MyErr('just so')
#>__main__.MyErr: ('just so',) from None
Run Code Online (Sandbox Code Playgroud)
但是,如果在此点之前存在处理的异常,则将其错误地设置为以下语境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
Run Code Online (Sandbox Code Playgroud)
我想原因是 sys.exc_info只返回"last"而不是"current"异常:
此函数返回三个值的元组,这些值提供有关当前正在处理的异常的信息.<...>这里,"处理异常"被定义为"执行或执行了except子句".
所以,我的问题是:如何判断的解释是执行的except条款(而不是有它过去执行).换句话说:有没有办法知道堆栈中MyErr.__init__是否存在exceptup?
我的应用程序不可移植,欢迎任何Cpython特定的黑客攻击.
jhe*_*ann 10
这是使用CPython 2.7.3测试的:
$ python myerr.py
MyErr('bang!',) from ZeroDivisionError('integer division or modulo by zero',)
MyErr('nobang!',)
Run Code Online (Sandbox Code Playgroud)
只要在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
Run Code Online (Sandbox Code Playgroud)
请记住,"明确比隐含更好",所以如果你问我这会更好:
try:
…
except Exception as exc:
raise MyErr("msg", cause=exc)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1063 次 |
| 最近记录: |