A better Protobuf / gRPC generator & library
npx @tessl/cli install tessl/pypi-betterproto@1.2.00
# BetterProto
1
2
A modern, idiomatic Python implementation of Protocol Buffers (protobuf) and gRPC code generation that addresses limitations of the official Google protoc plugin. It generates clean, readable Python code using modern language features including dataclasses, async/await, type hints, and timezone-aware datetime objects for Python 3.6+.
3
4
## Package Information
5
6
- **Package Name**: betterproto
7
- **Language**: Python
8
- **Installation**: `pip install betterproto` (runtime) or `pip install "betterproto[compiler]"` (with code generation)
9
10
## Core Imports
11
12
```python
13
import betterproto
14
```
15
16
For message classes and field definitions:
17
18
```python
19
from betterproto import Message, Enum
20
```
21
22
For field creation functions:
23
24
```python
25
from betterproto import (
26
string_field, int32_field, bool_field, message_field,
27
enum_field, bytes_field, map_field
28
)
29
```
30
31
## Basic Usage
32
33
### Defining Messages
34
35
```python
36
from dataclasses import dataclass
37
from typing import List
38
import betterproto
39
40
@dataclass
41
class Person(betterproto.Message):
42
name: str = betterproto.string_field(1)
43
age: int = betterproto.int32_field(2)
44
email: str = betterproto.string_field(3)
45
46
@dataclass
47
class AddressBook(betterproto.Message):
48
people: List[Person] = betterproto.message_field(1)
49
```
50
51
### Serialization and Deserialization
52
53
```python
54
# Create a message
55
person = Person(name="Alice", age=30, email="alice@example.com")
56
57
# Serialize to binary
58
binary_data = bytes(person)
59
60
# Parse from binary
61
parsed_person = Person().parse(binary_data)
62
63
# JSON serialization
64
json_str = person.to_json()
65
person_from_json = Person().from_json(json_str)
66
67
# Dictionary conversion
68
person_dict = person.to_dict()
69
person_from_dict = Person().from_dict(person_dict)
70
```
71
72
### Using with gRPC
73
74
```python
75
import asyncio
76
from grpclib.client import Channel
77
import betterproto
78
79
class GreeterServiceStub(betterproto.ServiceStub):
80
async def say_hello(
81
self,
82
request: HelloRequest,
83
*,
84
timeout: Optional[float] = None,
85
deadline: Optional["Deadline"] = None,
86
metadata: Optional[betterproto._MetadataLike] = None,
87
) -> HelloReply:
88
return await self._unary_unary(
89
"/helloworld.Greeter/SayHello",
90
request,
91
HelloReply,
92
timeout=timeout,
93
deadline=deadline,
94
metadata=metadata,
95
)
96
97
async def main():
98
async with Channel("localhost", 50051) as channel:
99
greeter = GreeterServiceStub(channel)
100
reply = await greeter.say_hello(HelloRequest(name="World"))
101
print(f"Greeting: {reply.message}")
102
```
103
104
## Architecture
105
106
BetterProto's architecture consists of several key components:
107
108
- **Message Base Class**: All generated messages inherit from `betterproto.Message` providing serialization, parsing, and JSON conversion
109
- **Field Metadata System**: `FieldMetadata` dataclass stores protobuf-specific information (field numbers, types, wire formats)
110
- **Code Generator Plugin**: `betterproto.plugin` generates Python dataclasses from `.proto` files
111
- **Wire Format Handling**: Built-in varint encoding/decoding and wire type management compatible with standard protobuf
112
- **gRPC Integration**: `ServiceStub` base class for async gRPC client generation with `grpclib` support
113
114
## Capabilities
115
116
### Message Definition and Field Types
117
118
Core message functionality including field creation functions for all protobuf types, dataclass integration, and metadata handling for proper serialization and deserialization.
119
120
```python { .api }
121
class Message:
122
def __bytes__(self) -> bytes: ...
123
def parse(self, data: bytes) -> T: ...
124
def to_dict(self, casing: Casing = Casing.CAMEL, include_default_values: bool = False) -> dict: ...
125
def from_dict(self, value: dict) -> T: ...
126
def to_json(self, indent: Union[None, int, str] = None) -> str: ...
127
def from_json(self, value: Union[str, bytes]) -> T: ...
128
129
def string_field(number: int, group: Optional[str] = None) -> Any: ...
130
def int32_field(number: int, group: Optional[str] = None) -> Any: ...
131
def int64_field(number: int, group: Optional[str] = None) -> Any: ...
132
def bool_field(number: int, group: Optional[str] = None) -> Any: ...
133
def bytes_field(number: int, group: Optional[str] = None) -> Any: ...
134
def message_field(number: int, group: Optional[str] = None, wraps: Optional[str] = None) -> Any: ...
135
```
136
137
[Message and Field Types](./message-fields.md)
138
139
### Enumerations
140
141
Protobuf enumeration support with integer-based enums that provide string name conversion and integration with the message system.
142
143
```python { .api }
144
class Enum(int, enum.Enum):
145
@classmethod
146
def from_string(cls, name: str) -> int: ...
147
148
def enum_field(number: int, group: Optional[str] = None) -> Any: ...
149
```
150
151
[Enumerations](./enumerations.md)
152
153
### gRPC Service Integration
154
155
Async gRPC client stub generation with support for unary and streaming calls, timeout handling, metadata, and deadline management using grpclib.
156
157
```python { .api }
158
class ServiceStub:
159
def __init__(
160
self,
161
channel: Channel,
162
*,
163
timeout: Optional[float] = None,
164
deadline: Optional[Deadline] = None,
165
metadata: Optional[_MetadataLike] = None,
166
) -> None: ...
167
168
async def _unary_unary(
169
self,
170
route: str,
171
request: IProtoMessage,
172
response_type: Type[T],
173
*,
174
timeout: Optional[float] = None,
175
deadline: Optional[Deadline] = None,
176
metadata: Optional[_MetadataLike] = None,
177
) -> T: ...
178
```
179
180
[gRPC Service Integration](./grpc-services.md)
181
182
### Code Generation
183
184
Protocol buffer compiler plugin that generates clean Python dataclasses from .proto files with proper type hints, async gRPC stubs, and modern Python conventions.
185
186
```python { .api }
187
def main() -> None: ...
188
def generate_code(request, response) -> None: ...
189
```
190
191
[Code Generation](./code-generation.md)
192
193
### Serialization and Wire Format
194
195
Low-level serialization utilities including varint encoding/decoding, wire type handling, and binary format parsing compatible with standard protobuf implementations.
196
197
```python { .api }
198
def encode_varint(value: int) -> bytes: ...
199
def decode_varint(buffer: bytes, pos: int, signed: bool = False) -> Tuple[int, int]: ...
200
def parse_fields(value: bytes) -> Generator[ParsedField, None, None]: ...
201
def serialized_on_wire(message: Message) -> bool: ...
202
```
203
204
[Serialization and Wire Format](./serialization.md)
205
206
### Utility Functions
207
208
Helper functions for message introspection, one-of field handling, and casing conversion to support generated code and user applications.
209
210
```python { .api }
211
def which_one_of(message: Message, group_name: str) -> Tuple[str, Any]: ...
212
def safe_snake_case(value: str) -> str: ...
213
```
214
215
[Utility Functions](./utilities.md)
216
217
## Types
218
219
```python { .api }
220
@dataclass(frozen=True)
221
class FieldMetadata:
222
number: int
223
proto_type: str
224
map_types: Optional[Tuple[str, str]] = None
225
group: Optional[str] = None
226
wraps: Optional[str] = None
227
228
@dataclass(frozen=True)
229
class ParsedField:
230
number: int
231
wire_type: int
232
value: Any
233
raw: bytes
234
235
class Casing(enum.Enum):
236
CAMEL: Callable
237
SNAKE: Callable
238
239
# Type variables and aliases
240
T = TypeVar('T', bound='Message') # Bound type variable for Message subclasses
241
_Value = Union[str, bytes]
242
_MetadataLike = Union[Mapping[str, _Value], Collection[Tuple[str, _Value]]]
243
```