-
Notifications
You must be signed in to change notification settings - Fork 57
Protobuf api V2: add protoMessage, merge and autoboxing wrapperspb #90
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
2adabf6 to
4089d9c
Compare
9e05611 to
1a154d8
Compare
1a154d8 to
fbffa16
Compare
| @@ -1,4 +1,4 @@ | |||
| // Copyright 2020 The Skycfg Authors. | |||
| // Copyright 2021 The Skycfg Authors. | |||
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.
😎
| fields := make(map[string]starlark.Value) | ||
|
|
||
| // Copy any existing set fields | ||
| msgReflect := msg.ProtoReflect() |
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.
nit: Since msg.ProtoReflect() is also used on line 36, could we move this line to be before line 36 so that line 36 can use this variable?
| } | ||
|
|
||
| val, err := valueToStarlark(fieldDesc.Default(), fieldDesc) | ||
| if err != nil { |
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.
OOC - would we want to revert the fields for which we've already set defaults? Or is it okay to leave the proto message in a partially updated state where some fields have defaults and others don't (but could)?
Relatedly, is there value in collecting errors (to return at the end of the method) while continuing to set as many default values as possible?
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.
Are you asking if SetDefaults should behave like first clearing the message like proto.Reset and then setting the defaults for fields that have them? If so this is what I would expect and that's what the stated behavior in the docs seems to apply https://github.com/stripe/skycfg/blob/trunk/docs/modules.asciidoc#proto.set_defaults but let me look into how the current implementation behaves
With errors, I don't think it really matters as when an error is returned the starlark/skycfg evaluation stops and returns the error. Since Skycfg is not mutating any values outside of the evaluation, all these values will just get thrown away anyway even if in a partial state
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.
Alright I tested the existing implementation and it does not behave this way, only setting values with defaults not clearing those without. It calls out to https://github.com/golang/protobuf/blob/master/proto/defaults.go#L25 and here it's filtering out to only fields with HasDefault() which AFAICT is only true for fields with proto2 defaults
This deviates from my expectation/Skycfg's state docs but is also what protobuf is doing as well. That said I would prefer to match the existing behavior as closely as possible to de-risk the new implementation refactor, and then change any behavior separately/explicitly afterwards
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.
Sorry for the confusion!
Are you asking if SetDefaults should behave like first clearing the message like proto.Reset and then setting the defaults for fields that have them?
No, I was asking "when SetDefaults errors, should we clear the errors that were successfully set earlier in SetDefaults?" i.e. Should setting defaults be all-or-nothing or "best effort"?
But based on
Since Skycfg is not mutating any values outside of the evaluation, all these values will just get thrown away anyway even if in a partial state
it sounds like this is all-or-nothing?
That said I would prefer to match the existing behavior as closely as possible to de-risk the new implementation refactor, and then change any behavior separately/explicitly afterwards
👍 SGTM!
| } | ||
|
|
||
| merged, err := mergeField(msg.fields[fieldName], val) | ||
| if err != nil { |
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.
same question here about whether it's safe/expected to leave the proto in a partially-merged state
go/protomodule/type_conversions.go
Outdated
| } | ||
| return nil, fmt.Errorf("ValueError: value %v is not exactly representable as type `int64'.", val) | ||
| } | ||
| case UInt32ValueType: |
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's already a check for this case on line 253 (similarly for case UInt64ValueType on line 315 below).
EDIT: This is the only blocking comment.
Summary
This PR adds the remaining type implementation
protoMessagematches https://github.com/stripe/skycfg/blob/trunk/internal/go/skycfg/proto_message.gomaybeConvertToWrappercopiesskycfg/internal/go/skycfg/proto_message.go
Line 368 in 3410450
mergeis an implementation ofproto.MergeImplementation differences
The main implementation differences here is the new implementation does not store values on the underlying proto.Message, which is only used as a type to later instantiate when using
toProtoMessage. All the values are stored only on thefieldsmap (which is similar to theattrCache) from before but the old implementation attempted to keep bothmsgandattrCachein sync, producing a lot of extra code and some weird edge case bugs.This means that
NewMessagewill not mutate the incoming proto message. This is mostly an internal detail except when passing in global values into skycfg -- the evaluated starlark will no longer be able to mutate incoming global variables. IMO this is a good thing, as it's surprising behavior, and a user can always fetch the updated value withAsProtoMessageExample:
In the old implementation,
msgis directly mutated, sostarlark.Evalcan cause a side effect. This means repeatedly runningevalcan produce different results.In the new implementation,
msgis unchanged, and the user can use the following to extract the changed global if they wishTests
This is all tested on https://github.com/stripe/skycfg/tree/seena/protobuf-api-v2 but am not adding the tests as part of this PR as they involve making skycfg use
*protoMessagein place of the legacy*skyProtoMessage-- this is the switch from old to new implementation, and I thought it would be better to separate that into it's own PR, since again this pr is mostly boilerplate