mozilla / uniffi-rs

a multi-language bindings generator for rust
https://mozilla.github.io/uniffi-rs/
Mozilla Public License 2.0
2.87k stars 232 forks source link

Python: bindings.python.external_packages in uniffi.toml doesn't seem to do anything #2313

Closed tmathern closed 1 week ago

tmathern commented 1 week ago

I am looking to reuse external types from a crate that defines a c2pa namespace (the UDL is here).

In my consuming crate, I have UDL defined like this:

namespace my_new_api {
  string version();
};

[External="c2pa_python"]
typedef extern SigningAlg;

I have a uniffi.toml file at the same level as my consuming's crate Cargo.toml, with this content:

[bindings.python.external_packages]
c2pa-python="c2pa"
c2pa_python="c2pa"
c2pa="c2pa"

I run bindings generation with this command:

cargo run --features=uniffi/cli --bin uniffi-bindgen generate src/my_new_api.udl -n --language python -o python/api/package_name --config uniffi.toml

The generated code has imports like this:

from . import SigningAlg
from . import _UniffiConverterTypeSigningAlg
from . import _UniffiRustBuffer as _UniffiRustBufferSigningAlg

How do I get uniffi to replace the dots . with c2pa? It seems I can't find the uniffi.toml config to do that. (The c2pa package would be installed independently, eg. in a virtualenv, next to the package I'm writing. you can imagine it as peer dependency!).

mhammond commented 1 week ago

I suspect the problem here is that you are generating from the UDL. For external types to work you need to generate from your built .so/.dylib, so all the types from both udl files are seen by the same generation pass.

It sure looks like we need to do a better job at giving sane error messages when external types are used when generating from the UDL.

tmathern commented 1 week ago

Could I ask that you add a way in uniffi to change those from . import? Like a config value somewhere that overrides it?

mhammond commented 1 week ago

The problem is that there's a level of indirection we can't really resolve when building from UDL. [External="c2pa_python"] tells us the crate name of the external type, but not what "namespace" it is in. We do know this when building from the final .so, and indeed, only building from the .so (what we call "library mode") support external types. And if you do generate from the final .so, then our existing config support from uniffi.toml should work.

Is there some reason you can't use library mode here?

tmathern commented 1 week ago

How would I switch to library mode? I need the .so from https://github.com/contentauth/c2pa-python to run that?

mhammond commented 1 week ago

You need the final .so which includes all of your Rust crates and which you intend to actually use. I don't really understand your repos, but IIUC, there is no .so for "c2a-python" because it's a crate consumed by other crates, and hence is never actually built as a .so. Or to put it another way, if you have more than one .so there's some confusion of mine about what you are actually trying to do.

tmathern commented 1 week ago

I'm reusing "c2a-python" as a dependency in another consuming crate. And also the generated "c2a-python" package as (peer) dependency of the Python package generated by my consuming crate.

tmathern commented 1 week ago

(Ignore the bad drawing)

My layout looks like this, with 2 crates in 2 different repos (both mixed Rust/Python):

Screenshot 2024-11-15 at 11 11 35
mhammond commented 1 week ago

I think some of this confusion comes from:

And also the generated "c2a-python" package as (peer) dependency

You should never be generating and sharing Python code generated by "c2a-python". All Python code, including that for "c2a-python" should be generated by your final consuming crate, by passing the .so from that final crate to UniFFI. That final generation will generate 2 .py files in this scenario in a single invocation - one for c2a-python, one for my_new_crate.