Creating instances of python sub classes of a Rust struct from factory methods on the struct
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 16.2k
- Forks
- 1k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 66
Description
The Problem
I have a struct in rust that is intended to be sub classed by python classes, and I need to provide factory class methods to this struct. Currently, there seems to be no direct way to instantiate an instance of a sub class with an already existing instance of the Rust struct.
Minimal Example
use pyo3::prelude::*;
use pyo3::types::PyType;
use rand::random;
#[pyclass(subclass)]
pub struct Spam {
#[pyo3(get)]
eggs: i32,
}
#[pymethods]
impl Spam {
#[classmethod]
fn mk<'py>(cls: Bound<'py, PyType>) -> Self {
// Naive attempt to return Spam, considering it may
// automatically get converted to subtype like `#[new]`
Spam {
eggs: random(),
}
}
}
and in python:
from test_pyo3 import Spam
class Child(Spam):
pass
print(Child.mk()) # <builtins.Spam object at 0x74993d11bf10>
The mk function doesn't automatically convert the returned Spam into a Child instance
Current Workaround
The best I've managed to do is the following:
#[pymethods]
impl Spam {
#[new]
fn new_py() -> Self {
Spam {
eggs: -1,
}
}
#[classmethod]
fn mk<'py>(cls: Bound<'py, PyType>) -> PyResult<Bound<'py, PyAny>> {
let spam = Spam {
eggs: random(),
};
let sub = cls.call0()?;
let mut parent_spam = sub.downcast::<Spam>()?.borrow_mut();
*parent_spam = spam;
Ok(sub)
}
}
print(Child.mk()) # <__main__.Child object at 0x744d73506d50>
However, this is not ideal, since I had to define a new, and in a real scenario if new is non trivial to call/or is costly, this is not feasible.
Suggestion
Would it be possible to add something like this:
#[pymethods]
impl Spam {
#[classmethod]
fn mk<'py>(cls: Bound<'py, PyType>) -> Bound<'py, PyAny> {
cls.from_super(Spam {
eggs: random(),
})
}
}
Note: This still keeps it possible to return the base type if so desired, since it doesn't automatically converting a classmethod's return value to the subtype like #[new]
Implementation
I'd be happy to give implementing this a go, but I'm not sure how hard it might be, and may need some guidance/mentoring!
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by examining PyO3's handling of #[classmethod] and #[pyclass(subclass)], using the minimal Rust/Python example as a reproduction. Compare the existing #[new] subclass behavior with the proposed factory path; done means a classmethod can construct the requested Python subclass from an existing Rust struct without requiring a costly new method, while still allowing the base type to be returned when desired.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100