Skip to content

Commit

Permalink
Use existing fields and methods before calling custom __getattr__
Browse files Browse the repository at this point in the history
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
  • Loading branch information
konstin committed Jun 5, 2019
1 parent c76399e commit 6db9587
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added

* `module` argument to `pyclass` macro. [#499](https://github.com/PyO3/pyo3/pull/499)
* Use existing fields and methods before calling custom __getattr__.


## [0.7.0] - 2018-05-26
Expand Down
37 changes: 31 additions & 6 deletions src/class/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>)
}
}

Expand Down

0 comments on commit 6db9587

Please sign in to comment.