woodruffw / pyrage

Python bindings for rage (age in Rust)
https://pypi.org/project/pyrage/
MIT License
53 stars 7 forks source link

Support age plugins #55

Closed vikanezrimaya closed 7 months ago

vikanezrimaya commented 9 months ago

Spec: https://c2sp.org/age-plugin

The rage CLI implements plugins, so why shouldn't this tool?

Documentation for rage's API for plugin usage: https://docs.rs/age/0.9.2/age/plugin/index.html

I may or may not want to try my hand at implementing this.

woodruffw commented 9 months ago

Please do! I'd be happy to review API bindings for plugins.

(If you have outstanding thoughts before beginning, I'm happy to discuss them as well. But I think this should be pretty straightforward, like the "baked in" identity types are.)

vikanezrimaya commented 9 months ago

PluginIdentityV1 and PluginRecipientV1 are not Clone, unlike normal Identity and Recipient structs. This seems to pose a problem with impl<'source> FromPyObject for Box<dyn Pyrage{Identity, Recipient}>, which effectively acts as a Pyrage{Identity, Recipient}: FromPyObject trait bound on anything that implements Pyrage{Identity, Recipient}, and FromPyObject requires Clone to be implemented for its default implementation on T where T: Clone to work.

Should I just put all plugins behind an Rc/Arc to bypass this? Python already uses reference counting everywhere, so it doesn't seem like a bad idea. Or maybe I should implement FromPyObject manually? But then PyrageRecipient needs to be implemented for references, and that introduces a can of worms called "lifetimes".

error[E0277]: the trait bound `plugin::IdentityPluginV1: Clone` is not satisfied
   --> src/lib.rs:124:35
    |
124 |         } else if let Ok(identity) = ob.extract::<plugin::IdentityPluginV1>() {
    |                                         ^^^^^^^ the trait `Clone` is not implemented for `plugin::IdentityPluginV1`
    |
    = help: the following other types implement trait `pyo3::FromPyObject<'source>`:
              <bool as pyo3::FromPyObject<'source>>
              <char as pyo3::FromPyObject<'_>>
              <isize as pyo3::FromPyObject<'source>>
              <i8 as pyo3::FromPyObject<'source>>
              <i16 as pyo3::FromPyObject<'source>>
              <i32 as pyo3::FromPyObject<'source>>
              <i64 as pyo3::FromPyObject<'source>>
              <i128 as pyo3::FromPyObject<'source>>
            and 161 others
    = note: required for `plugin::IdentityPluginV1` to implement `pyo3::FromPyObject<'_>`
note: required by a bound in `pyo3::PyAny::extract`
   --> /home/vika/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pyo3-0.20.2/src/types/any.rs:951:12
    |
949 |     pub fn extract<'a, D>(&'a self) -> PyResult<D>
    |            ------- required by a bound in this associated function
950 |     where
951 |         D: FromPyObject<'a>,
    |            ^^^^^^^^^^^^^^^^ required by this bound in `PyAny::extract`
vikanezrimaya commented 9 months ago

Wrapping into an Arc solved that problem. Maybe later it could be optimized, if needed. I personally think it's ok.