Derive macro for IntoPyObject#4495
Conversation
ffc4c81 to
68e57f4
Compare
davidhewitt
left a comment
There was a problem hiding this comment.
This looks brilliant, thank you so much! I am travelling with family for the next few days so it might be the weekend before I have a chance to do any reviewing... 🙏
|
No worries, enjoy your trip. |
davidhewitt
left a comment
There was a problem hiding this comment.
Overall this looks spot on, thanks very much. I have given this initial review from my phone so haven't read the implementation in full detail, looks great in general.
I have a bunch of various suggestions, mostly related to interactions with FromPyObject.
| body: TokenStream, | ||
| } | ||
|
|
||
| struct NamedStructField<'a> { |
There was a problem hiding this comment.
We should consider the interaction with FromPyObject's options like #[pyo3(item)] and #[pyo3(attribute)]. I am fine if we just parse them and reject cases where we don't want to support them for IntoPyObject (yet); all I'd like to avoid is that we don't silently ignore those options.
If there is an unsupported case then users can split their FromPyObject and IntoPyObject derives onto different structs, and they are then aware their conversions will not round-trip.
There was a problem hiding this comment.
Hmm, we could definitely parse them, but I'm not sure what we would do with them in this case. Due to the PyDict return type this always uses set_item. So I think it makes only sense to parse them, if there is a way (I'm not aware of any) to create an anonymous object and set arbitrary attributes on it. I guess we could accept item to allow renaming and to make a round-trip possible if it is given.
There was a problem hiding this comment.
Good point. Let's handle item round trips here and error on attribute? Users can always make two separate types if they need #[attribute] for their FromPyObject implementation for some reason.
There was a problem hiding this comment.
I added parsing for #[pyo3(item)] and #[pyo3(attribute)] (like for FromPyObject), roundtrip tests in test_frompy_intopy_roundtrip.rs and ui tests for the unsupported cases.
davidhewitt
left a comment
There was a problem hiding this comment.
Sorry for the long review cycle, this is looking great and I think we're nearly there! Main change I would like to see is just on the interaction with the FromPyObject attributes, where I think it makes our future development simpler if we handle that before releasing.
| Crate(CrateAttribute), | ||
| } | ||
|
|
||
| impl Parse for ContainerPyO3Attribute { |
There was a problem hiding this comment.
Reading all these structures it feels like there's a ton of boilerplate which might be interesting to refactor into common macro_rules! patterns (maybe). Not for this PR, but maybe a non-urgent follow up to explore sometime.
| _ => bail_spanned!( | ||
| fields.span() => "cannot derive `IntoPyObject` for empty structs" | ||
| ), |
There was a problem hiding this comment.
I wonder, does it make sense for it to be empty tuple and/or empty dict? Perhaps again we leave this for a follow up and consider FromPyObject at the same time.
There was a problem hiding this comment.
Yeah, I think revisiting this in a followup (perhaps after 0.23) makes sense.
| body: TokenStream, | ||
| } | ||
|
|
||
| struct NamedStructField<'a> { |
There was a problem hiding this comment.
Good point. Let's handle item round trips here and error on attribute? Users can always make two separate types if they need #[attribute] for their FromPyObject implementation for some reason.
|
No worries, thanks for the comments. I'll have a look in a few days. |
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2ed88ed to
49d9ce3
Compare
49d9ce3 to
748bad3
Compare
davidhewitt
left a comment
There was a problem hiding this comment.
This looks perfect, thanks so much! I'm really optimistic this will help users have a good experience upgrading to 0.23! 🚀
Initial implementation of
#[derive(IntoPyObject)].#[derive(FromPyObject)]was used as a base/reference for this implementation.This currently supports
#[pyo3(transparent)]as the only real option, which just forwards to the innerIntoPyObjectimpl.#[pyo3(transparent)]named and single tuple): forward impl.PyDictwith the field names as keysPyTuplein declaration order'pyis treated special and is used a thePython<'py>lifetime if given.We still need lots of tests here, but I thought I'll put this up as a POC already. Hopefully this can smoothen the migration to
IntoPyObjecta bit.One thing not needed initially, but may be a nice to have at some point is some like
#[pyo3(into_py_with = "...")]analogous to#[pyo3(from_py_with = "...")]Closes #4458