danielgtaylor / danielgtaylor/python-betterproto

OneOf and empty Message issue

Open
#360 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
1.8k
Forks
234
PR merge metrics
No merged PRs in 30d

Description

When try to use empty Message (Message without fields) in one_of group, I cannot convert it to dict or bool.

Code to reproduce:
```proto
syntax = "proto3";

message CommandGetInfo
{}

message CommandGetName
{}

message Test {
oneof foo {
bool on = 1;
int32 count = 2;
string name = 3;
CommandGetInfo get_info = 4;
CommandGetName get_name = 5;
}
}
```

```python
import betterproto
from oneof import Test, CommandGetInfo, CommandGetName

def print_one_of(msg: Test):
def _to_bool(_value) -> bool:
return True if _value else False
msg_one_of = betterproto.which_one_of(msg, "foo")
print(f"msg={msg}")
print(f"msg_one_of={msg_one_of}")
print(f"msg to dict={msg.to_dict()}")
print(f"to bool test:")
print(f"\tmsg.on={_to_bool(msg.on)}")
print(f"\tmsg.count={_to_bool(msg.count)}")
print(f"\tmsg.name={_to_bool(msg.name)}")
print(f"\tmsg.get_info={_to_bool(msg.get_info)}")
print(f"\tmsg.get_name={_to_bool(msg.get_name)}")
print(f"to bytes: {bytes(msg)}")
print("\n")

test = Test()
print_one_of(msg=test)

test.get_name = CommandGetName()
print_one_of(msg=test)

test.on = True
print_one_of(msg=test)

test.count = 57
print_one_of(msg=test)

test.name = "Name"
print_one_of(msg=test)

test.get_info = CommandGetInfo()
print_one_of(msg=test)
```

Output:
```bash
msg=Test(on=False, count=0, name='', get_info=CommandGetInfo(), get_name=CommandGetName())
msg_one_of=('', None)
msg to dict={}
to bool test:
msg.on=False
msg.count=False
msg.name=False
msg.get_info=True
msg.get_name=True
to bytes: b''

msg=Test(on=False, count=0, name='', get_info=CommandGetInfo(), get_name=CommandGetName())
msg_one_of=('get_name', CommandGetName())
msg to dict={}
to bool test:
msg.on=False
msg.count=False
msg.name=False
msg.get_info=True
msg.get_name=True
to bytes: b''

msg=Test(on=True, count=0, name='', get_info=CommandGetInfo(), get_name=CommandGetName())
msg_one_of=('on', True)
msg to dict={'on': True}
to bool test:
msg.on=True
msg.count=False
msg.name=False
msg.get_info=True
msg.get_name=True
to bytes: b'\x08\x01'

msg=Test(on=False, count=57, name='', get_info=CommandGetInfo(), get_name=CommandGetName())
msg_one_of=('count', 57)
msg to dict={'count': 57}
to bool test:
msg.on=False
msg.count=True
msg.name=False
msg.get_info=True
msg.get_name=True
to bytes: b'\x109'

msg=Test(on=False, count=0, name='Name', get_info=CommandGetInfo(), get_name=CommandGetName())
msg_one_of=('name', 'Name')
msg to dict={'name': 'Name'}
to bool test:
msg.on=False
msg.count=False
msg.name=True
msg.get_info=True
msg.get_name=True
to bytes: b'\x1a\x04Name'

msg=Test(on=False, count=0, name='', get_info=CommandGetInfo(), get_name=CommandGetName())
msg_one_of=('get_info', CommandGetInfo())
msg to dict={}
to bool test:
msg.on=False
msg.count=False
msg.name=False
msg.get_info=True
msg.get_name=True
to bytes: b''
```

We can see that bool values are always set to True for empty proto messages, serializing them to dictionary gives empty dictionary, serializing to bytes also gives empty value.

I tested on python versions 3.9 and 3.10.
Betterproto: 1.2.5

Contributor guide

Open the contributing guide

Research direction

Start by running the provided Python reproduction against the reported Betterproto version and compare the empty-message cases with the scalar oneof cases. Trace which_one_of, to_dict(), and bytes(msg) for empty messages, then verify that oneof selection and the intended dictionary and byte behavior are consistent for both CommandGetInfo and CommandGetName.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.