1
1
use crate :: function:: PyFuncArgs ;
2
+ use crate :: obj:: objproperty:: PyPropertyRef ;
3
+ use crate :: obj:: objstr:: PyStringRef ;
2
4
use crate :: pyobject:: {
3
- IntoPyObject , PyContext , PyObjectRef , PyRef , PyResult , PyValue , TypeProtocol ,
5
+ AttributeProtocol , IntoPyObject , PyContext , PyObjectRef , PyRef , PyResult , PyValue ,
6
+ TryFromObject , TypeProtocol ,
4
7
} ;
5
8
use crate :: vm:: VirtualMachine ;
6
9
@@ -39,6 +42,57 @@ impl PyNoneRef {
39
42
fn bool ( self , _vm : & mut VirtualMachine ) -> PyResult < bool > {
40
43
Ok ( false )
41
44
}
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
+ }
42
96
}
43
97
44
98
fn none_new ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
@@ -55,5 +109,6 @@ pub fn init(context: &PyContext) {
55
109
"__new__" => context. new_rustfunc( none_new) ,
56
110
"__repr__" => context. new_rustfunc( PyNoneRef :: repr) ,
57
111
"__bool__" => context. new_rustfunc( PyNoneRef :: bool ) ,
112
+ "__getattribute__" => context. new_rustfunc( PyNoneRef :: get_attribute)
58
113
} ) ;
59
114
}
0 commit comments