Python:检查异常引发的位置

发布于 2024-10-04 19:35:24 字数 379 浏览 9 评论 0原文

采取这段代码:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

温柔嚣张 2024-10-11 19:35:24

如果不实际引发异常,您无法决定在何处处理异常。这很容易在这里看到:

try: 
    raise input('Raise which?')
except input('Catch which?') as e: 
    pass`

任何执行您想要的操作的函数都必须在此处预测用户输入。整个努力是徒劳的,Python 也不支持它。

无论如何,我希望你只是出于兴趣才问......

You can't decide where a Exception will get handled without actually raising the Exception. This is easy to see here:

try: 
    raise input('Raise which?')
except input('Catch which?') as e: 
    pass`

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 ...

二智少女猫性小仙女 2024-10-11 19:35:24

这是一件非常愚蠢的事情,大多数人会说这是不可能完成的(THC4k 为一般情况提供了令人信服的证据),但它听起来确实很有趣,并且在许多实际用例中应该是完全可行的。

第 1 步。你需要退一步穿过框架。使用 sys._getframeinspect.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_GLOBALLOAD_GLOBALLOAD_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 or inspect.currentframe (don't tell anyone, the second seems aliased to the first). Then you can iterate through them with f.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 a SETUP_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 a POP_JUMP_IF_FALSE. This will jump to the next except clause or the finally 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 straight LOAD_GLOBAL or a LOAD_GLOBAL or LOAD_FAST (depending if the module with the exceptions is global or local) followed by a LOAD_ATTR. If there are multiple exceptions being matched then there will be a sequence of load operations followed by a BUILD_TUPLE (idiomatic) or BUILD_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 a co_filename attribute. You can loop through sys.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 their f.func_code.co_firstlineno attribute with the frames f.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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文