Skip to content

Commit 1b89c50

Browse files
committed
py/objtype: Don't delegate lookup of descriptor methods to __getattr__.
When descriptors are enabled, lookup of the `__get__`, `__set__` and `__delete__` descriptor methods should not be delegated to `__getattr__`. That follows CPython behaviour. Signed-off-by: Damien George <[email protected]>
1 parent 3fecab5 commit 1b89c50

File tree

2 files changed

+34
-0
lines changed

2 files changed

+34
-0
lines changed

py/objtype.c

+7
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,13 @@ static void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des
660660

661661
// try __getattr__
662662
if (attr != MP_QSTR___getattr__) {
663+
#if MICROPY_PY_DESCRIPTORS
664+
// With descriptors enabled, don't delegate lookups of __get__/__set__/__delete__.
665+
if (attr == MP_QSTR___get__ || attr == MP_QSTR___set__ || attr == MP_QSTR___delete__) {
666+
return;
667+
}
668+
#endif
669+
663670
#if MICROPY_PY_DELATTR_SETATTR
664671
// If the requested attr is __setattr__/__delattr__ then don't delegate the lookup
665672
// to __getattr__. If we followed CPython's behaviour then __setattr__/__delattr__

tests/basics/class_descriptor.py

+27
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,41 @@ class Main:
2121
try:
2222
m.__class__
2323
except AttributeError:
24+
# Target doesn't support __class__.
2425
print("SKIP")
2526
raise SystemExit
2627

2728
r = m.Forward
2829
if 'Descriptor' in repr(r.__class__):
30+
# Target doesn't support descriptors.
2931
print('SKIP')
3032
raise SystemExit
3133

34+
# Test assignment and deletion.
35+
3236
print(r)
3337
m.Forward = 'a'
3438
del m.Forward
39+
40+
# Test that lookup of descriptors like __get__ are not passed into __getattr__.
41+
42+
43+
class NonDescriptor:
44+
def __getattr__(self, attr):
45+
print("getattr", attr)
46+
47+
48+
class TestClass:
49+
non_descriptor = NonDescriptor()
50+
51+
52+
print(isinstance(TestClass().non_descriptor, NonDescriptor))
53+
54+
t = TestClass()
55+
t.non_descriptor = 123
56+
print(t.non_descriptor)
57+
58+
try:
59+
del TestClass().non_descriptor
60+
except AttributeError:
61+
print("AttributeError")

0 commit comments

Comments
 (0)