Python:检查异常引发的位置
采取这段代码:
def A():
try:
B()
except Exception:
pass
def B():
C()
def C():
print exception_handling_pointer()
A()
函数exception_handling_pointer
应该返回一个指向函数的指针,在该函数中将首先检查此特定异常是否被处理。即,在这种情况下,我期望输出是……。例如:
<function A ...>
如何实现函数exception_handling_pointer
?
Take this code:
def A():
try:
B()
except Exception:
pass
def B():
C()
def C():
print exception_handling_pointer()
A()
The function exception_handling_pointer
should return me a pointer to the function where this specific exception would be checked first for being handled. I.e., in this case, I would expect the output to be sth. like:
<function A ...>
How can I implement the function exception_handling_pointer
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果不实际引发异常,您无法决定在何处处理异常。这很容易在这里看到:
任何执行您想要的操作的函数都必须在此处预测用户输入。整个努力是徒劳的,Python 也不支持它。
无论如何,我希望你只是出于兴趣才问......
You can't decide where a Exception will get handled without actually raising the Exception. This is easy to see here:
Any function that does what you want would have to predict user input here. The whole endeavor is futile and Python has no support for it.
Anyways, i hope you ask only out of interest ...
这是一件非常愚蠢的事情,大多数人会说这是不可能完成的(THC4k 为一般情况提供了令人信服的证据),但它听起来确实很有趣,并且在许多实际用例中应该是完全可行的。
第 1 步。你需要退一步穿过框架。使用
sys._getframe
或inspect.currentframe
获取第一个(不要告诉任何人,第二个似乎是第一个的别名)。然后,您可以使用f.f_back
第 2 步 迭代它们。每个指令都有一个
f.f_lasti
指令。这是帧中执行的最后一条指令。你必须保存它。现在回溯字节码 -f.f_code.co_code
- 并查找SETUP_EXCEPT
操作码,其参数跳转到 f.f_lasti 之后 `。跳转点就是异常处理。第 3 步。这就是事情变得更加模糊的地方。关键是实际的比较操作将是一个以 10 作为参数的
COMPARE_OP
。在我见过的所有情况下,它后面都会跟着一个POP_JUMP_IF_FALSE
。这将跳转到下一个except
子句或finally
子句。其前面是加载异常到堆栈的代码。如果只有一个,那么它将是一个直接的LOAD_GLOBAL
或LOAD_GLOBAL
或LOAD_FAST
(取决于有例外的模块是全局的还是local) 后跟LOAD_ATTR
。如果匹配多个异常,则将出现一系列加载操作,后跟BUILD_TUPLE
(惯用)或BUILD_LIST
(其他一些奇怪或非惯用的情况)。关键是您可以浏览
LOAD_X
指令并将名称与您匹配的异常进行比较。请注意,您仅比较名称。如果他们重新分配了名称,那么您就是 SOL。第 4 步。假设您找到了匹配项。现在您需要函数对象。我能想到的最好方法如下(我保留更新的权利):
f.f_code
将具有co_filename
属性。您可以循环遍历sys.modules
,每个模块都将具有__name__
属性。您可以比较两者,记住您应该使用__name__.endswith(co_filename)
。当您获得匹配项时,您可以循环遍历模块函数并将其f.func_code.co_firstlineno
属性与框架f.f_lineno
属性进行比较。当你获得匹配时,你就有了你的功能。您还应该循环访问模块中每个类的方法。处理可能发生在某些嵌套函数中,在这种情况下,我目前无法想到明智的做法。 (这将是一个完全不同的字节码黑客,并且本身就很不稳定)第 5 步。利润。
这应该能让您大致了解如何进行此操作。在各种各样的极端情况下,您将无法做到这一点,但在任何正常的用例中,您应该能够完成它。如果您编写的代码依赖于能否完成它,那么它将会崩溃。这有点像“因为我能做所以做”之类的事情。
This is a pretty silly thing to do and most people would say that it can't be done (THC4k gives compelling evidence of this for the general cace) but it does sound fun and should be perfectly doable in many real use-cases.
step 1. You need to step back through the frames. Get the first one with
sys._getframe
orinspect.currentframe
(don't tell anyone, the second seems aliased to the first). Then you can iterate through them withf.f_back
step 2. Each one will have a
f.f_lasti
instruction. This is the last instruction that was executed in the frame. You'll have to save it. Now go backwords through the bytecode -f.f_code.co_code
- and look for aSETUP_EXCEPT
opcode with an argument that jumps to after f.f_lasti`. The jump point is the exception handling.step 3. This is where it gets fuzzier. The key is that the actual comparison operation will be a
COMPARE_OP
with a 10 as its argument. In all cases that I've seen, it's followed by aPOP_JUMP_IF_FALSE
. This will jump to the nextexcept
clause or thefinally
clause. It will be preceded by the code that loads loads the exceptions onto the stack. If there is only one, then it will be a straightLOAD_GLOBAL
or aLOAD_GLOBAL
orLOAD_FAST
(depending if the module with the exceptions is global or local) followed by aLOAD_ATTR
. If there are multiple exceptions being matched then there will be a sequence of load operations followed by aBUILD_TUPLE
(idiomatic) orBUILD_LIST
(some other weird or non-idiomatic situation).The point is that you can go through the
LOAD_X
instructions and compare the name to the exception that you're matching. Note that you're comparing name only. If they've reassigned the name, you're SOL.step 4. Let's assume that you found a match. Now you need the function object. The best way that I can think of to do this follows (I reserve the right to update): The
f.f_code
will have aco_filename
attribute. You can loop throughsys.modules
and each one will have__name__
attribute. You can compare the two keeping in mind that you should use__name__.endswith(co_filename)
. When you get a match, you can loop over the modules functions and compare theirf.func_code.co_firstlineno
attribute with the framesf.f_lineno
attribute. When you get a match, you have your function. You should loop over the methods of each class in the module as well. There's the possibility that the handling is occurring in some nested function in which case, I can't currently think of a sensible thing to do. (It would be a whole other bytecode hack and would itself be flakey)step 5. Profit.
This should give you the general idea of how to go about doing this. There are all sorts of corner cases where you wont be able to do it but in any normal use-case, you should be able to pull it off. If you write code that depends on being able to do it, it will break though. This is sort of "Do it because I can" sort of thing.