Skip to content

Commit d54c54d

Browse files
committed
Revert "Serialize default values in oneofs when calling to_dict() or to_json() (danielgtaylor#110)"
This reverts commit c1a76a5
1 parent 15e4fbb commit d54c54d

File tree

7 files changed

+27
-277
lines changed

7 files changed

+27
-277
lines changed

src/betterproto/__init__.py

+20-63
Original file line numberDiff line numberDiff line change
@@ -581,20 +581,18 @@ def __bytes__(self) -> bytes:
581581
# Being selected in a a group means this field is the one that is
582582
# currently set in a `oneof` group, so it must be serialized even
583583
# if the value is the default zero value.
584-
selected_in_group = (
585-
meta.group and self._group_current[meta.group] == field_name
586-
)
584+
selected_in_group = False
585+
if meta.group and self._group_current[meta.group] == field_name:
586+
selected_in_group = True
587587

588-
# Empty messages can still be sent on the wire if they were
589-
# set (or received empty).
590-
serialize_empty = isinstance(value, Message) and value._serialized_on_wire
591-
592-
include_default_value_for_oneof = self._include_default_value_for_oneof(
593-
field_name=field_name, meta=meta
594-
)
588+
serialize_empty = False
589+
if isinstance(value, Message) and value._serialized_on_wire:
590+
# Empty messages can still be sent on the wire if they were
591+
# set (or received empty).
592+
serialize_empty = True
595593

596594
if value == self._get_field_default(field_name) and not (
597-
selected_in_group or serialize_empty or include_default_value_for_oneof
595+
selected_in_group or serialize_empty
598596
):
599597
# Default (zero) values are not serialized. Two exceptions are
600598
# if this is the selected oneof item or if we know we have to
@@ -623,17 +621,6 @@ def __bytes__(self) -> bytes:
623621
sv = _serialize_single(2, meta.map_types[1], v)
624622
output += _serialize_single(meta.number, meta.proto_type, sk + sv)
625623
else:
626-
# If we have an empty string and we're including the default value for
627-
# a oneof, make sure we serialize it. This ensures that the byte string
628-
# output isn't simply an empty string. This also ensures that round trip
629-
# serialization will keep `which_one_of` calls consistent.
630-
if (
631-
isinstance(value, str)
632-
and value == ""
633-
and include_default_value_for_oneof
634-
):
635-
serialize_empty = True
636-
637624
output += _serialize_single(
638625
meta.number,
639626
meta.proto_type,
@@ -737,13 +724,6 @@ def _postprocess_single(
737724

738725
return value
739726

740-
def _include_default_value_for_oneof(
741-
self, field_name: str, meta: FieldMetadata
742-
) -> bool:
743-
return (
744-
meta.group is not None and self._group_current.get(meta.group) == field_name
745-
)
746-
747727
def parse(self: T, data: bytes) -> T:
748728
"""
749729
Parse the binary encoded Protobuf into this message instance. This
@@ -822,22 +802,10 @@ def to_dict(
822802
cased_name = casing(field_name).rstrip("_") # type: ignore
823803
if meta.proto_type == TYPE_MESSAGE:
824804
if isinstance(value, datetime):
825-
if (
826-
value != DATETIME_ZERO
827-
or include_default_values
828-
or self._include_default_value_for_oneof(
829-
field_name=field_name, meta=meta
830-
)
831-
):
805+
if value != DATETIME_ZERO or include_default_values:
832806
output[cased_name] = _Timestamp.timestamp_to_json(value)
833807
elif isinstance(value, timedelta):
834-
if (
835-
value != timedelta(0)
836-
or include_default_values
837-
or self._include_default_value_for_oneof(
838-
field_name=field_name, meta=meta
839-
)
840-
):
808+
if value != timedelta(0) or include_default_values:
841809
output[cased_name] = _Duration.delta_to_json(value)
842810
elif meta.wraps:
843811
if value is not None or include_default_values:
@@ -847,28 +815,19 @@ def to_dict(
847815
value = [i.to_dict(casing, include_default_values) for i in value]
848816
if value or include_default_values:
849817
output[cased_name] = value
850-
elif (
851-
value._serialized_on_wire
852-
or include_default_values
853-
or self._include_default_value_for_oneof(
854-
field_name=field_name, meta=meta
855-
)
856-
):
857-
output[cased_name] = value.to_dict(casing, include_default_values,)
858-
elif meta.proto_type == "map":
818+
else:
819+
if value._serialized_on_wire or include_default_values:
820+
output[cased_name] = value.to_dict(
821+
casing, include_default_values
822+
)
823+
elif meta.proto_type == TYPE_MAP:
859824
for k in value:
860825
if hasattr(value[k], "to_dict"):
861826
value[k] = value[k].to_dict(casing, include_default_values)
862827

863828
if value or include_default_values:
864829
output[cased_name] = value
865-
elif (
866-
value != self._get_field_default(field_name)
867-
or include_default_values
868-
or self._include_default_value_for_oneof(
869-
field_name=field_name, meta=meta
870-
)
871-
):
830+
elif value != self._get_field_default(field_name) or include_default_values:
872831
if meta.proto_type in INT_64_TYPES:
873832
if field_is_repeated:
874833
output[cased_name] = [str(n) for n in value]
@@ -927,8 +886,6 @@ def from_dict(self: T, value: dict) -> T:
927886
elif meta.wraps:
928887
setattr(self, field_name, value[key])
929888
else:
930-
# NOTE: `from_dict` mutates the underlying message, so no
931-
# assignment here is necessary.
932889
v.from_dict(value[key])
933890
elif meta.map_types and meta.map_types[1] == TYPE_MESSAGE:
934891
v = getattr(self, field_name)
@@ -954,8 +911,8 @@ def from_dict(self: T, value: dict) -> T:
954911
elif isinstance(v, str):
955912
v = enum_cls.from_string(v)
956913

957-
if v is not None:
958-
setattr(self, field_name, v)
914+
if v is not None:
915+
setattr(self, field_name, v)
959916
return self
960917

961918
def to_json(self, indent: Union[None, int, str] = None) -> str:

tests/inputs/google_impl_behavior_equivalence/google_impl_behavior_equivalence.proto

-13
This file was deleted.

tests/inputs/google_impl_behavior_equivalence/test_google_impl_behavior_equivalence.py

-55
This file was deleted.

tests/inputs/oneof_default_value_serialization/oneof_default_value_serialization.proto

-28
This file was deleted.

tests/inputs/oneof_default_value_serialization/test_oneof_default_value_serialization.py

-74
This file was deleted.

tests/inputs/oneof_enum/test_oneof_enum.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,34 @@
99
from tests.util import get_test_case_json_data
1010

1111

12+
@pytest.mark.xfail
1213
def test_which_one_of_returns_enum_with_default_value():
1314
"""
1415
returns first field when it is enum and set with default value
1516
"""
1617
message = Test()
1718
message.from_json(get_test_case_json_data("oneof_enum", "oneof_enum-enum-0.json"))
18-
19-
assert message.move == Move(
20-
x=0, y=0
21-
) # Proto3 will default this as there is no null
19+
assert message.move is None
2220
assert message.signal == Signal.PASS
2321
assert betterproto.which_one_of(message, "action") == ("signal", Signal.PASS)
2422

2523

24+
@pytest.mark.xfail
2625
def test_which_one_of_returns_enum_with_non_default_value():
2726
"""
2827
returns first field when it is enum and set with non default value
2928
"""
3029
message = Test()
3130
message.from_json(get_test_case_json_data("oneof_enum", "oneof_enum-enum-1.json"))
32-
assert message.move == Move(
33-
x=0, y=0
34-
) # Proto3 will default this as there is no null
35-
assert message.signal == Signal.RESIGN
31+
assert message.move is None
32+
assert message.signal == Signal.PASS
3633
assert betterproto.which_one_of(message, "action") == ("signal", Signal.RESIGN)
3734

3835

36+
@pytest.mark.xfail
3937
def test_which_one_of_returns_second_field_when_set():
4038
message = Test()
4139
message.from_json(get_test_case_json_data("oneof_enum"))
4240
assert message.move == Move(x=2, y=3)
43-
assert message.signal == Signal.PASS
41+
assert message.signal == 0
4442
assert betterproto.which_one_of(message, "action") == ("move", Move(x=2, y=3))

0 commit comments

Comments
 (0)