-
Notifications
You must be signed in to change notification settings - Fork 219
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
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 <50501825+Gobot1234@users.noreply.github.com>
Co-authored-by: James <50501825+Gobot1234@users.noreply.github.com>
- 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 <n@natn.me>
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 forPLACEHOLDER
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, 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.