@@ -582,11 +582,18 @@ a pure Python equivalent:
582
582
583
583
.. testcode ::
584
584
585
+ def find_name_in_mro(cls, name, default):
586
+ "Emulate _PyType_Lookup() in Objects/typeobject.c"
587
+ for base in cls.__mro__:
588
+ if name in vars(base):
589
+ return vars(base)[name]
590
+ return default
591
+
585
592
def object_getattribute(obj, name):
586
593
"Emulate PyObject_GenericGetAttr() in Objects/object.c"
587
594
null = object()
588
595
objtype = type(obj)
589
- cls_var = getattr (objtype, name, null)
596
+ cls_var = find_name_in_mro (objtype, name, null)
590
597
descr_get = getattr(type(cls_var), '__get__', null)
591
598
if descr_get is not null:
592
599
if (hasattr(type(cls_var), '__set__')
@@ -663,6 +670,15 @@ a pure Python equivalent:
663
670
def __getattr__(self, name):
664
671
return ('getattr_hook', self, name)
665
672
673
+ class D1:
674
+ def __get__(self, obj, objtype=None):
675
+ return type(self), obj, objtype
676
+
677
+ class U1:
678
+ x = D1()
679
+
680
+ class U2(U1):
681
+ pass
666
682
667
683
.. doctest ::
668
684
:hide:
@@ -696,6 +712,10 @@ a pure Python equivalent:
696
712
>>> b.g == b[' g' ] == (' getattr_hook' , b, ' g' )
697
713
True
698
714
715
+ >>> u2 = U2()
716
+ >>> object_getattribute(u2, ' x' ) == u2.x == (D1, u2, U2)
717
+ True
718
+
699
719
Note, there is no :meth: `__getattr__ ` hook in the :meth: `__getattribute__ `
700
720
code. That is why calling :meth: `__getattribute__ ` directly or with
701
721
``super().__getattribute__ `` will bypass :meth: `__getattr__ ` entirely.
0 commit comments