检测\uuuGetAttribute\uuuuu调用是否由于

2024-06-01 13:04:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在为一个类实现__getattribute__。你知道吗

我想注意任何错误的(当然,这意味着失败是意料之中的)提供属性的失败(因为__getattribute__实现变得非常复杂)。因此,如果我的代码在引发AttributeError之前无法找到/提供属性,我会记录一个警告。你知道吗

我知道:

  1. __getattribute__鼓励实现尽可能小而简单。你知道吗
  2. 根据调用方式/原因,__getattribute__实现的行为不同被认为是错误的。你知道吗
  3. 访问属性的代码也可以try/except而不是使用hasattr。你知道吗

TL;DR:尽管如此,我还是想检测对__getattribute__的调用是否是由于hasattr(而不是“真正”尝试访问该属性)。你知道吗


Tags: 代码警告属性错误方式记录原因hasattr
2条回答

您可以使用sys._getframe来获取调用者框架,使用inspect.getframeinfo来获取发出调用的代码行,然后使用某种解析机制,例如regex(您不能使用ast.parse,因为这一行代码通常是一个不完整的语句)来查看hasattr是否是调用者。它不是很健壮,但在最合理的情况下应该可以工作:

import inspect
import sys
import re
class A:
    def __getattribute__(self, item):
        if re.search(r'\bhasattr\b', inspect.getframeinfo(sys._getframe(1)).code_context[0]):
            print('called by hasattr')
        else:
            print('called by something else')
hasattr(A(), 'foo')
getattr(A(), 'foo')

这将输出:

called by hasattr
called by something else

这是不可能的,即使通过堆栈检查。hasattr在Python调用堆栈中不生成frame对象,因为它是用C编写的,并且试图检查最后一个Python帧来猜测它是否在hasattr调用的中间被挂起,很容易出现各种各样的误报和误报。你知道吗

如果你下定决心要尽最大努力,那么我能想到的最可靠(但仍然脆弱)的麻烦就是用一个Python函数来修补builtins.hasattr,这个函数可以生成Python堆栈框架:

import builtins
import inspect
import types

_builtin_hasattr = builtins.hasattr
if not isinstance(_builtin_hasattr, types.BuiltinFunctionType):
    raise Exception('hasattr already patched by someone else!')

def hasattr(obj, name):
    return _builtin_hasattr(obj, name)

builtins.hasattr = hasattr

def probably_called_from_hasattr():
    # Caller's caller's frame.
    frame = inspect.currentframe().f_back.f_back
    return frame.f_code is hasattr.__code__

__getattribute__内调用probably_called_from_hasattr将测试您的__getattribute__是否可能是从hasattr调用的。这避免了假设调用代码使用了名称“hasattr”,或者名称“hasattr”对应于这个特定的__getattribute__调用,或者hasattr调用源自Python级别的代码而不是C

这里脆弱性的主要来源是,如果有人在monkey补丁程序运行之前保存了对真实的hasattr的引用,或者其他人在monkey补丁程序运行之前hasattr(例如,如果有人将此代码复制粘贴到同一程序中的另一个文件中)。isinstance检查试图捕捉其他人在我们面前修补hasattr的大多数情况,但它并不完美。你知道吗

另外,如果用C编写的对象上的hasattr触发了对对象的属性访问,那么您的__getattribute__看起来就像是从hasattr调用的。这是获得假阳性的最可能的方法;上一段中的所有内容都会给出假阴性。您可以通过检查hasattr帧的f_locals中的obj项是否是它应该是的对象来防止这种情况。你知道吗

最后,如果您的__getattribute__是从装饰程序创建的包装器、子类__getattribute__或类似的东西调用的,那么即使包装器或重写是从hasattr调用的,即使您希望它计数,它也不会计为来自hasattr的调用。你知道吗

相关问题 更多 >