Skip to content

Commit

Permalink
Add super object (#2486)
Browse files Browse the repository at this point in the history
  • Loading branch information
ikrivosheev committed Jul 3, 2022
1 parent 17742dc commit 1cd1dbf
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `CompareOp::matches` to easily implement `__richcmp__` as the result of a
Rust `std::cmp::Ordering` comparison. [#2460](https://github.com/PyO3/pyo3/pull/2460)
- Supprt `#[pyo3(name)]` on enum variants [#2457](https://github.com/PyO3/pyo3/pull/2457)
- Add `PySuper` object [#2049](https://github.com/PyO3/pyo3/issues/2049)

### Changed

Expand Down
10 changes: 10 additions & 0 deletions src/types/any.rs
Expand Up @@ -3,6 +3,8 @@ use crate::conversion::{AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyTryF
use crate::err::{PyDowncastError, PyErr, PyResult};
use crate::exceptions::PyTypeError;
use crate::type_object::PyTypeInfo;
#[cfg(not(PyPy))]
use crate::types::PySuper;
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
use crate::{err, ffi, Py, PyNativeType, PyObject, Python};
use std::cell::UnsafeCell;
Expand Down Expand Up @@ -883,6 +885,14 @@ impl PyAny {
pub fn py(&self) -> Python<'_> {
PyNativeType::py(self)
}

/// Return a proxy object that delegates method calls to a parent or sibling class of type.
///
/// This is equivalent to the Python expression `super()`
#[cfg(not(PyPy))]
pub fn py_super(&self) -> PyResult<&PySuper> {
PySuper::new(self.get_type(), self)
}
}

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions src/types/mod.rs
Expand Up @@ -27,6 +27,8 @@ pub use self::mapping::PyMapping;
pub use self::module::PyModule;
pub use self::num::PyLong;
pub use self::num::PyLong as PyInt;
#[cfg(not(PyPy))]
pub use self::pysuper::PySuper;
pub use self::sequence::PySequence;
pub use self::set::PySet;
pub use self::slice::{PySlice, PySliceIndices};
Expand Down Expand Up @@ -277,6 +279,8 @@ mod list;
mod mapping;
mod module;
mod num;
#[cfg(not(PyPy))]
mod pysuper;
mod sequence;
mod set;
mod slice;
Expand Down
61 changes: 61 additions & 0 deletions src/types/pysuper.rs
@@ -0,0 +1,61 @@
use crate::ffi;
use crate::type_object::PyTypeInfo;
use crate::types::PyType;
use crate::{PyAny, PyResult};

/// Represents a Python `super` object.
///
/// This type is immutable.
#[repr(transparent)]
pub struct PySuper(PyAny);

pyobject_native_type_core!(PySuper, ffi::PySuper_Type);

impl PySuper {
/// Constructs a new super object. More read about super object: [docs](https://docs.python.org/3/library/functions.html#super)
///
/// # Examples
///
/// ```rust
/// use pyo3::prelude::*;
///
///#[pyclass(subclass)]
/// struct BaseClass {
/// val1: usize,
/// }
///
/// #[pymethods]
/// impl BaseClass {
/// #[new]
/// fn new() -> Self {
/// BaseClass { val1: 10 }
/// }
///
/// pub fn method(&self) -> usize {
/// self.val1
/// }
/// }
///
/// #[pyclass(extends=BaseClass)]
/// struct SubClass {}
///
/// #[pymethods]
/// impl SubClass {
/// #[new]
/// fn new() -> (Self, BaseClass) {
/// (SubClass {}, BaseClass::new())
/// }
///
/// fn method(self_: &PyCell<Self>) -> PyResult<&PyAny> {
/// let super_ = self_.py_super()?;
/// super_.call_method("method", (), None)
/// }
/// }
/// ```
pub fn new<'py>(ty: &'py PyType, obj: &'py PyAny) -> PyResult<&'py PySuper> {
let py = ty.py();
let super_ = PySuper::type_object(py).call1((ty, obj))?;
let super_ = super_.downcast::<PySuper>()?;
Ok(super_)
}
}
51 changes: 51 additions & 0 deletions tests/test_super.rs
@@ -0,0 +1,51 @@
#![cfg(all(feature = "macros", not(PyPy)))]

use pyo3::prelude::*;

#[pyclass(subclass)]
struct BaseClass {
val1: usize,
}

#[pymethods]
impl BaseClass {
#[new]
fn new() -> Self {
BaseClass { val1: 10 }
}

pub fn method(&self) -> usize {
self.val1
}
}

#[pyclass(extends=BaseClass)]
struct SubClass {}

#[pymethods]
impl SubClass {
#[new]
fn new() -> (Self, BaseClass) {
(SubClass {}, BaseClass::new())
}

fn method(self_: &PyCell<Self>) -> PyResult<&PyAny> {
let super_ = self_.py_super()?;
super_.call_method("method", (), None)
}
}

#[test]
fn test_call_super_method() {
Python::with_gil(|py| {
let cls = py.get_type::<SubClass>();
pyo3::py_run!(
py,
cls,
r#"
obj = cls()
assert obj.method() == 10
"#
)
});
}

0 comments on commit 1cd1dbf

Please sign in to comment.