Pyo3: Wrapping external rust crate

Created on 26 Nov 2018  路  5Comments  路  Source: PyO3/pyo3

I have an external rust crate, and I would like to build some subset of it as a Python extension, all the while keeping the original crate independent from pyo3 (among other things due to the Rust nightly requirement). Is this currently possible and could you please outline the general approach? Currently, I created a second crate that depends on pyo3 and the original crate that I want to wrap, but I'm not sure what to do next.

For instance, taking the example from https://pyo3.rs/v0.5.2/class.html , but assuming that we have a struct with an implementation in an external crate that we would like to expose in Python. Is it possible to apply the pyclass macro to an external struct? If one needs to write some wrapper, how would it look roughly? Thank you!

Most helpful comment

Is it possible to apply the pyclass macro to an external struct?

It's impossible for 2 reasons.

  1. Simply it's not built for external structs
  2. Rust has orphan rules about trait implementation(impl A for B have to be in the crate where A is defined, or the crate where B is defined).

I think the easiest way is to define a wrapper struct, llike

#[pyclass]
pub struct PyWrapper(ExternalStruct);
#[pymethods]
impl PyWrapper {
    #[new]
    fn __new__(obj: &PyRawObject, /*some python objects you want to pass*/) -> PyResult<()> {
        obj.init(|_token| PyWrapper(ExternalStruct {..})
    }
// write methods you want to use from python side

All 5 comments

Is it possible to apply the pyclass macro to an external struct?

It's impossible for 2 reasons.

  1. Simply it's not built for external structs
  2. Rust has orphan rules about trait implementation(impl A for B have to be in the crate where A is defined, or the crate where B is defined).

I think the easiest way is to define a wrapper struct, llike

#[pyclass]
pub struct PyWrapper(ExternalStruct);
#[pymethods]
impl PyWrapper {
    #[new]
    fn __new__(obj: &PyRawObject, /*some python objects you want to pass*/) -> PyResult<()> {
        obj.init(|_token| PyWrapper(ExternalStruct {..})
    }
// write methods you want to use from python side

Thank you for the explanation! I understand.

I tried the above suggestion, however,

#[pyclass]
pub struct PyWrapper(ExternalStruct);

produces

error: custom attribute panicked
  --> src/lib.rs:31:1
   |
31 | #[pyclass]
   | ^^^^^^^^^^
   |
   = help: message: #[pyclass] can only be used with C-style structs

error: aborting due to previous error

where ExternalStruct is defined in a separate crate as,

pub struct ExternalStruct {
    lowercase: bool,
    token_pattern: String,
    n_features: u32,
}

This happens due to this condition not passing in PyO3 but I'm not sure what could be a way around it?

Sorry, it looks tuple struct is currently unsupported :sweat:
How about

#[pyclass]
pub struct PyWrapper {
    inner: ExternalStruct,
}

?

Yes, that works, thank you @kngwyu !

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fubuloubu picture fubuloubu  路  7Comments

konstin picture konstin  路  5Comments

c410-f3r picture c410-f3r  路  5Comments

thedrow picture thedrow  路  5Comments

jamesray1 picture jamesray1  路  3Comments