Skip to content
This repository was archived by the owner on Jun 9, 2025. It is now read-only.

Commit 25d0c4d

Browse files
Generate server option (#68)
* Add server generation option * Format code * Add documentation
1 parent 64be741 commit 25d0c4d

File tree

5 files changed

+63
-19
lines changed

5 files changed

+63
-19
lines changed

docs/getting-started.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ You can also use it from `grpcio-tools`:
1010
pip install grpcio-tools
1111
```
1212

13-
1413
## Install `betterproto2_compiler`
1514

1615
It is possible to install `betterproto2_compiler` using pip:
@@ -19,10 +18,6 @@ It is possible to install `betterproto2_compiler` using pip:
1918
pip install betterproto2_compiler
2019
```
2120

22-
!!! warning
23-
The compiler needs Python 3.10, 3.11 or 3.12. Don't worry! The generated code will be compatible with all Python versions from Python 3.8 to Python 3.13.
24-
25-
2621
## Compile a proto file
2722

2823
Create the following `example.proto` file.
@@ -35,6 +30,10 @@ package helloworld;
3530
message HelloWorld {
3631
string message = 1;
3732
}
33+
34+
service HelloService {
35+
rpc SayHello (HelloWorld) returns (HelloWorld);
36+
}
3837
```
3938

4039
You should now be able to compile it using:
@@ -50,3 +49,40 @@ If you installed `protoc` with `grpc-tools`, the command will be:
5049
mkdir lib
5150
python -m grpc.tools.protoc -I . --python_betterproto2_out=lib example.proto
5251
```
52+
53+
### Service compilation
54+
55+
#### Clients
56+
57+
By default, for each service, betterproto will generate a synchronous client. Both synchronous and asynchronous clients
58+
are supported.
59+
60+
- Synchronous clients rely on the `grpcio` package. Make sure to enable the `grpcio` extra package when installing
61+
betterproto2 to use them.
62+
- Asynchronous clients rely on the `grpclib` package. Make sure to enable the `grpclib` extra package when installing
63+
betterproto2 to use them.
64+
65+
To choose which clients to generate, use the `client_generation` option of betterproto. It supports the following
66+
values:
67+
68+
- `none`: Clients are not generated.
69+
- `sync`: Only synchronous clients are generated.
70+
- `async`: Only asynchronous clients are generated.
71+
- `sync_async`: Both synchronous and asynchronous clients are generated.
72+
Asynchronous clients are generated with the Async suffix.
73+
- `async_sync`: Both synchronous and asynchronous clients are generated.
74+
Synchronous clients are generated with the Sync suffix.
75+
- `sync_async_no_default`: Both synchronous and asynchronous clients are generated.
76+
Synchronous clients are generated with the Sync suffix, and asynchronous clients are generated with the Async
77+
suffix.
78+
79+
For example, `protoc -I . --python_betterproto2_out=lib example.proto --python_betterproto2_opt=client_generation=async`
80+
will only generate asynchronous clients.
81+
82+
#### Servers
83+
84+
By default, betterproto will not generate server base classes. To enable them, set the `server_generation` option to
85+
`async` with `--python_betterproto2_opt=server_generation=async`.
86+
87+
These base classes will be asynchronous and rely on `grpclib`. To use them, make sure to install `betterproto2` with the
88+
`grpclib` extra package.

src/betterproto2_compiler/plugin/parser.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
CodeGeneratorResponseFeature,
1515
CodeGeneratorResponseFile,
1616
)
17-
from betterproto2_compiler.settings import ClientGeneration, Settings
17+
from betterproto2_compiler.settings import ClientGeneration, ServerGeneration, Settings
1818

1919
from .compiler import outputfile_compiler
2020
from .models import (
@@ -62,22 +62,27 @@ def _traverse(
6262
def get_settings(plugin_options: list[str]) -> Settings:
6363
# Synchronous clients are suitable for most users
6464
client_generation = ClientGeneration.SYNC
65+
server_generation = ServerGeneration.NONE
6566

6667
for opt in plugin_options:
6768
if opt.startswith("client_generation="):
6869
name = opt.split("=")[1]
69-
70-
# print(ClientGeneration.__members__, file=sys.stderr)
71-
# print([member.value for member in ClientGeneration])
72-
7370
try:
7471
client_generation = ClientGeneration(name)
7572
except ValueError:
7673
raise ValueError(f"Invalid client_generation option: {name}")
7774

75+
if opt.startswith("server_generation="):
76+
name = opt.split("=")[1]
77+
try:
78+
server_generation = ServerGeneration(name)
79+
except ValueError:
80+
raise ValueError(f"Invalid server_generation option: {name}")
81+
7882
return Settings(
7983
pydantic_dataclasses="pydantic_dataclasses" in plugin_options,
8084
client_generation=client_generation,
85+
server_generation=server_generation,
8186
)
8287

8388

src/betterproto2_compiler/settings.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ class ClientGeneration(StrEnum):
2020
SYNC_ASYNC = "sync_async"
2121
"""Both synchronous and asynchronous clients are generated.
2222
23-
The asynchronous client is generated with the Async suffix."""
23+
Asynchronous clients are generated with the Async suffix."""
2424

2525
ASYNC_SYNC = "async_sync"
2626
"""Both synchronous and asynchronous clients are generated.
2727
28-
The synchronous client is generated with the Sync suffix."""
28+
Synchronous clients are generated with the Sync suffix."""
2929

3030
SYNC_ASYNC_NO_DEFAULT = "sync_async_no_default"
3131
"""Both synchronous and asynchronous clients are generated.
3232
33-
The synchronous client is generated with the Sync suffix, and the asynchronous client is generated with the Async
33+
Synchronous clients are generated with the Sync suffix, and asynchronous clients are generated with the Async
3434
suffix."""
3535

3636
@property
@@ -60,8 +60,14 @@ def is_async_prefixed(self) -> bool:
6060
return self in {ClientGeneration.SYNC_ASYNC, ClientGeneration.SYNC_ASYNC_NO_DEFAULT}
6161

6262

63+
class ServerGeneration(StrEnum):
64+
NONE = "none"
65+
ASYNC = "async"
66+
67+
6368
@dataclass
6469
class Settings:
6570
pydantic_dataclasses: bool
6671

6772
client_generation: ClientGeneration
73+
server_generation: ServerGeneration

src/betterproto2_compiler/templates/template.py.j2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ default_message_pool.register_message("{{ output_file.package }}", "{{ message.p
102102
{{ i }}
103103
{% endfor %}
104104

105+
{% if output_file.settings.server_generation == "async" %}
105106
{% for _, service in output_file.services|dictsort(by="key") %}
106107
class {{ service.py_name }}Base(ServiceBase):
107108
{% if service.comment %}
@@ -170,3 +171,4 @@ class {{ service.py_name }}Base(ServiceBase):
170171
}
171172

172173
{% endfor %}
174+
{% endif %}

tests/util.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,7 @@ def get_directories(path):
2727
yield from directories
2828

2929

30-
async def protoc(
31-
path: str | Path,
32-
output_dir: str | Path,
33-
reference: bool = False,
34-
pydantic_dataclasses: bool = False,
35-
):
30+
async def protoc(path: str | Path, output_dir: str | Path, reference: bool = False, pydantic_dataclasses: bool = False):
3631
path: Path = Path(path).resolve()
3732
output_dir: Path = Path(output_dir).resolve()
3833
python_out_option: str = "python_betterproto2_out" if not reference else "python_out"

0 commit comments

Comments
 (0)