33
44use crate :: {
55 builtins:: {
6- pystr:: IntoPyStrRef , PyBytes , PyDict , PyGenericAlias , PyInt , PyStrRef , PyTupleRef ,
7- PyTypeRef ,
6+ pystr:: IntoPyStrRef , PyBytes , PyDict , PyDictRef , PyGenericAlias , PyInt , PyStrRef ,
7+ PyTupleRef , PyTypeRef ,
88 } ,
99 bytesinner:: ByteInnerNewOptions ,
1010 common:: { hash:: PyHash , str:: to_ascii} ,
@@ -118,8 +118,6 @@ impl PyObject {
118118 setattro ( self , attr_name, attr_value, vm)
119119 }
120120
121- // PyObject *PyObject_GenericGetAttr(PyObject *o, PyObject *name)
122-
123121 pub fn set_attr (
124122 & self ,
125123 attr_name : impl IntoPyStrRef ,
@@ -131,6 +129,109 @@ impl PyObject {
131129 }
132130
133131 // 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+ }
134235
135236 pub fn del_attr ( & self , attr_name : impl IntoPyStrRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
136237 let attr_name = attr_name. into_pystr_ref ( vm) ;
0 commit comments