python-betterproto icon indicating copy to clipboard operation
python-betterproto copied to clipboard

Implicit optional is not respected

Open hrist0stoichev opened this issue 1 year ago • 3 comments

Summary

When you don't add an explicit optional, fields are not serialized properly if they happen to be set to the default value.

Reproduction Steps

message Sample {
    bool foo = 1
}

generates

[
 {
  "name": "foo",
  "type": "BOOLEAN",
  "mode": "NULLABLE",
 }
]

but the python code generated is

@dataclass(eq=False, repr=False)
class Sample(betterproto.Message):
    foo: bool = betterproto.string_field(1)

which implies that the boolean is non-nullable.

Then, if we do

bytes(Sample(foo=False))

it will be serialized to

b''

and actually treated as null.

If we change the proto to

message Sample {
    optional bool foo = 1
}

then the correct code is generated and the message is serialized as expected.

Actual Results

The protobuf documentation suggests that not setting optional should be treated in the same way as setting it.

System Information

libprotoc 28.3 Python 3.10.11 betterproto 2.0.0b7

Checklist

  • [X] I have searched the issues for duplicates.
  • [X] I have shown the entire traceback, if possible.
  • [X] I have verified this issue occurs on the latest prelease of betterproto which can be installed using pip install -U --pre betterproto, if possible.

hrist0stoichev avatar Nov 14 '24 13:11 hrist0stoichev

This should be the correct behavior for proto3, which is the version supported in betterproto. When you decode the message from the empty byte, the field should be set to the right value.

See the following example with Google's implementation (proto-plus):

import proto

class Msg(proto.Message):
    field = proto.Field(proto.BOOL, number=1)

print(Msg.serialize(Msg(field=True)))  # b'\x08\x01'
print(Msg.serialize(Msg(field=False)))  # b''

AdrienVannson avatar Nov 25 '24 13:11 AdrienVannson

When you decode the message from the empty byte, the field should be set to the right value.

How do you know whether the empty byte is null or false? If I understand correctly, having no modifier is the same as having the optional modifier, so b'' should be decoded as null but is, in fact, decoded as false.

hrist0stoichev avatar Nov 25 '24 16:11 hrist0stoichev

If the field is not marked as optional, it is not possible for the field to have a null value. If you decode the message from the empty byte, you will see that the field will be actually set to False.

If the field is marked as optional, b'' will make the field have a null value.

Note that this behavior is not valid for messages, which can always have a null value.

You can see this page for more details: https://protobuf.dev/programming-guides/field_presence/

AdrienVannson avatar Nov 25 '24 16:11 AdrienVannson