Use existing fields and methods before calling custom __getattr__

Previously, defining `__getattr__` would override all existing fields and methods. This changes it to behave like a `__getattr__` method defined in python, i.e. the custom method is only called if there isn't a field or method of that name
This commit is contained in:
konstin 2019-06-05 11:32:46 +02:00 committed by kngwyu
parent 5d85ea7fdc
commit 7a83cb6afa
2 changed files with 32 additions and 6 deletions

View File

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* `module` argument to `pyclass` macro. [#499](https://github.com/PyO3/pyo3/pull/499)
* `py_run!` macro [#512](https://github.com/PyO3/pyo3/pull/512)
* Use existing fields and methods before calling custom __getattr__. [#505](https://github.com/PyO3/pyo3/pull/512)
### Fixed

View File

@ -207,12 +207,37 @@ where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
fn tp_getattro() -> Option<ffi::binaryfunc> {
py_binary_func!(
PyObjectGetAttrProtocol,
T::__getattr__,
T::Success,
PyObjectCallbackConverter
)
#[allow(unused_mut)]
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
let _pool = crate::GILPool::new();
let py = Python::assume_gil_acquired();
// Behave like python's __getattr__ (as opposed to __getattribute__) and check
// for existing fields and methods first
let existing = ffi::PyObject_GenericGetAttr(slf, arg);
if existing == std::ptr::null_mut() {
// PyObject_HasAttr also tries to get an object and clears the error if it fails
ffi::PyErr_Clear();
} else {
return existing;
}
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let arg = py.from_borrowed_ptr::<crate::types::PyAny>(arg);
let result = match arg.extract() {
Ok(arg) => slf.__getattr__(arg).into(),
Err(e) => Err(e.into()),
};
crate::callback::cb_convert(PyObjectCallbackConverter, py, result)
}
Some(wrap::<T>)
}
}