@@ -834,8 +834,9 @@ def _get_field_default_gen(cls, field: dataclasses.Field) -> Any:
834
834
# This is some kind of list (repeated) field.
835
835
return list
836
836
elif t .__origin__ is Union and t .__args__ [1 ] is type (None ):
837
- # This is an optional (wrapped) field. For setting the default we
838
- # really don't care what kind of field it is.
837
+ # This is an optional field (either wrapped, or using proto3
838
+ # field presence). For setting the default we really don't care
839
+ # what kind of field it is.
839
840
return type (None )
840
841
else :
841
842
return t
@@ -1009,6 +1010,7 @@ def to_dict(
1009
1010
defaults = self ._betterproto .default_gen
1010
1011
for field_name , meta in self ._betterproto .meta_by_field_name .items ():
1011
1012
field_is_repeated = defaults [field_name ] is list
1013
+ field_is_optional = defaults [field_name ] is type (None )
1012
1014
value = getattr (self , field_name )
1013
1015
cased_name = casing (field_name ).rstrip ("_" ) # type: ignore
1014
1016
if meta .proto_type == TYPE_MESSAGE :
@@ -1096,6 +1098,9 @@ def to_dict(
1096
1098
output [cased_name ] = [enum_class (value ).name ]
1097
1099
elif value is None :
1098
1100
output [cased_name ] = None
1101
+ elif field_is_optional :
1102
+ enum_class = field_types [field_name ].__args__ [0 ]
1103
+ output [cased_name ] = enum_class (value ).name
1099
1104
else :
1100
1105
enum_class = field_types [field_name ] # noqa
1101
1106
output [cased_name ] = enum_class (value ).name
@@ -1133,6 +1138,9 @@ def from_dict(self: T, value: Dict[str, Any]) -> T:
1133
1138
if value [key ] is not None :
1134
1139
if meta .proto_type == TYPE_MESSAGE :
1135
1140
v = getattr (self , field_name )
1141
+ if value [key ] is None and self ._get_field_default (key ) == None :
1142
+ # Setting an optional value to None.
1143
+ setattr (self , field_name , None )
1136
1144
if isinstance (v , list ):
1137
1145
cls = self ._betterproto .cls_by_field [field_name ]
1138
1146
if cls == datetime :
@@ -1152,6 +1160,9 @@ def from_dict(self: T, value: Dict[str, Any]) -> T:
1152
1160
setattr (self , field_name , v )
1153
1161
elif meta .wraps :
1154
1162
setattr (self , field_name , value [key ])
1163
+ elif v is None :
1164
+ cls = self ._betterproto .cls_by_field [field_name ]
1165
+ setattr (self , field_name , cls ().from_dict (value [key ]))
1155
1166
else :
1156
1167
# NOTE: `from_dict` mutates the underlying message, so no
1157
1168
# assignment here is necessary.
0 commit comments