You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+211-5Lines changed: 211 additions & 5 deletions
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,10 @@
1
1
# Better Protobuf / gRPC Support for Python
2
2
3
-
This project aims to provide an improved experience when using Protobuf / gRPC in a modern Python environment by making use of modern language features and generating readable, understandable code. It will not support legacy features or environments. The following are supported:
3
+
This project aims to provide an improved experience when using Protobuf / gRPC in a modern Python environment by making use of modern language features and generating readable, understandable, idiomatic Python code. It will not support legacy features or environments. The following are supported:
4
4
5
5
- Protobuf 3 & gRPC code generation
6
6
- Both binary & JSON serialization is built-in
7
-
- Python 3.7+
7
+
- Python 3.7+ making use of:
8
8
- Enums
9
9
- Dataclasses
10
10
-`async`/`await`
@@ -17,7 +17,198 @@ This project is heavily inspired by, and borrows functionality from:
17
17
-https://github.com/eigenein/protobuf/
18
18
-https://github.com/vmagamedov/grpclib
19
19
20
-
## TODO
20
+
## Motivation
21
+
22
+
This project exists because I am unhappy with the state of the official Google protoc plugin for Python.
23
+
24
+
- No `async` support (requires additional `grpclib` plugin)
25
+
- No typing support or code completion/intelligence (requires additional `mypy` plugin)
26
+
- No `__init__.py` module files get generated
27
+
- Output is not importable
28
+
- Import paths break in Python 3 unless you mess with `sys.path`
29
+
- Bugs when names clash (e.g. `codecs` package)
30
+
- Generated code is not idiomatic
31
+
- Completely unreadable runtime code-generation
32
+
- Much code looks like C++ or Java ported 1:1 to Python
33
+
- Capitalized function names like `HasField()` and `SerializeToString()`
34
+
- Uses `SerializeToString()` rather than the built-in `__bytes__()`
35
+
36
+
This project is a reimplementation from the ground up focused on idiomatic modern Python to help fix some of the above. While it may not be a 1:1 drop-in replacement due to changed method names and call patterns, the wire format is identical.
37
+
38
+
## Installation & Getting Started
39
+
40
+
First, install the package:
41
+
42
+
```sh
43
+
$ pip install betterproto
44
+
```
45
+
46
+
Now, given a proto file, e.g `example.proto`:
47
+
48
+
```protobuf
49
+
syntax = "proto3";
50
+
51
+
package hello;
52
+
53
+
// Greeting represents a message you can tell a user.
# Generated by the protocol buffer compiler. DO NOT EDIT!
69
+
# sources: hello.proto
70
+
# plugin: python-betterproto
71
+
from dataclasses import dataclass
72
+
73
+
import betterproto
74
+
75
+
76
+
@dataclass
77
+
classHello(betterproto.Message):
78
+
"""Greeting represents a message you can tell a user."""
79
+
80
+
message: str= betterproto.string_field(1)
81
+
```
82
+
83
+
Now you can use it!
84
+
85
+
```py
86
+
>>>from hello import Hello
87
+
>>> test = Hello()
88
+
>>> test
89
+
Hello(message='')
90
+
91
+
>>> test.message ="Hey!"
92
+
>>> test
93
+
Hello(message="Hey!")
94
+
95
+
>>> serialized =bytes(test)
96
+
>>> serialized
97
+
b'\n\x04Hey!'
98
+
99
+
>>> another = Hello().parse(serialized)
100
+
>>> another
101
+
Hello(message="Hey!")
102
+
103
+
>>> another.to_dict()
104
+
{"message": "Hey!"}
105
+
>>> another.to_json(indent=2)
106
+
'{\n "message": "Hey!"\n}'
107
+
```
108
+
109
+
### Async gRPC Support
110
+
111
+
The generated Protobuf `Message` classes are compatible with [grpclib](https://github.com/vmagamedov/grpclib) so you are free to use it if you like. That said, this project also includes support for async gRPC stub generation with better static type checking and code completion support. It is enabled by default.
Sometimes it is useful to be able to determine whether a message has been sent on the wire. This is how the Google wrapper types work to let you know whether a value is unset, set as the default (zero value), or set as something else, for example.
168
+
169
+
Use `Message().serialized_on_wire` to determine if it was sent. This is a little bit different from the official Google generated Python code:
170
+
171
+
```py
172
+
# Old way
173
+
>>> mymessage.HasField('myfield')
174
+
175
+
# New way
176
+
>>> mymessage.myfield.serialized_on_wire
177
+
```
178
+
179
+
## Development
180
+
181
+
First, make sure you have Python 3.7+ and `pipenv` installed:
182
+
183
+
```sh
184
+
# Get set up with the virtual env & dependencies
185
+
$ pipenv install --dev
186
+
187
+
# Link the local package
188
+
$ pipenv shell
189
+
$ pip install -e .
190
+
```
191
+
192
+
### Tests
193
+
194
+
There are two types of tests:
195
+
196
+
1. Manually-written tests for some behavior of the library
197
+
2. Proto files and JSON inputs for automated tests
198
+
199
+
For #2, you can add a new `*.proto` file into the `betterproto/tests` directory along with a sample `*.json` input and it will get automatically picked up.
200
+
201
+
Here's how to run the tests.
202
+
203
+
```sh
204
+
# Generate assets from sample .proto files
205
+
$ pipenv run generate
206
+
207
+
# Run the tests
208
+
$ pipenv run tests
209
+
```
210
+
211
+
### TODO
21
212
22
213
-[x] Fixed length fields
23
214
-[x] Packed fixed-length
@@ -30,11 +221,26 @@ This project is heavily inspired by, and borrows functionality from:
30
221
-[ ] Support passthrough of unknown fields
31
222
-[x] Refs to nested types
32
223
-[x] Imports in proto files
33
-
-[] Well-known Google types
224
+
-[x] Well-known Google types
34
225
-[ ] JSON that isn't completely naive.
226
+
-[x] 64-bit ints as strings
227
+
-[x] Maps
228
+
-[x] Lists
229
+
-[ ] Bytes as base64
230
+
-[ ] Any support
231
+
-[ ] Well known types support (timestamp, duration, wrappers)
35
232
-[ ] Async service stubs
36
233
-[x] Unary-unary
37
234
-[x] Server streaming response
38
235
-[ ] Client streaming request
39
-
-[ ] Python package
236
+
-[ ] Renaming messages and fields to conform to Python name standards
237
+
-[ ] Renaming clashes with language keywords and standard library top-level packages
0 commit comments