|
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, |
@@ -173,6 +171,68 @@ impl PyObject { |
173 | 171 | } |
174 | 172 | } |
175 | 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 | + } |
| 235 | + |
176 | 236 | pub fn del_attr(&self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult<()> { |
177 | 237 | let attr_name = attr_name.into_pystr_ref(vm); |
178 | 238 | self.call_set_attr(vm, attr_name, None) |
|
0 commit comments