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

feat: support pyclass on tuple enums #4072

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open

Conversation

newcomertv
Copy link

@newcomertv newcomertv commented Apr 12, 2024

#3748
Based on @mkovaxx 's https://github.com/PyO3/pyo3/pull/3582/files

Adds support for #[pyclass] for enums containing Tuple Variants.

Supports:

  • Pattern matching
  • Accessing by field ._id
  • Accessing by array [id]
  • Len (elements in the tuple)

This introduces an issue that I didn't know how to solve quickly: Since the fields have no identifiers I need to create them inside the derive macro. Since they need to outlive their context where they are created as FnArg requires a &'a Ident I ended up creating a Box::leak based identifier.

I'm not sure how bad this is in the first place since it is only during the build process (code gen) and extremely small.

Addressing the getitem issue the comments on #4135 were very helpful.
Thanks to @carderne for fixing the doc issues, trying to solve the getitem problem and seeing the value in this PR :) - I had a few busy weeks and didn't get to continue working on this PR until now.

I hope I didn't miss any further documentation requirements or am missing any testcases.

Blocking issues:

  • @birkenfield suggested refactoring FnArg to take a Cow instead. I don't think this should be part of this PR.
    If we need this before this PR gets merged I can open a seperate PR. (Though I'm not keen on it). A tracking issue has been opened within Replace &'a Ident with Cow in FnArg Enum #4156
  • @Icxolu I'm not sure if __match_args__ needs to be generated as we already support matching through the subclass generation.

guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pytests/src/enums.rs Show resolved Hide resolved
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for following on this. Don't worry about being busy, happens to all of us, sometimes there are more important things.

This is already looking very good to me. I left a few comments to simplify the code a bit. Regarding using Cow in FnArg, that was not my suggestion, but I agree with that. While leaking would probably be fine, I think using Cow would be the cleaner solution. If you would like open a separate PR for that, that would be fine with me.

Also just giving @carderne a ping, such that we don't duplicate work.

pyo3-macros-backend/src/pymethod.rs Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pytests/tests/test_enums.py Outdated Show resolved Hide resolved
pytests/tests/test_enums_match.py Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much 🙏 . I think we're getting really close now 🚀.

The slot and __match_args__ handling looks great.

I marked a few spots, where we can reduce some code and some (optional) style nits.
There are two pieces that I would like to address before merge.

For the documentation in the guide I think we should encourage using indexing and tuple match for these tuple variants. This should be a pretty minor change just adapting the examples.

The second point is to also support constructor customization here, which we just landed for struct variants in #4158. This should also be a pretty simple change and I added a note in the corresponding place.

(I hope I'm not putting too much additional work on your table 🙃 )

Comment on lines 976 to 990
#pytypeinfo

#pyclass_impls
#pyclass_impls

#[doc(hidden)]
#[allow(non_snake_case)]
impl #cls {}
#[doc(hidden)]
#[allow(non_snake_case)]
impl #cls {}

#(#variant_cls_zsts)*
#(#variant_cls_zsts)*

#(#variant_cls_pytypeinfos)*
#(#variant_cls_pytypeinfos)*

#(#variant_cls_pyclass_impls)*
#(#variant_cls_pyclass_impls)*

#(#variant_cls_impls)*
#(#variant_cls_impls)*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I do prefer the indented version here. (But not a blocker)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like so?

Suggested change
#pytypeinfo
#pyclass_impls
#pyclass_impls
#[doc(hidden)]
#[allow(non_snake_case)]
impl #cls {}
#[doc(hidden)]
#[allow(non_snake_case)]
impl #cls {}
#(#variant_cls_zsts)*
#(#variant_cls_zsts)*
#(#variant_cls_pytypeinfos)*
#(#variant_cls_pytypeinfos)*
#(#variant_cls_pyclass_impls)*
#(#variant_cls_pyclass_impls)*
#(#variant_cls_impls)*
#(#variant_cls_impls)*
#pytypeinfo
#pyclass_impls
#[doc(hidden)]
#[allow(non_snake_case)]
impl #cls {}
#(#variant_cls_zsts)*
#(#variant_cls_pytypeinfos)*
#(#variant_cls_pyclass_impls)*
#(#variant_cls_impls)*

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but with #pytypeinfo also indented to the same level. I think that makes it clearer that the whole block is part of the same statement.

pyo3-macros-backend/src/pyclass.rs Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pyo3-macros-backend/src/pyclass.rs Outdated Show resolved Hide resolved
pytests/tests/test_enums_match.py Outdated Show resolved Hide resolved
guide/src/class.md Outdated Show resolved Hide resolved
Comment on lines 94 to 98
#[pyclass]
pub enum TupleEnum {
Full(i32, f64, bool),
EmptyTuple(),
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I like the ergonomics of explicitly initializing a tuple based on arbitrary identifiers.
The alternative would be to simply define the tuple.

Haven't looked into implementation details, seems doable though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you are referring to this?

#[pyo3(constructor = (_0 = 1, _1 = 1.0, _2 = true))]

Yeah, it's not ideal, but I think it's better the nothing and we improve on it in the future if necessary.

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 this pull request may close these issues.

None yet

4 participants