这是一件非常愚蠢的事情,大多数人会说它无法完成(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_OP10 作为参数。在我见过的所有情况下,它后面都跟着一个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。利润。
这应该让您大致了解如何执行此操作。在各种极端情况下您将无法做到这一点,但在任何正常的用例中,您都应该能够做到这一点。如果您编写的代码依赖于能够做到这一点,那么它会中断。这是一种“因为我能做到”之类的事情。