这是一件非常愚蠢的事情,大多数人会说它无法完成(THC4k 为一般的 cace 提供了令人信服的证据),但它听起来确实很有趣,并且在许多实际用例中应该是完全可行的。
步骤 1。您需要退后一步。sys._getframe
用or获取第一个inspect.currentframe
(不要告诉任何人,第二个似乎与第一个有别名)。然后你可以遍历它们f.f_back
步骤 2。每个人都会有一个f.f_lasti
指示。这是帧中执行的最后一条指令。你必须保存它。现在遍历字节码 - f.f_code.co_code
- 并查找SETUP_EXCEPT
带有跳转到f.f_lasti`之后的参数的操作码。跳转点是异常处理。
步骤 3。这是它变得更加模糊的地方。关键是实际的比较操作将以COMPARE_OP
10 作为参数。在我见过的所有情况下,它后面都跟着一个POP_JUMP_IF_FALSE
. 这将跳转到下一个except
子句或该finally
子句。它前面是加载异常到堆栈的代码。如果只有一个,那么它将是直接LOAD_GLOBAL
的或 aLOAD_GLOBAL
或LOAD_FAST
(取决于具有异常的模块是全局的还是本地的)后跟 a LOAD_ATTR
。如果有多个异常被匹配,那么将有一系列加载操作,然后是BUILD_TUPLE
(惯用的)或BUILD_LIST
(一些其他奇怪或非惯用的情况)。
关键是您可以浏览LOAD_X
说明并将名称与您匹配的异常进行比较。请注意,您只比较 name。如果他们重新分配了名称,那么您就是 SOL。
步骤 4。假设您找到了匹配项。现在你需要函数对象。我能想到的最好方法如下(我保留更新的权利):f.f_code
将有一个co_filename
属性。你可以循环遍历sys.modules
,每个都有__name__
属性。您可以比较两者,记住您应该使用__name__.endswith(co_filename)
. 当你得到匹配时,你可以遍历模块函数并将它们的f.func_code.co_firstlineno
属性与框架进行比较f.f_lineno
属性。当你得到匹配时,你就有了你的功能。您还应该遍历模块中每个类的方法。处理可能发生在某些嵌套函数中,在这种情况下,我目前想不出明智的做法。(这将是一个完全不同的字节码黑客,本身就是脆弱的)
步骤 5。利润。
这应该让您大致了解如何执行此操作。在各种极端情况下您将无法做到这一点,但在任何正常的用例中,您都应该能够做到这一点。如果您编写的代码依赖于能够做到这一点,那么它会中断。这是一种“因为我能做到”之类的事情。