-
Notifications
You must be signed in to change notification settings - Fork 183
Rewrite t.Struct
to use type annotations
#440
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
Conversation
Makes `__init__` work as you'd expect with positional and keyword args.
this is great. Love it so much. |
I'm planning on doing that later today. It doesn't look like The only real source of bugs that I can see is stuff like |
Switched to your branch in my production. So far looks good. it better stay that way :) |
But this does look good. Going to |
I'm still exploring a few ways to clean up and improve this code to make it more reusable:
|
Should we explore approach |
How about something like this? import typing
class OptionalStruct(t.Struct):
foo: int
bar: typing.Optional[int]
#bar: typing.Optional[int] = t.StructField() # same thing as above
# Both work
OptionalStruct(foo=1, bar=1).serialize()
OptionalStruct(foo=1).serialize()
class ComplexOptionalStruct(t.Struct):
foo: int
status: t.Status
bar: typing.Optional[int] = t.StructField(depends_on=lambda s: s.status == t.Status.Success)
# These work
ComplexOptionalStruct(foo=1, status=t.Status.Success, bar=2)
ComplexOptionalStruct(foo=1, status=t.Status.Failure)
# This doesn't
ComplexOptionalStruct(foo=1, status=t.Status.Success)
# This breaks a lot of unit tests but I don't see a reason to allow structs to be
# created with non-keyword arguments. So this:
nd1 = t.NodeDescriptor(0, 1, 2, 0x0303, 0x04, 0x0505, 0x0606, 0x0707, 0x08)
# Would have to be created like this:
nd2 = t.NodeDescriptor(
byte1=0,
byte2=1,
mac_capability_flags=2,
manufacturer_code=0x0303,
maximum_buffer_size=0x04,
maximum_incoming_transfer_size=0x0505,
server_mask=0x0606,
maximum_outgoing_transfer_size=0x0707,
descriptor_capability_field=0x08
)
# And this would break because structs are immutable
nd2.server_mask = 0xFFFF
# But you could do something like this instead
nd3 = nd2.replace(server_mask=0xFFFF) |
I don't have a strong opinion on this one. In fact I do agree that using keyword arguments would be much more descriptive. Just ATM I think there are a few places where Otherwise this looks great |
I renamed As far as compatibility, it looks like there are at least two places in quirks where the One source of friction so far when fixing the unit tests is that a lot of objects are created "empty" and then a few attributes are set, sometimes as sentinel values. This conflicts with the construction-time checking of arguments (if a struct exists, it's valid and can be serialized) so a bunch of unit tests will have to be adjusted to work with complete, valid objects. Alternatively the creation-time constraints for structs can be relaxed but I feel like this conflicts with their purpose. |
how many tests would need updates? |
in other words I'm happy to merge the yesterday version without last commit as is and build on top of that. Would rather have a few smaller PRs. |
Yeah, that sounds reasonable. I'll split off the more complex version into a separate branch and just fix up the drop-in replacement. |
I was able to slightly tweak the more feature-rich struct class to also be a drop-in replacement for the old one while passing all of the unit tests, including the new struct ones. It should now be possible to incrementally simplify struct subclasses that have optional fields that also depend on the presence/value of other fields (e.g Getting all unit tests to pass with the strict I'm debating whether or not to rename the |
Yeah, this is a good one. I was thinking if |
Let me know when it is ready for review. I glanced through ZCL foundation changes and it looks fine. |
I missed a couple of instances of |
I've rewritten
t.Struct
to behave more like a normal Python class. Before:After:
I had to change a few types around to make things work:
FileImage
was a subclass ofOTAImageHeader
. It isn't anymore and now stores the image header under theheader
attribute.Date._year
has been renamed toDate.years_since_1900
.