3
3
4
4
use crate :: {
5
5
builtins:: {
6
- pystr:: IntoPyStrRef , PyBytes , PyDict , PyGenericAlias , PyInt , PyStrRef , PyTupleRef ,
7
- PyTypeRef ,
6
+ pystr:: IntoPyStrRef , PyBytes , PyDict , PyDictRef , PyGenericAlias , PyInt , PyStrRef ,
7
+ PyTupleRef , PyTypeRef ,
8
8
} ,
9
9
bytesinner:: ByteInnerNewOptions ,
10
10
common:: { hash:: PyHash , str:: to_ascii} ,
@@ -118,8 +118,6 @@ impl PyObject {
118
118
setattro ( self , attr_name, attr_value, vm)
119
119
}
120
120
121
- // PyObject *PyObject_GenericGetAttr(PyObject *o, PyObject *name)
122
-
123
121
pub fn set_attr (
124
122
& self ,
125
123
attr_name : impl IntoPyStrRef ,
@@ -131,6 +129,109 @@ impl PyObject {
131
129
}
132
130
133
131
// int PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value)
132
+ #[ cfg_attr( feature = "flame-it" , flame) ]
133
+ pub fn generic_setattr (
134
+ & self ,
135
+ attr_name : PyStrRef ,
136
+ value : Option < PyObjectRef > ,
137
+ vm : & VirtualMachine ,
138
+ ) -> PyResult < ( ) > {
139
+ vm_trace ! ( "object.__setattr__({:?}, {}, {:?})" , obj, attr_name, value) ;
140
+
141
+ if let Some ( attr) = self . get_class_attr ( attr_name. as_str ( ) ) {
142
+ let descr_set = attr. class ( ) . mro_find_map ( |cls| cls. slots . descr_set . load ( ) ) ;
143
+ if let Some ( descriptor) = descr_set {
144
+ return descriptor ( attr, self . to_owned ( ) , value, vm) ;
145
+ }
146
+ }
147
+
148
+ if let Some ( dict) = self . dict ( ) {
149
+ if let Some ( value) = value {
150
+ dict. set_item ( attr_name, value, vm) ?;
151
+ } else {
152
+ dict. del_item ( attr_name. clone ( ) , vm) . map_err ( |e| {
153
+ if e. fast_isinstance ( & vm. ctx . exceptions . key_error ) {
154
+ vm. new_attribute_error ( format ! (
155
+ "'{}' object has no attribute '{}'" ,
156
+ self . class( ) . name( ) ,
157
+ attr_name,
158
+ ) )
159
+ } else {
160
+ e
161
+ }
162
+ } ) ?;
163
+ }
164
+ Ok ( ( ) )
165
+ } else {
166
+ Err ( vm. new_attribute_error ( format ! (
167
+ "'{}' object has no attribute '{}'" ,
168
+ self . class( ) . name( ) ,
169
+ attr_name,
170
+ ) ) )
171
+ }
172
+ }
173
+
174
+ pub fn generic_getattr ( & self , name : PyStrRef , vm : & VirtualMachine ) -> PyResult {
175
+ self . generic_getattr_opt ( name. clone ( ) , None , vm) ?
176
+ . ok_or_else ( || vm. new_attribute_error ( format ! ( "{} has no attribute '{}'" , self , name) ) )
177
+ }
178
+
179
+ /// CPython _PyObject_GenericGetAttrWithDict
180
+ pub fn generic_getattr_opt (
181
+ & self ,
182
+ name_str : PyStrRef ,
183
+ dict : Option < PyDictRef > ,
184
+ vm : & VirtualMachine ,
185
+ ) -> PyResult < Option < PyObjectRef > > {
186
+ let name = name_str. as_str ( ) ;
187
+ let obj_cls = self . class ( ) ;
188
+ let cls_attr = match obj_cls. get_attr ( name) {
189
+ Some ( descr) => {
190
+ let descr_cls = descr. class ( ) ;
191
+ let descr_get = descr_cls. mro_find_map ( |cls| cls. slots . descr_get . load ( ) ) ;
192
+ if let Some ( descr_get) = descr_get {
193
+ if descr_cls
194
+ . mro_find_map ( |cls| cls. slots . descr_set . load ( ) )
195
+ . is_some ( )
196
+ {
197
+ drop ( descr_cls) ;
198
+ let cls = obj_cls. into_owned ( ) . into ( ) ;
199
+ return descr_get ( descr, Some ( self . to_pyobject ( vm) ) , Some ( cls) , vm)
200
+ . map ( Some ) ;
201
+ }
202
+ }
203
+ drop ( descr_cls) ;
204
+ Some ( ( descr, descr_get) )
205
+ }
206
+ None => None ,
207
+ } ;
208
+
209
+ let dict = dict. or_else ( || self . dict ( ) ) ;
210
+
211
+ let attr = if let Some ( dict) = dict {
212
+ dict. get_item_opt ( name, vm) ?
213
+ } else {
214
+ None
215
+ } ;
216
+
217
+ if let Some ( obj_attr) = attr {
218
+ Ok ( Some ( obj_attr) )
219
+ } else if let Some ( ( attr, descr_get) ) = cls_attr {
220
+ match descr_get {
221
+ Some ( descr_get) => {
222
+ let cls = obj_cls. into_owned ( ) . into ( ) ;
223
+ descr_get ( attr, Some ( self . to_pyobject ( vm) ) , Some ( cls) , vm) . map ( Some )
224
+ }
225
+ None => Ok ( Some ( attr) ) ,
226
+ }
227
+ } else if let Some ( getter) = obj_cls. get_attr ( "__getattr__" ) {
228
+ drop ( obj_cls) ;
229
+ vm. invoke ( & getter, ( self . to_pyobject ( vm) , name_str) )
230
+ . map ( Some )
231
+ } else {
232
+ Ok ( None )
233
+ }
234
+ }
134
235
135
236
pub fn del_attr ( & self , attr_name : impl IntoPyStrRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
136
237
let attr_name = attr_name. into_pystr_ref ( vm) ;
0 commit comments