Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrapping external rust crate #287

Closed
rth opened this issue Nov 26, 2018 · 8 comments · Fixed by #1504
Closed

Wrapping external rust crate #287

rth opened this issue Nov 26, 2018 · 8 comments · Fixed by #1504

Comments

@rth
Copy link

rth commented Nov 26, 2018

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!

@kngwyu
Copy link
Member

kngwyu commented Nov 26, 2018

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

@rth
Copy link
Author

rth commented Nov 26, 2018

Thank you for the explanation! I understand.

@rth
Copy link
Author

rth commented Dec 24, 2018

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?

@kngwyu
Copy link
Member

kngwyu commented Dec 25, 2018

Sorry, it looks tuple struct is currently unsupported 😓
How about

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

?

@rth
Copy link
Author

rth commented Dec 30, 2018

Yes, that works, thank you @kngwyu !

@messense
Copy link
Member

#[pyclass]
pub struct PyWrapper(ExternalStruct);

@kngwyu Is there any reason why tuple struct cannot be supported? Is it possible to add support for it?

@davidhewitt
Copy link
Member

davidhewitt commented Mar 17, 2021

From a technical level, I think it should be possible to support. Design-wise I think it makes sense to support it; the prevailing direction of PyO3 is (imo) write natural Rust code when writing Python bindings.

I think the first step would be to add support in the parsing code here:

if let syn::Fields::Named(fields) = &mut class.fields {

... and then add a bunch of tests and see if anything else needs adjusting to support tuple structs of varying length / with #[pyo3(get, set)] etc.

@kngwyu
Copy link
Member

kngwyu commented Mar 18, 2021

I don't see any specific reason now, but need to check it more precisely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants