8 Commits

Author SHA1 Message Date
kalzoo
ee4265492d docs: Add semantic release information and issue template 2021-11-16 22:42:31 -08:00
kalzoo
32faddf322 chore: Add Semantic Release GitHub action 2021-11-16 22:42:03 -08:00
kalzoo
89d1f47fac Install Semantic Release 2021-11-16 22:30:16 -08:00
lazytype
c424b6f8db Include AsyncIterator import for both clients and servers (#264)
Co-authored-by: Robin Lambertz <github@roblab.la>
2021-11-05 14:22:15 +00:00
James Hilton-Balfe
421fdba309 Allow parsing of messages from ByteStrings #266 2021-10-26 00:34:33 +01:00
Robin Lambertz
fb2793e0b6 Allow parsing messages from byteslike
Byteslike objects (like memoryview) do not have a decode function defined.
Instead, a string may be created from them by passing them to the str
constructor along with an encoding.
2021-08-25 12:53:02 +02:00
PIGNOSE
ad8b91766a Add benchmarking cases for nested, repeat and deserialize (#241) 2021-06-21 23:38:22 +02:00
Bekhzod Tillakhanov
a33126544b Fix readme docs 'Async gRPC Support' (#249) 2021-06-21 23:29:59 +02:00
10 changed files with 4219 additions and 14 deletions

9
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View 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).

View File

@@ -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
View File

@@ -6,6 +6,7 @@
.pytest_cache
.python-version
build/
node_modules/
tests/output_*
**/__pycache__
dist

View File

@@ -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)!

View File

@@ -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
View 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"
}
}

View File

@@ -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]

View File

@@ -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

View File

@@ -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

4049
yarn.lock Normal file

File diff suppressed because it is too large Load Diff