Skip to content

Commit aa4aa61

Browse files
ohmayrparthea
andauthored
fix: construct messages with nested struct (#479)
* fix: construct messages with nested struct * fix lint issues * some cleanup * remove code for handling underscore keys * update commit message * fix style --------- Co-authored-by: Anthonios Partheniou <[email protected]>
1 parent 3476348 commit aa4aa61

File tree

3 files changed

+31
-33
lines changed

3 files changed

+31
-33
lines changed

proto/marshal/rules/message.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ def to_proto(self, value):
3434
try:
3535
# Try the fast path first.
3636
return self._descriptor(**value)
37-
except TypeError as ex:
38-
# If we have a type error,
37+
except (TypeError, ValueError) as ex:
38+
# If we have a TypeError or Valueerror,
3939
# try the slow path in case the error
40-
# was an int64/string issue
40+
# was:
41+
# - an int64/string issue.
42+
# - a missing key issue in case a key only exists with a `_` suffix.
43+
# See related issue: https://github.com/googleapis/python-api-core/issues/227.
44+
# - a missing key issue due to nested struct. See: b/321905145.
4145
return self._wrapper(value)._pb
4246
return value
4347

proto/message.py

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -725,36 +725,7 @@ def __init__(
725725
"Unknown field for {}: {}".format(self.__class__.__name__, key)
726726
)
727727

728-
try:
729-
pb_value = marshal.to_proto(pb_type, value)
730-
except ValueError:
731-
# Underscores may be appended to field names
732-
# that collide with python or proto-plus keywords.
733-
# In case a key only exists with a `_` suffix, coerce the key
734-
# to include the `_` suffix. It's not possible to
735-
# natively define the same field with a trailing underscore in protobuf.
736-
# See related issue
737-
# https://github.com/googleapis/python-api-core/issues/227
738-
if isinstance(value, dict):
739-
if _upb:
740-
# In UPB, pb_type is MessageMeta which doesn't expose attrs like it used to in Python/CPP.
741-
keys_to_update = [
742-
item
743-
for item in value
744-
if item not in pb_type.DESCRIPTOR.fields_by_name
745-
and f"{item}_" in pb_type.DESCRIPTOR.fields_by_name
746-
]
747-
else:
748-
keys_to_update = [
749-
item
750-
for item in value
751-
if not hasattr(pb_type, item)
752-
and hasattr(pb_type, f"{item}_")
753-
]
754-
for item in keys_to_update:
755-
value[f"{item}_"] = value.pop(item)
756-
757-
pb_value = marshal.to_proto(pb_type, value)
728+
pb_value = marshal.to_proto(pb_type, value)
758729

759730
if pb_value is not None:
760731
params[key] = pb_value

tests/test_marshal_types_struct.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,26 @@ class Foo(proto.Message):
243243
detached["bacon"] = True
244244
foo.value = detached
245245
assert foo.value == {"foo": "bar", "bacon": True}
246+
247+
248+
def test_struct_nested():
249+
class Foo(proto.Message):
250+
struct_field: struct_pb2.Struct = proto.Field(
251+
proto.MESSAGE,
252+
number=1,
253+
message=struct_pb2.Struct,
254+
)
255+
256+
class Bar(proto.Message):
257+
foo_field: Foo = proto.Field(
258+
proto.MESSAGE,
259+
number=1,
260+
message=Foo,
261+
)
262+
263+
foo = Foo({"struct_field": {"foo": "bagel"}})
264+
assert foo.struct_field == {"foo": "bagel"}
265+
266+
bar = Bar({"foo_field": {"struct_field": {"foo": "cheese"}}})
267+
assert bar.foo_field == Foo({"struct_field": {"foo": "cheese"}})
268+
assert bar.foo_field.struct_field == {"foo": "cheese"}

0 commit comments

Comments
 (0)