PyO3 / PyO3/pyo3

[Question] Return different sub-classes from one function?

Open
#1,637 12 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

1.0-candidate confusing-api needs-design
Dominant language
Rust
Stars
16.2k
Forks
1k
Avg merge
2d 6h
Merged PRs (30d)
66

Description

Is it possible to return different sub-classes from a function?

In Rust I have an enum containing data

pub enum Frame {
    Type,
    TypeResponse(u8, Version, u8),
    Ping(u8),
    Pong(u8),
}

In Python I though I would expose this by having a Frame base class and a sub-class for each type of frame:

#[pyclass(name="Frame", subclass)]
pub struct PyFrame { /* some fields */ }

#[pyclass(extends=PyFrame, subclass)]
pub struct PingFrame {
    #[pyo3(get)]
    sequence: u8,
}

#[pymethods]
impl PingFrame {
    #[new]
    pub fn new(sequence: u8) -> (Self, PyFrame) {
        let frame = Frame::new_ping(sequence);
        ((&frame).try_into().unwrap(), frame.into())
    }
}

impl TryFrom<&Frame<'_>> for PingFrame {
    type Error = ();
    
    fn try_from(frame: &Frame) -> Result<Self, Self::Error> {
        match frame.payload() {
            Frame::Ping(sequence) => Ok(PingFrame { sequence: *sequence }),
            _ => Err(())
        }
    }
}

Now I'm trying to have a function that can return any of the sub-classes depending on the frame enum variant. Is that possible?

fn parse(&mut self, py: Python, byte: u8) -> PyResult<Option<PyObject>> {
        match self.parser.parse(byte) {
            Ok(f) => Ok(Some(PingFrame::new(5).into_py(py))),
            Err(e) => return Err(ParseError { err: e }.into())
        }
    }

I tried to use PyFrame and PyObject in the return type, but that doesn't seem to work.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names no repository files or tests. Start with the parse function, PyFrame and PingFrame definitions, and PingFrame::new; determine whether the requested variant-dependent return can be represented through the PyO3 API, with completion defined by a documented answer or minimal working example.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
api
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.