Skip to content

Commit 48ebb30

Browse files
Add documentation for field presence and oneof support (#37)
* Hide source code * Small documentation changes * Document field presence * Add example * Document oneof
1 parent 9c44891 commit 48ebb30

File tree

5 files changed

+815
-14
lines changed

5 files changed

+815
-14
lines changed

docs/tutorial/messages.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Messages
2+
3+
A protobuf message is represented by a class that inherit from the `betterproto2.Message` abstract class.
4+
5+
## Field presence
6+
7+
The [documentation](https://protobuf.dev/programming-guides/field_presence/) of protobuf defines field presence as "the
8+
notion of whether a protobuf field has a value". The presence of a field can be tracked in two ways:
9+
10+
- **Implicit presence.** It is not possible to know if the field was set to its default value or if it was simply
11+
omitted. When the field is omitted, it is set to its default value automatically (`0` for an `int`, `""` for a
12+
string, ...)
13+
- **Explicit presence.** It is possible to know if the field was set to its default value or if it was
14+
omitted. In Python, these fields are marked as optional. They are set to `None` when omitted.
15+
16+
The [documentation](https://protobuf.dev/programming-guides/field_presence/#presence-in-proto3-apis) of protobuf shows
17+
when field presence is explicitly tracked.
18+
19+
For example, given the following `proto` file:
20+
21+
```proto
22+
syntax = "proto3";
23+
24+
message Message {
25+
int32 x = 1;
26+
optional int32 y = 2;
27+
}
28+
```
29+
30+
We can see that the default values are not the same:
31+
32+
```python
33+
>>> msg = Message()
34+
>>> print(msg.x)
35+
0
36+
>>> print(msg.y)
37+
None
38+
```
39+
40+
!!! warning
41+
When a field is a message, its presence is always tracked explicitly even if it is not marked as optional. Marking a
42+
message field as optional has no effect: the default value of such a field is always `None`, not an empty message.
43+
44+
## Oneof support
45+
46+
Protobuf supports grouping fields in a `oneof` clause: at most one of the fields in the group can be set at the same
47+
time. Let's use the following `proto`:
48+
49+
```proto
50+
syntax = "proto3";
51+
52+
message Test {
53+
oneof group {
54+
bool a = 1;
55+
int32 b = 2;
56+
string c = 3;
57+
}
58+
}
59+
```
60+
61+
The `betterproto2.which_one_of` function allows finding which one of the fields of the `oneof` group is set. The
62+
function returns the name of the field that is set, and the value of the field.
63+
64+
```python
65+
>>> betterproto2.which_one_of(Message(a=True), group_name="group")
66+
('a', True)
67+
>>> betterproto2.which_one_of(Message(), group_name="group")
68+
('', None)
69+
```
70+
71+
On Python 3.10 and later, it is also possible to use a `match` statement to find which item in a `oneof` group is active.
72+
73+
```python
74+
>>> def find(m: Message) -> str:
75+
... match m:
76+
... case Message(a=bool(value)):
77+
... return f"a is set to {value}"
78+
... case Message(b=int(value)):
79+
... return f"b is set to {value}"
80+
... return "No field set"
81+
...
82+
>>> find(Message(a=True))
83+
'a is set to True'
84+
>>> find(Message(b=12))
85+
'b is set to 12'
86+
>>> find(Message())
87+
'No field set'
88+
```

mkdocs.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ nav:
1313
- Betterproto2:
1414
- index.md
1515
- Quick start: quick-start.md
16+
- Tutorial:
17+
- Messages: tutorial/messages.md
1618
- API: api.md
1719
- Migrating: migrating.md
1820
- Betterproto2 compiler: https://betterproto.github.io/python-betterproto2-compiler/
1921

2022
plugins:
2123
- search
22-
- mkdocstrings
24+
- mkdocstrings:
25+
handlers:
26+
python:
27+
options:
28+
show_source: false
2329

2430
markdown_extensions:
2531
- admonition

0 commit comments

Comments
 (0)