m-ou-se / inline-python

Inline Python code directly in your Rust code
https://docs.rs/inline-python
BSD 2-Clause "Simplified" License
1.16k stars 40 forks source link

Is There a Way to Use `pyo3::PyObject`s in the `python!` Macro? #11

Closed zicklag closed 5 years ago

zicklag commented 5 years ago

I am writing a Blender plugin in Rust using PyO3 to create a native Python extension, but I have the need to frequently access the Blender Python API from inside of the Rust extension. This results in a lot of rather verbose code such as:

let mut translation = [0f32; 3];
for i in 0..3 {
    translation[i] = object
        .getattr(py, "location")?
        .call_method1(py, "__getitem__", PyTuple::new(py, &[i]))?
        .extract(py)?;
}

Could inline_python help me write this more like this?

let mut translation = [0f32; 3];

for i in 0..3 {
    python! {
        'translation['i] = 'object.location['i]
    }
}

The problem I am having is that I cannot pass pyo3::PyObject's into the python! macro because, as the compiler puts it, the trait `inline_python::pyo3::ToPyObject` is not implemented for `pyo3::object::PyObject. This is the case with the object object in the above code samples.

Is there any way to get around this?

Edit: I just realized that you can't make assignments to Rust variables in the Python block, so that might kind of defeat the use-case because the code would end up being just as verbose as before if I had to copy the globals out of the context every time I needed to get values out of the Python block.

de-vri-es commented 5 years ago

ToPyObject is implemented for pyo3::PyObject: https://docs.rs/pyo3/0.6.0/src/pyo3/object.rs.html#268-273.

What you're seeing probably means that your crate is using a different pyo3 version than inline-python. That's why inline-python publicly re-exports pyo3. You can use that to be sure the versions match. If you add use inline_python::pyo3, that particular error should go away.

As for getting values back out, that might be easy, depending on what you're doing. Getting objects that are pyo3::FromPyObject is pretty easy using Context::get_global. That includes primitive types, strings, vectors, options, small tuples and a lot of pyo3 types out of the box.

If you can think of other helper functions/traits/objects to make it easier to send objects back-and-forth between Rust and Python, we're all ears :)

de-vri-es commented 5 years ago

I just pushed a new release with less strict version requirements for pyo3. That should reduce the likelyhood of running into this, and it allows to use pyo3 0.7.

I don't have permission to publish it on crates.io though. Once I sorted that out, I'll also publish the new release there.

Update: inline-python 0.3.1 is published on crates.io

zicklag commented 5 years ago

That's great thanks! I might end up doing some experimentation with the ergonomics of the Python<->Rust interaction and I'll let you know if I come up with anything.