-
Notifications
You must be signed in to change notification settings - Fork 783
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
enum WIP #1045
enum WIP #1045
Conversation
@stillinbeta many thanks for kicking this off, I'm thrilled to see this! With respect to your comment on the other thread:
If we were to work together to design this functionality, have you got interest in continuing onwards with this PR? If you've lost interest that's also fine, I'll pick this up eventually when I get round to thinking about this feature harder. Reading through what you've started here, I have a few initial thoughts:
|
Thanks!
Yeah, but I don't think both inheritance and |
My initial intent was to inherit from enum, but that blocked on #991. I
agree that modularizing the pyclass macro is probably the way forward,
since we're definitely going to need some of it (`EnumName.EnumVariant` was
the UX I was going for, a la the enum modules, and I think that basically
necessitates a class)
…On Sat, 18 Jul 2020, 09:00 Yuji Kanagawa, ***@***.***> wrote:
Thanks!
I don't think that we'll need either inheritance or being able to have
&mut self receivers in #[pymethods] for these "C-style" enums.
Yeah, but I don't think both inheritance and &mut self don't contribute
the code complexity here.
I think what we need is just to use the common code as pyclass.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724CBE7DAMNMQN7L4N5DR4GME7ANCNFSM4O63GGYA>
.
|
Agreed. Perhaps if I drafted up a solution to #991, we could get that merged which would pave the way for inheriting from I guess we can continue building out the other elements of this PR in the meanwhile!
If you haven't seen it yet, you should check out the |
I took a stab at getting rid of all the PyClass constraint, but it's going to require a slot of boilerplate on its own: We need a Layout, and the only blanket implementation is for PyClass: https://docs.rs/pyo3/0.11.1/pyo3/type_object/trait.PyLayout.html#impl-PyLayout%3CT%3E I managed to get enough of the traits in there to compile (was just missing one, it turns out), but it feels... janky. We don't actually need the Inventory or the ProtoRegistry, but the constraints on PyClass mandate them. Do we think we could loosen some of those constraints, or would that break other things? |
Thanks for continuing to iterate on this! Regarding the layout, I think that we could try adding a new struct like this:
This new type could get a new blanket layout impl? As for the initializer, I think that we can use
I actually think we probably do want to support |
I really don't think we should do so. |
I think what we really need here is to provide a nice API for our Rustic enum. How to implement it does not matter so much. So I recommend implementing it internally just as pyclass. |
I keep wondering if another way to think about enums is if we supported static getters on pyclasses? That would look from the python side very similar esp if we could subclass Enum... |
I'm not sure I agree with this. Do we have examples of Enums in python with methods on them? It seems like usually they're just sigils in Python. |
class DivPipeCoreOperation(enum.Enum):
""" Operation for ``DivPipeCore``.
:attribute UDivRem: unsigned divide/remainder.
:attribute SqrtRem: square-root/remainder.
:attribute RSqrtRem: reciprocal-square-root/remainder.
"""
SqrtRem = 0
UDivRem = 1
RSqrtRem = 2
def __int__(self):
""" Convert to int. """
return self.value
@classmethod
def create_signal(cls, *, src_loc_at=0, **kwargs):
""" Create a signal that can contain a ``DivPipeCoreOperation``. """
return Signal(range(min(map(int, cls)), max(map(int, cls)) + 2),
src_loc_at=(src_loc_at + 1),
decoder=lambda v: str(cls(v)),
**kwargs) |
oh yep, fair enough. In that case, I think PyClass is the way to go. Though I think perhaps initially I'll try to land the change without pymethod support, then we can add it. |
Any luck @stillinbeta ? |
Sorry, I don't have a lot of time during the week to work on this (day job
drains my programming energy). Will give it a shot tomorrow!
…On Fri, 24 Jul 2020 at 12:53, Squirrel ***@***.***> wrote:
Any luck @stillinbeta <https://github.com/stillinbeta> ?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724BCC2Y2TR2A43LXN4DR5G4AFANCNFSM4O63GGYA>
.
|
Don't worry, that’s the order of OSS |
Made some progress this weekend! You can see that you can now return enums from pyfunctions, and they'll be Python-ified properly. However, I ran into issues when trying to pass one back:
Any ideas how to get around this? Should be implementing a different trait, like |
let name = cls.to_string(); | ||
// TODO: this should be | ||
// #cls as pyo3::type_object::PyTypeObject>::type_object(py).compare() | ||
// but I can't figure out how to get py inside extract() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use pyo3::PyNativeType::py(ob)
How does this work without |
Do you mean “does it compile without that part?”
If so, yes, and the appropriate test cases pass
…On Mon, 27 Jul 2020, 03:28 Yuji Kanagawa, ***@***.***> wrote:
How does this work without impl FromPyObject?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724BATAES6E4KG4T4GETR5UUDRANCNFSM4O63GGYA>
.
|
Nice work @stillinbeta ! As an extension I wonder if it would be possible to use enums where the numeric value is not specified? I know on the rust side you could access the https://doc.rust-lang.org/std/mem/fn.discriminant.html but whether that helps at all I'm less certain. |
Then why should we need that impl? |
|
||
impl pyo3::pyclass::PyClassAlloc for #typ {} | ||
|
||
// TODO: handle not in send |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's reasonable to expect #[pyenum]
to always be Send
? It wouldn't ever carry nontrivial data afaik.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe go further and have pyenum always be 'Copy' as we are doing small data here rather than ADTs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, though I'm not sure it's our place to add #[derive(Copy, Clone)]
automatically to the struct. If we don't need it for the #[pyenum]
implementation, I'd rather give users the freedom to choose this themselves.
+1. Like for |
The impl is because I want passing the class in to rust to turn into the
enum variant, not the a type object. I don't think that'll work
automatically
…On Sat, 1 Aug 2020, 04:41 David Hewitt, ***@***.***> wrote:
Then why should we need that impl?
+1. Like for #[pyclass], I think this impl gets derived automatically if
you have #[derive(Clone)].
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724F32SVXBUIATXBO7QDR6PILXANCNFSM4O63GGYA>
.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test_enum_arg
test is confirming that this behaviour works automatically?
(You can get &MyEnum
always, and MyEnum
as long as MyEnum: Clone
)
fn enum_arg(e: MyEnum) { | ||
assert_eq!(MyEnum::OtherVariant, e) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest making this
fn enum_arg(e: MyEnum) { | |
assert_eq!(MyEnum::OtherVariant, e) | |
} | |
fn enum_arg(e: MyEnum) -> String { | |
format!("{}", e) | |
} |
let f = wrap_pyfunction!(enum_arg)(py); | ||
let mynum = py.get_type::<MyEnum>(); | ||
|
||
py_run!(py, f mynum, "f(mynum.Variant)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you take my suggestion from above, we can test both cases:
py_run!(py, f mynum, "f(mynum.Variant)") | |
py_run!(py, f mynum, "assert f(mynum.Variant) == 'Variant'") | |
py_run!(py, f mynum, "assert f(mynum.OtherVariant) == 'OtherVariant'") |
the
There's no way to automatically determine that I want the class Variant to map to MyEnum::Variant, I need to produce that code myself. |
#( | ||
struct #variant_names; | ||
)* | ||
|
||
#( | ||
#variant_cls | ||
)* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this bit is needed - instead you should just be making the variants be instances of the the enum class. (Like how it is with the enum
module.)
Oh, I see what you mean now! I don't think that |
Ah yeah, that makes sense. I misinterpreted how Enum was architected.
…On Sun, 9 Aug 2020 at 17:04, David Hewitt ***@***.***> wrote:
Oh, I see what you mean now! I don't think that MyClass.Variant should be
a separate type? I've marked that code in the comment above.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724CWL5W5LZX2NUNYEMTR74FMDANCNFSM4O63GGYA>
.
|
The one advantage of every variant having its own class is it made mapping the variants to their classes fit better into the model of struct -> class. I think to create instances of Variant, I need to make some use of IntoPython, any ideas what that would look like? I'm a little lost in the type system right now. |
I think FWIW if you rebase on master I just merged some simplifications to the type system, which hopefully will help! |
.collect::<syn::Result<Vec<_>>>()?; | ||
|
||
Ok(quote! { | ||
impl pyo3::FromPy<#enum_> for pyo3::PyObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the rebase on master, you'll need to change this to impl IntoPy<PyObject> for #enum
impl pyo3::IntoPy<pyo3::PyObject> for #enum_ {
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
self.into_py(py)
}
} is definitely not going to do it, that's just recursion. |
Oh heh I see what you're asking 😄 You probably want to use the same implementation as pyo3/pyo3-derive-backend/src/pyclass.rs Line 379 in bcb9077
|
I checked the problem and think each variant does not have to its own types, just as David says. @stillinbeta |
I'm not likely to have time for this until next weekend, feel free to pick
it up if you've got time before then.
…On Sat, 22 Aug 2020, 03:57 Yuji Kanagawa, ***@***.***> wrote:
I checked the problem and think each variant does not have to its own
types, just as David says.
@stillinbeta <https://github.com/stillinbeta>
Thanks, if you cannot afford to complete this PR, please feel free to
leave the rest for maintainers.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724GOFK5Y5X3BTRJTRALSB526BANCNFSM4O63GGYA>
.
|
Given #1065 is merged in, do we want to also try and get this landed so they can be in 0.12 together or does that PR also make it possible to convert simple rust enums to python enums so this PR is now not needed? |
#1065 enables the Python -> Rust direction, but it doesn't really solve the case of data enums like we see here (afaik). I'm now thinking that this new macro |
Still keen on using this and having proper python enums! What's left to do? (and can we help in any way...?) |
Same here, I'm definitely looking forward to this and I'm happy to help. |
I think at this point it's reasonable to assume the original PR author has unfortunately not been able to find the time to finish this off, so I guess they wouldn't mind if anyone is interested in copying this branch and reopening it as a new PR. I think that rebasing it on master, applying the review changes I've made here, and then fixing any subsequent test failures would be a great starting point. I don't think there's particularly far to go for us to merge this! |
Yeah, I've mostly moved onto other projects, sorry for not un-licking this
particular cookie sooner. Go forth with my blessing, and I'll try to answer
questions about my code if they come up.
…On Tue, 10 Nov 2020, 01:46 David Hewitt, ***@***.***> wrote:
I think at this point it's reasonable to assume the original PR author has
unfortunately not been able to find the time to finish this off, so I guess
they wouldn't mind if anyone is interested in copying this branch and
reopening it as a new PR.
I think that rebasing it on master, applying the review changes I've made
here, and then fixing any subsequent test failures would be a great
starting point. I don't think there's particularly far to go for us to
merge this!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1045 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD724CGGUIBTJESOMBJ5U3SPDOSZANCNFSM4O63GGYA>
.
|
This is now superseded by #2002. Thanks again for kicking this off. |
Thank you for contributing to pyo3!
Here are some things you should check for submitting your pull request:
cargo fmt
(This is checked by travis ci)cargo clippy
and check there are no hard errors (There are a bunch of existing warnings; This is also checked by travis)black .
. You can install black withpip install black
)You might want to run
tox
(pip install tox
) locally to check compatibility with all supported python versions. If you're using linux or mac you might find the Makefile helpful for testing.