-
Notifications
You must be signed in to change notification settings - Fork 229
Support for recursive messages #130
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
Support for recursive messages #130
Conversation
|
It looks like this may also fix #63. But, since I wasn't trying to do that, it should get some extra review. |
src/betterproto/__init__.py
Outdated
| if value is PLACEHOLDER: | ||
| continue | ||
| found = True | ||
| parts.extend([field_name, "=", repr(value), ","]) |
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.
There should be a space after this comma, to match PEP8
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 would be better to insert the part using an f-string and join them using ", " to avoid this.
On another note, since this is for constructing the class from the repr you should probably also check if the field is an Enum and then use Enum.__name__.value.name
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'm not sure I follow this:
On another note, since this is for constructing the class from the repr you should probably also check if the field is an Enum and then use Enum.name.value.name
repr isn't generally meant to be round-trippable. For example:
from dataclasses import dataclass
from enum import Enum
class Color(Enum):
RED = 1
BLUE = 2
@dataclass
class Foo(object):
color: Color
repr(Foo(Color.RED)) # => "Foo(color=<Color.RED: 1>)"This implementation of __repr__ gives a similar result to what the implementation provided by @dataclass gives.
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.
According to the python docs (https://docs.python.org/3/reference/datamodel.html#object.__repr__) where possible an object's __repr__ should allow for x = eval(repr(x)) where possible, with the current implementation it would not be possible to do this, so I was suggesting that Enum fields should be converted to there Enum class name and member name (the default for str(Enum)) to allow for this.
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 agree with your interpretation of the docs, but already the built in enum.Enum does not follow this convention. I'm just delegating to each field's existing __repr__ implementation, which is as much as we should do, in my opinion. I don't think Message's implementation of __repr__ should inspect its field's types and change its behavior.
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 just realized that betterproto generates enums that inherit from betterproto.Enum, not enum.Enum. I apologize for my confusion.
Given that, it would be possible to implement betterproto.Enum.__repr__ and get the behavior you're talking about. And it seems like a perfectly good thing to do. It's independent of this PR, though, and would have the same benefit whether it lands before or after this, so I still wouldn't want to tie the two changes together.
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.
also in favor of keeping general improvements out of scope of bugfix PR's, unless they are essential
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 wasn't suggesting altering the betterproto.Enum.__repr__ but altering Message.__repr__ to use enum.Enum.__str__ instead to follow the standard.
|
Sorry, while these PRs have been in review, I discovered that the encoding and decoding latency of betterproto is too high for my use-case, so I need to put my efforts into another solution. Please, feel free to make whatever changes you wish, if you want to incorporate these changes. I enabled direct editing of the PR by contributors, so it should be easy to make changes. Or, if you prefer to close this and take another approach to solving this problem, that works for me. |
|
Hi @chris-chambers, thank you for your effort nevertheless! I am in need of this feature. Is there something that could be done by me before this PR is ready to be merged (@Gobot1234)? |
|
It LGTM. |
|
I can also fix the conflicts at some point soon-ish |
|
I've got time this afternoon, so I'll rebase and fix the merge conflict. So no need, @Gobot1234 |
178c3a7 to
558b04c
Compare
|
just fyi: I forked |
Changes message initialization (`__post_init__`) so that default values are no longer eagerly created to prevent infinite recursion when initializing recursive messages. As a result, `PLACEHOLDER` will be present in the message for any uninitialized fields. So, an implementation of `__get_attribute__` is added that checks for `PLACEHOLDER` and lazily creates and stores default field values. And, because `PLACEHOLDER` values don't compare equal with zero values, a custom implementation of `__eq__` is provided, and the code generation template is updated so that messages generate with `@dataclass(eq=False)`.
- Uses `is not` to compare types in `Message.__eq__` - Adds a space after each comma in `Message.__repr__`
Co-authored-by: James <[email protected]>
Co-authored-by: James <[email protected]>
- Add more test cases using the standard test pattern - Add sorted_field_names to message subclass metadata to support stable ordering of keys in repr. - Tweak code in new message methods
9e8fbbc to
2af6f63
Compare
|
Thanks to @chris-chambers et al. for the good work on this. Sorry for the slow turn around. I pushed some final changes:
Ready to merge I'd say 👍 |
* Implement Message.__bool__ for #130 * Add __bool__ to special members * Tweak __bool__ docstring * remove compiler: prefix Co-authored-by: nat <[email protected]>
Changes message initialization (
__post_init__) so that default values are no longer eagerly created to prevent infinite recursion when initializing recursive messages.As a result,
PLACEHOLDERwill be present in the message for any uninitialized fields. So, an implementation of__get_attribute__is added that checks forPLACEHOLDERand lazily creates and stores default field values.And, because
PLACEHOLDERvalues don't compare equal with zero values, a custom implementation of__eq__is provided, and the code generation template is updated so that messages generate with@dataclass(eq=False, repr=False).Fixes #13 and fixes #74.
This needs careful review! I'm not sure if this is the desired approach. It's just the least-friction way I could think of.