Compare commits
8 Commits
v2.0.0b3
...
285-semant
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee4265492d | ||
|
|
32faddf322 | ||
|
|
89d1f47fac | ||
|
|
c424b6f8db | ||
|
|
421fdba309 | ||
|
|
fb2793e0b6 | ||
|
|
ad8b91766a | ||
|
|
a33126544b |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
## Description
|
||||
|
||||
<!-- Thanks for contributing to betterproto! Add a thorough explanation of what your changes do below this line: -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] This PR targets the `rc` branch (**not** `master`).
|
||||
- [ ] [If this should release a new version to PyPI when merged] The title of the PR follows the [Angular Conventional Commit](https://www.conventionalcommits.org/) syntax (`feat:` or `fix:`, with `BREAKING CHANGE:` in the commit message body if appropriate), and clearly describes the fix or feature.
|
||||
- [ ] Documentation is updated (`README.md` and docstrings).
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -16,12 +16,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Install poetry
|
||||
run: python -m pip install poetry
|
||||
- name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build package
|
||||
run: poetry build
|
||||
- name: Publish package to PyPI
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
.pytest_cache
|
||||
.python-version
|
||||
build/
|
||||
node_modules/
|
||||
tests/output_*
|
||||
**/__pycache__
|
||||
dist
|
||||
|
||||
39
README.md
39
README.md
@@ -160,6 +160,12 @@ service Echo {
|
||||
}
|
||||
```
|
||||
|
||||
Generate echo proto file:
|
||||
|
||||
```
|
||||
python -m grpc_tools.protoc -I . --python_betterproto_out=. echo.proto
|
||||
```
|
||||
|
||||
A client can be implemented as follows:
|
||||
```python
|
||||
import asyncio
|
||||
@@ -199,28 +205,29 @@ To use them, simply subclass the base class in the generated files and override
|
||||
service methods:
|
||||
|
||||
```python
|
||||
from echo import EchoBase
|
||||
import asyncio
|
||||
from echo import EchoBase, EchoResponse, EchoStreamResponse
|
||||
from grpclib.server import Server
|
||||
from typing import AsyncIterator
|
||||
|
||||
|
||||
class EchoService(EchoBase):
|
||||
async def echo(self, value: str, extra_times: int) -> "EchoResponse":
|
||||
return value
|
||||
return EchoResponse([value for _ in range(extra_times)])
|
||||
|
||||
async def echo_stream(
|
||||
self, value: str, extra_times: int
|
||||
) -> AsyncIterator["EchoStreamResponse"]:
|
||||
async def echo_stream(self, value: str, extra_times: int) -> AsyncIterator["EchoStreamResponse"]:
|
||||
for _ in range(extra_times):
|
||||
yield value
|
||||
yield EchoStreamResponse(value)
|
||||
|
||||
|
||||
async def start_server():
|
||||
HOST = "127.0.0.1"
|
||||
PORT = 1337
|
||||
async def main():
|
||||
server = Server([EchoService()])
|
||||
await server.start(HOST, PORT)
|
||||
await server.serve_forever()
|
||||
await server.start("127.0.0.1", 50051)
|
||||
await server.wait_closed()
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
```
|
||||
|
||||
### JSON
|
||||
@@ -491,6 +498,16 @@ protoc \
|
||||
- [x] Automate running tests
|
||||
- [ ] Cleanup!
|
||||
|
||||
|
||||
## Release
|
||||
|
||||
New versions are versioned and released using [Semantic Release](https://github.com/semantic-release/semantic-release). When new commits
|
||||
using the Angular Conventional Commits syntax land on `master` or `rc`, those commits are used to determine what new version to release.
|
||||
|
||||
All Pull Requests must target the `rc` branch; when merged into `rc` they will publish new release candidate (`rc`) versions to PyPI
|
||||
automatically. When maintainers want to publish a new full release, they simply merge `rc` into `master`. This flow ensures that features
|
||||
and fixes are published quickly and continuously rather than awaiting a manual release process.
|
||||
|
||||
## Community
|
||||
|
||||
Join us on [Slack](https://join.slack.com/t/betterproto/shared_invite/zt-f0n0uolx-iN8gBNrkPxtKHTLpG3o1OQ)!
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import betterproto
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestMessage(betterproto.Message):
|
||||
@@ -9,6 +11,29 @@ class TestMessage(betterproto.Message):
|
||||
baz: float = betterproto.float_field(2)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestNestedChildMessage(betterproto.Message):
|
||||
str_key: str = betterproto.string_field(0)
|
||||
bytes_key: bytes = betterproto.bytes_field(1)
|
||||
bool_key: bool = betterproto.bool_field(2)
|
||||
float_key: float = betterproto.float_field(3)
|
||||
int_key: int = betterproto.uint64_field(4)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestNestedMessage(betterproto.Message):
|
||||
foo: TestNestedChildMessage = betterproto.message_field(0)
|
||||
bar: TestNestedChildMessage = betterproto.message_field(1)
|
||||
baz: TestNestedChildMessage = betterproto.message_field(2)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestRepeatedMessage(betterproto.Message):
|
||||
foo_repeat: List[str] = betterproto.string_field(0)
|
||||
bar_repeat: List[int] = betterproto.int64_field(1)
|
||||
baz_repeat: List[bool] = betterproto.bool_field(2)
|
||||
|
||||
|
||||
class BenchMessage:
|
||||
"""Test creation and usage a proto message."""
|
||||
|
||||
@@ -16,6 +41,30 @@ class BenchMessage:
|
||||
self.cls = TestMessage
|
||||
self.instance = TestMessage()
|
||||
self.instance_filled = TestMessage(0, "test", 0.0)
|
||||
self.instance_filled_bytes = bytes(self.instance_filled)
|
||||
self.instance_filled_nested = TestNestedMessage(
|
||||
TestNestedChildMessage("foo", bytearray(b"test1"), True, 0.1234, 500),
|
||||
TestNestedChildMessage("bar", bytearray(b"test2"), True, 3.1415, -302),
|
||||
TestNestedChildMessage("baz", bytearray(b"test3"), False, 1e5, 300),
|
||||
)
|
||||
self.instance_filled_nested_bytes = bytes(self.instance_filled_nested)
|
||||
self.instance_filled_repeated = TestRepeatedMessage(
|
||||
[
|
||||
"test1",
|
||||
"test2",
|
||||
"test3",
|
||||
"test4",
|
||||
"test5",
|
||||
"test6",
|
||||
"test7",
|
||||
"test8",
|
||||
"test9",
|
||||
"test10",
|
||||
],
|
||||
[2, -100, 0, 500000, 600, -425678, 1000000000, -300, 1, -694214214466],
|
||||
[True, False, False, False, True, True, False, True, False, False],
|
||||
)
|
||||
self.instance_filled_repeated_bytes = bytes(self.instance_filled_repeated)
|
||||
|
||||
def time_overhead(self):
|
||||
"""Overhead in class definition."""
|
||||
@@ -50,6 +99,26 @@ class BenchMessage:
|
||||
"""Time serializing a message to wire."""
|
||||
bytes(self.instance_filled)
|
||||
|
||||
def time_deserialize(self):
|
||||
"""Time deserialize a message."""
|
||||
TestMessage().parse(self.instance_filled_bytes)
|
||||
|
||||
def time_serialize_nested(self):
|
||||
"""Time serializing a nested message to wire."""
|
||||
bytes(self.instance_filled_nested)
|
||||
|
||||
def time_deserialize_nested(self):
|
||||
"""Time deserialize a nested message."""
|
||||
TestNestedMessage().parse(self.instance_filled_nested_bytes)
|
||||
|
||||
def time_serialize_repeated(self):
|
||||
"""Time serializing a repeated message to wire."""
|
||||
bytes(self.instance_filled_repeated)
|
||||
|
||||
def time_deserialize_repeated(self):
|
||||
"""Time deserialize a repeated message."""
|
||||
TestRepeatedMessage().parse(self.instance_filled_repeated_bytes)
|
||||
|
||||
|
||||
class MemSuite:
|
||||
def setup(self):
|
||||
|
||||
52
package.json
Normal file
52
package.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "python-betterproto-semantic-release",
|
||||
"version": "1.0.0",
|
||||
"description": "Encapsulate dependencies needed to use semantic-release",
|
||||
"dependencies": {
|
||||
"@semantic-release/exec": "^5.0.0",
|
||||
"@semantic-release/git": "^9.0.0",
|
||||
"@semantic-release/gitlab": "^6.0.4",
|
||||
"conventional-changelog-eslint": "^3.0.8",
|
||||
"semantic-release": "^17.1.1"
|
||||
},
|
||||
"release": {
|
||||
"branches": [
|
||||
"master",
|
||||
{
|
||||
"name": "rc",
|
||||
"prerelease": true
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"preset": "angular"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/release-notes-generator",
|
||||
{
|
||||
"preset": "angular"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/exec",
|
||||
{
|
||||
"prepareCmd": "poetry version ${nextRelease.version}"
|
||||
}
|
||||
],
|
||||
"@semantic-release/github",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": [
|
||||
"pyproject.toml"
|
||||
],
|
||||
"message": "Release v${nextRelease.version} [skip ci]"
|
||||
}
|
||||
]
|
||||
],
|
||||
"repositoryUrl": "ssh://git@github.com/danielgtaylor/python-betterproto.git"
|
||||
}
|
||||
}
|
||||
@@ -866,7 +866,7 @@ class Message(ABC):
|
||||
value = struct.unpack(fmt, value)[0]
|
||||
elif wire_type == WIRE_LEN_DELIM:
|
||||
if meta.proto_type == TYPE_STRING:
|
||||
value = value.decode("utf-8")
|
||||
value = str(value, "utf-8")
|
||||
elif meta.proto_type == TYPE_MESSAGE:
|
||||
cls = self._betterproto.cls_by_field[field_name]
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from abc import ABC
|
||||
from collections import AsyncIterable
|
||||
from collections.abc import AsyncIterable
|
||||
from typing import Callable, Any, Dict
|
||||
|
||||
import grpclib
|
||||
|
||||
@@ -653,7 +653,9 @@ class ServiceMethodCompiler(ProtoContentBase):
|
||||
self.output_file.typing_imports.add("AsyncIterable")
|
||||
self.output_file.typing_imports.add("Iterable")
|
||||
self.output_file.typing_imports.add("Union")
|
||||
if self.server_streaming:
|
||||
|
||||
# Required by both client and server
|
||||
if self.client_streaming or self.server_streaming:
|
||||
self.output_file.typing_imports.add("AsyncIterator")
|
||||
|
||||
super().__post_init__() # check for unset fields
|
||||
|
||||
Reference in New Issue
Block a user