mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
Extract the Python 3.14-compatible descriptor slot dispatch from #8954. Call the raw type attribute with descriptor, instance and owner, preserving ownership and recursion handling. Extend the existing Python snippet for staticmethod, classmethod, callable-object and ordinary __get__ replacements. Assisted-by: OpenAI Codex:model identifier unavailable
196 lines
5.4 KiB
Python
196 lines
5.4 KiB
Python
class MyObject:
|
|
pass
|
|
|
|
|
|
assert not MyObject() == MyObject()
|
|
assert MyObject() != MyObject()
|
|
myobj = MyObject()
|
|
assert myobj == myobj
|
|
assert not myobj != myobj
|
|
|
|
object.__subclasshook__(1) == NotImplemented
|
|
|
|
assert MyObject().__eq__(MyObject()) == NotImplemented
|
|
assert MyObject().__ne__(MyObject()) == NotImplemented
|
|
assert MyObject().__lt__(MyObject()) == NotImplemented
|
|
assert MyObject().__le__(MyObject()) == NotImplemented
|
|
assert MyObject().__gt__(MyObject()) == NotImplemented
|
|
assert MyObject().__ge__(MyObject()) == NotImplemented
|
|
|
|
obj = MyObject()
|
|
|
|
assert obj.__eq__(obj) is True
|
|
assert obj.__ne__(obj) is False
|
|
|
|
assert not hasattr(obj, "a")
|
|
obj.__dict__ = {"a": 1}
|
|
assert obj.a == 1
|
|
|
|
del obj.__dict__
|
|
d = obj.__dict__
|
|
assert isinstance(d, dict)
|
|
assert len(d) == 0
|
|
|
|
try:
|
|
obj.a
|
|
assert False, "AttributeError expected"
|
|
except AttributeError:
|
|
pass
|
|
|
|
# Value inside the formatter goes through a different path of resolution.
|
|
# Check that it still works all the same
|
|
d = {
|
|
0: "ab",
|
|
}
|
|
assert "ab ab" == "{k[0]} {vv}".format(k=d, vv=d[0])
|
|
|
|
|
|
from testutils import assert_raises
|
|
|
|
|
|
class AttributeHook:
|
|
existing = "class"
|
|
|
|
@property
|
|
def absent(self):
|
|
raise AttributeError("descriptor miss")
|
|
|
|
@property
|
|
def invalid(self):
|
|
raise ValueError("descriptor error")
|
|
|
|
def __getattr__(self, name):
|
|
if name == "refused":
|
|
raise AttributeError("hook refusal")
|
|
return "hook:" + name
|
|
|
|
|
|
class InheritedAttributeHook(AttributeHook):
|
|
pass
|
|
|
|
|
|
hooked = InheritedAttributeHook()
|
|
hooked.instance = "instance"
|
|
assert hooked.instance == "instance"
|
|
assert hooked.existing == "class"
|
|
assert hooked.missing == "hook:missing"
|
|
assert hooked.absent == "hook:absent"
|
|
with assert_raises(ValueError):
|
|
hooked.invalid
|
|
with assert_raises(AttributeError) as caught:
|
|
hooked.refused
|
|
assert caught.exception.__context__ is None
|
|
assert not hasattr(hooked, "refused")
|
|
assert getattr(hooked, "refused", "default") == "default"
|
|
|
|
# Changing __getattribute__ must invalidate the generic lookup path.
|
|
InheritedAttributeHook.__getattribute__ = lambda self, name: "custom:" + name
|
|
assert hooked.existing == "custom:existing"
|
|
InheritedAttributeHook.__getattribute__ = object.__getattribute__
|
|
assert hooked.existing == "class"
|
|
assert hooked.missing == "hook:missing"
|
|
del InheritedAttributeHook.__getattribute__
|
|
assert hooked.existing == "class"
|
|
|
|
|
|
class SlotAttributeHook:
|
|
__slots__ = ("value",)
|
|
|
|
def __getattr__(self, name):
|
|
return "unset"
|
|
|
|
|
|
slotted_hook = SlotAttributeHook()
|
|
assert slotted_hook.value == "unset"
|
|
slotted_hook.value = "set"
|
|
assert slotted_hook.value == "set"
|
|
|
|
# Retain the raw hook across callbacks, but delay its descriptor binding.
|
|
for lookup_kind in ("property", "getattribute"):
|
|
for change in ("replace", "delete"):
|
|
hook_events = []
|
|
|
|
class ChangingHookDescriptor:
|
|
def __get__(self, obj, owner):
|
|
hook_events.append("bind")
|
|
return lambda name: "original:" + name
|
|
|
|
class ChangingAttributeHook:
|
|
__getattr__ = ChangingHookDescriptor()
|
|
|
|
def changing_lookup(self, name):
|
|
hook_events.append("lookup")
|
|
if change == "replace":
|
|
ChangingAttributeHook.__getattr__ = lambda self, name: "replacement"
|
|
else:
|
|
del ChangingAttributeHook.__getattr__
|
|
raise AttributeError("lookup miss")
|
|
|
|
if lookup_kind == "property":
|
|
ChangingAttributeHook.value = property(
|
|
lambda self: changing_lookup(self, "value")
|
|
)
|
|
else:
|
|
ChangingAttributeHook.__getattribute__ = changing_lookup
|
|
assert ChangingAttributeHook().value == "original:value"
|
|
assert hook_events == ["lookup", "bind"]
|
|
|
|
|
|
class AddedAttributeHook:
|
|
def __getattribute__(self, name):
|
|
AddedAttributeHook.__getattr__ = lambda self, name: "added"
|
|
raise AttributeError("initial miss")
|
|
|
|
|
|
with assert_raises(AttributeError):
|
|
AddedAttributeHook().value
|
|
assert AddedAttributeHook().value == "added"
|
|
|
|
|
|
class RaisingAttributeHook(AttributeHook):
|
|
def __getattr__(self, name):
|
|
raise ValueError("hook error")
|
|
|
|
|
|
with assert_raises(ValueError) as caught:
|
|
RaisingAttributeHook().absent
|
|
assert caught.exception.__context__ is None
|
|
try:
|
|
raise RuntimeError("outer error")
|
|
except RuntimeError as outer:
|
|
with assert_raises(ValueError) as caught:
|
|
RaisingAttributeHook().absent
|
|
assert caught.exception.__context__ is outer
|
|
|
|
|
|
# The descriptor slot calls the raw __get__ attribute without binding it first.
|
|
class RawGetDescriptor:
|
|
__get__ = staticmethod(lambda *args: args)
|
|
|
|
|
|
class RawGetOwner:
|
|
value = RawGetDescriptor()
|
|
|
|
|
|
raw_descriptor = vars(RawGetOwner)["value"]
|
|
raw_instance = RawGetOwner()
|
|
assert raw_instance.value == (raw_descriptor, raw_instance, RawGetOwner)
|
|
assert RawGetOwner.value == (raw_descriptor, None, RawGetOwner)
|
|
|
|
|
|
class CallableGet:
|
|
def __get__(self, instance, owner):
|
|
raise AssertionError("__get__ must not be bound before the slot call")
|
|
|
|
def __call__(self, *args):
|
|
return args
|
|
|
|
|
|
RawGetDescriptor.__get__ = CallableGet()
|
|
assert raw_instance.value == (raw_descriptor, raw_instance, RawGetOwner)
|
|
RawGetDescriptor.__get__ = classmethod(lambda *args: args)
|
|
with assert_raises(TypeError):
|
|
raw_instance.value
|
|
RawGetDescriptor.__get__ = lambda self, instance, owner: (self, instance, owner)
|
|
assert raw_instance.value == (raw_descriptor, raw_instance, RawGetOwner)
|