-
Hi PyO3 community, First of all, huge thanks to everyone that makes PyO3 happen. In our codebase we have a Rust library that we'd like to provide Python APIs for other internal users to use. A simplified version of the public interface of the library is something like this: pub struct EventA {
pub field_a1: i64,
pub field_a2: bool,
}
pub struct EventB {
pub field_b1: f64,
pub field_b2: i32,
}
pub enum Event {
EventA(EventA),
EventB(EventB),
}
pub trait Client {
fn handle_event(&mut self, event: &Event);
}
pub struct Driver {
clients: Vec<Box<dyn Client>>,
}
impl Driver {
pub fn new(clients: Vec<Box<dyn Client>>) -> Self {
Self { clients }
}
fn emit_event(&mut self, event: Event) {
for client in self.clients.iter_mut() {
client.handle_event(&event)
}
}
pub fn emit_event_a(&mut self, field_a1: i64, field_a2: bool) {
// complicated things happen in here in real codebase
let event = Event::EventA(EventA { field_a1, field_a2 });
self.emit_event(event)
}
pub fn emit_event_b(&mut self, field_b1: f64, field_b2: i32) {
// complicated things happen in here in real codebase
let event = Event::EventB(EventB { field_b1, field_b2 });
self.emit_event(event)
} The way a user uses this library is, the user will have its own type that implements I'd like to provide a pythonic interface for this library (so from abc import Abc, abstractmethod
from dataclasses import dataclass
from typing import List
class Event(Abc):
pass
@dataclass
class EventA(Event):
field_a1: int
field_a2: bool
@dataclass
class EventB(Event):
field_b1: float
field_b2: int
class Client(Abc):
@abstractmethod
def handle_event(self, event: Event):
pass
class Driver:
def __init__(self, clients: List[Client]):
pass # should construct a Rust driver
def emit_event_a(self, field_a1: int, field_a2: bool):
pass # should use the Rust driver
def emit_event_b(self, field_b1: float, field_b2: int):
pass # should use the Rust driver Then a user would construct its own client and use the library this way: class MyClient(Client):
def handle_event(self, event: Event):
match event:
case EventA(field_a1=a1, field_a2=a2): print(f"got A(a1={a1}, a2={a2})")
case EventB(field_b1=b1, field_b2=b2): print(f"got B(b1={b1}, b2={b2})")
driver = Driver([MyClient()])
driver.emit_event_a(100, False)
driver.emit_event_b(3.4, 20) I have been reading PyO3 docs and playing with it for a few days now. I have two main questions:
I've tried something like this, first create a dummy base class: #[pyclass(name="Event", subclass)]
struct PyEvent {} Then I can add I thought I could then implement I can see a compromising way is to define Event = typing.Union[EventA, EventB] in Python instead of using base class. But I wonder whether there is some way to make the desired class hierarchy in Python side work?
Constraints:
I'll really appreciate if my question gets answered. A repo with my sample code is in here: https://github.com/cdiwoxv/pyo3-question-2535 |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
All types in PyO3 implement |
Beta Was this translation helpful? Give feedback.
All types in PyO3 implement
IntoPy
, as does a#[pyclass]
which doesn't useextends
, see also #1836