11use crate :: function:: PyFuncArgs ;
2+ use crate :: obj:: objproperty:: PyPropertyRef ;
3+ use crate :: obj:: objstr:: PyStringRef ;
24use crate :: pyobject:: {
3- IntoPyObject , PyContext , PyObjectRef , PyRef , PyResult , PyValue , TypeProtocol ,
5+ AttributeProtocol , IntoPyObject , PyContext , PyObjectRef , PyRef , PyResult , PyValue ,
6+ TryFromObject , TypeProtocol ,
47} ;
58use crate :: vm:: VirtualMachine ;
69
@@ -39,6 +42,57 @@ impl PyNoneRef {
3942 fn bool ( self , _vm : & mut VirtualMachine ) -> PyResult < bool > {
4043 Ok ( false )
4144 }
45+
46+ fn get_attribute ( self , name : PyStringRef , vm : & mut VirtualMachine ) -> PyResult {
47+ trace ! ( "None.__getattribute__({:?}, {:?})" , self , name) ;
48+ let cls = self . typ ( ) . into_object ( ) ;
49+
50+ // Properties use a comparision with None to determine if they are either invoked by am
51+ // instance binding or a class binding. But if the object itself is None then this detection
52+ // won't work. Instead we call a special function on property that bypasses this check, as
53+ // we are invoking it as a instance binding.
54+ //
55+ // In CPython they instead call the slot tp_descr_get with NULL to indicates it's an
56+ // instance binding.
57+ // https://github.com/python/cpython/blob/master/Objects/typeobject.c#L3281
58+ fn call_descriptor (
59+ descriptor : PyObjectRef ,
60+ get_func : PyObjectRef ,
61+ obj : PyObjectRef ,
62+ cls : PyObjectRef ,
63+ vm : & mut VirtualMachine ,
64+ ) -> PyResult {
65+ if let Ok ( property) = PyPropertyRef :: try_from_object ( vm, descriptor. clone ( ) ) {
66+ property. instance_binding_get ( obj, vm)
67+ } else {
68+ vm. invoke ( get_func, vec ! [ descriptor, obj, cls] )
69+ }
70+ }
71+
72+ if let Some ( attr) = cls. get_attr ( & name. value ) {
73+ let attr_class = attr. typ ( ) ;
74+ if attr_class. has_attr ( "__set__" ) {
75+ if let Some ( get_func) = attr_class. get_attr ( "__get__" ) {
76+ return call_descriptor ( attr, get_func, self . into_object ( ) , cls. clone ( ) , vm) ;
77+ }
78+ }
79+ }
80+
81+ if let Some ( obj_attr) = self . as_object ( ) . get_attr ( & name. value ) {
82+ Ok ( obj_attr)
83+ } else if let Some ( attr) = cls. get_attr ( & name. value ) {
84+ let attr_class = attr. typ ( ) ;
85+ if let Some ( get_func) = attr_class. get_attr ( "__get__" ) {
86+ call_descriptor ( attr, get_func, self . into_object ( ) , cls. clone ( ) , vm)
87+ } else {
88+ Ok ( attr)
89+ }
90+ } else if let Some ( getter) = cls. get_attr ( "__getattr__" ) {
91+ vm. invoke ( getter, vec ! [ self . into_object( ) , name. into_object( ) ] )
92+ } else {
93+ Err ( vm. new_attribute_error ( format ! ( "{} has no attribute '{}'" , self . as_object( ) , name) ) )
94+ }
95+ }
4296}
4397
4498fn none_new ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
@@ -55,5 +109,6 @@ pub fn init(context: &PyContext) {
55109 "__new__" => context. new_rustfunc( none_new) ,
56110 "__repr__" => context. new_rustfunc( PyNoneRef :: repr) ,
57111 "__bool__" => context. new_rustfunc( PyNoneRef :: bool ) ,
112+ "__getattribute__" => context. new_rustfunc( PyNoneRef :: get_attribute)
58113 } ) ;
59114}
0 commit comments