Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ stages:
- name: deploy
if: tag IS present AND type != pull_request
env:
- PYTHON_IMAGE="python:3.6"
- PYTHON_IMAGE="python:3.7"
- PYTHON_IMAGE="standy/pypy:3.6-6.1.0"
- PYTHON_IMAGE="python:3.6" PURERPC_BACKEND="asyncio"
- PYTHON_IMAGE="python:3.6" PURERPC_BACKEND="curio"
- PYTHON_IMAGE="python:3.6" PURERPC_BACKEND="trio"
- PYTHON_IMAGE="python:3.7" PURERPC_BACKEND="asyncio"
- PYTHON_IMAGE="python:3.7" PURERPC_BACKEND="curio"
- PYTHON_IMAGE="python:3.7" PURERPC_BACKEND="trio"
- PYTHON_IMAGE="standy/pypy:3.6-6.1.0" PURERPC_BACKEND="asyncio"
- PYTHON_IMAGE="standy/pypy:3.6-6.1.0" PURERPC_BACKEND="curio"
- PYTHON_IMAGE="standy/pypy:3.6-6.1.0" PURERPC_BACKEND="trio"

script:
- ./ci/run_tests_in_docker.sh $PYTHON_IMAGE
- ./ci/run_tests_in_docker.sh $PYTHON_IMAGE $PURERPC_BACKEND

jobs:
include:
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ FROM ${BASE_IMAGE}
COPY . /purerpc
WORKDIR /purerpc
RUN pip install .
RUN pip install curio trio # Optional, for tests
31 changes: 16 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

[![Build Status](https://travis-ci.org/standy66/purerpc.png?branch=master)](https://travis-ci.org/standy66/purerpc)

Asynchronous pure Python gRPC server and client implementation using
[curio](https://github.com/dabeaz/curio) and [hyper-h2](https://github.com/python-hyper/hyper-h2)
Asynchronous pure Python gRPC server and client implementation supporting
[asyncio](https://docs.python.org/3/library/asyncio.html),
[curio](https://github.com/dabeaz/curio) and
[trio](https://github.com/python-trio/trio).

## Requirements

Expand All @@ -24,6 +26,8 @@ Latest development version:
pip install git+https://github.com/standy66/purerpc.git
```

By default purerpc uses asyncio event loop, if you want to use curio or trio, please install them manually.

## protoc plugin

purerpc adds `protoc-gen-purerpc` plugin for `protoc` to your `PATH` enviroment variable
Expand Down Expand Up @@ -62,16 +66,16 @@ class Greeter(GreeterServicer):

server = Server(50055)
server.add_service(Greeter().service)
server.serve()
server.serve(backend="asyncio")
```

### Client

```python
import curio
import purerpc
from greeter_pb2 import HelloRequest, HelloReply
from greeter_grpc import GreeterStub
from purerpc import Channel


async def gen():
Expand All @@ -80,22 +84,19 @@ async def gen():


async def main():
channel = Channel("localhost", 50055)
# This is optional, will be run automatically on the first request
await channel.connect()
stub = GreeterStub(channel)

reply = await stub.SayHello(HelloRequest(name="World"))
print(reply.message)

async for reply in stub.SayHelloToMany(gen()):
async with purerpc.insecure_channel("localhost", 50055) as channel:
stub = GreeterStub(channel)
reply = await stub.SayHello(HelloRequest(name="World"))
print(reply.message)

async for reply in stub.SayHelloToMany(gen()):
print(reply.message)


if __name__ == "__main__":
curio.run(main)
curio.run(main) # Or trio.run(main)
```

You can mix server and client code, for example make a server that requests something using purerpc from another server, etc.
You can mix server and client code, for example make a server that requests something using purerpc from another gRPC server, etc.

More examples in `misc/` folder
4 changes: 3 additions & 1 deletion ci/run_tests_in_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
set -e

BASE_IMAGE="$1"
PURERPC_BACKEND="$2"
BUILD_TAG=${BASE_IMAGE//:/-}
BUILD_TAG=${BUILD_TAG//\//-}

docker build --build-arg BASE_IMAGE=${BASE_IMAGE} -t "standy/purerpc:${BUILD_TAG}" .
docker run -it "standy/purerpc:$BUILD_TAG" bash -c 'python setup.py test'
echo "Runnig tests with $PURERPC_BACKEND backend"
docker run -it -e PURERPC_BACKEND=${PURERPC_BACKEND} "standy/purerpc:$BUILD_TAG" bash -c 'python setup.py test'
34 changes: 34 additions & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = {
rules: {
'body-leading-blank': [1, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 72],
'scope-case': [2, 'always', 'lower-case'],
'subject-case': [
2,
'never',
['sentence-case', 'start-case', 'pascal-case', 'upper-case']
],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'type-enum': [
2,
'always',
[
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test'
]
]
}
};
File renamed without changes.
4 changes: 2 additions & 2 deletions misc/greeter/baseline_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import grpc

from greeter_pb2 import HelloRequest, HelloReply
from greeter_pb2_grpc import GreeterServicer, add_GreeterServicer_to_server
from generated.greeter_pb2 import HelloReply
from generated.greeter_pb2_grpc import GreeterServicer, add_GreeterServicer_to_server

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

Expand Down
22 changes: 10 additions & 12 deletions misc/greeter/client.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import curio
import time
from purerpc.client import Channel, Client
from greeter_pb2 import HelloRequest, HelloReply
from greeter_grpc import GreeterStub
from purerpc.utils import print_memory_growth_statistics
import purerpc
from generated.greeter_pb2 import HelloRequest
from generated.greeter_grpc import GreeterStub


async def worker(channel):
Expand All @@ -16,14 +15,13 @@ async def worker(channel):

async def main_coro():
# await curio.spawn(print_memory_growth_statistics(), daemon=True)
channel = Channel("localhost", 50055)
await channel.connect()
for i in range(100):
start = time.time()
async with curio.TaskGroup() as task_group:
for i in range(100):
await task_group.spawn(worker(channel))
print("RPS: {}".format(10000 / (time.time() - start)))
async with purerpc.insecure_channel("localhost", 50055) as channel:
for i in range(100):
start = time.time()
async with curio.TaskGroup() as task_group:
for i in range(100):
await task_group.spawn(worker(channel))
print("RPS: {}".format(10000 / (time.time() - start)))


def main():
Expand Down
4 changes: 2 additions & 2 deletions misc/greeter/client_grpcio.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import grpc
from greeter_pb2 import HelloRequest, HelloReply
from greeter_pb2_grpc import GreeterStub
from generated.greeter_pb2 import HelloRequest
from generated.greeter_pb2_grpc import GreeterStub


def main():
Expand Down
Empty file.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import purerpc
import greeter_pb2
import generated.greeter_pb2


class GreeterServicer(purerpc.Servicer):
Expand All @@ -25,35 +25,35 @@ def service(self) -> purerpc.Service:
self.SayHello,
purerpc.RPCSignature(
purerpc.Cardinality.UNARY_UNARY,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
service_obj.add_method(
"SayHelloGoodbye",
self.SayHelloGoodbye,
purerpc.RPCSignature(
purerpc.Cardinality.UNARY_STREAM,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
service_obj.add_method(
"SayHelloToMany",
self.SayHelloToMany,
purerpc.RPCSignature(
purerpc.Cardinality.STREAM_STREAM,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
service_obj.add_method(
"SayHelloToManyAtOnce",
self.SayHelloToManyAtOnce,
purerpc.RPCSignature(
purerpc.Cardinality.STREAM_UNARY,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
return service_obj
Expand All @@ -69,31 +69,31 @@ def __init__(self, channel):
"SayHello",
purerpc.RPCSignature(
purerpc.Cardinality.UNARY_UNARY,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
self.SayHelloGoodbye = self._client.get_method_stub(
"SayHelloGoodbye",
purerpc.RPCSignature(
purerpc.Cardinality.UNARY_STREAM,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
self.SayHelloToMany = self._client.get_method_stub(
"SayHelloToMany",
purerpc.RPCSignature(
purerpc.Cardinality.STREAM_STREAM,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
self.SayHelloToManyAtOnce = self._client.get_method_stub(
"SayHelloToManyAtOnce",
purerpc.RPCSignature(
purerpc.Cardinality.STREAM_UNARY,
greeter_pb2.HelloRequest,
greeter_pb2.HelloReply,
generated.greeter_pb2.HelloRequest,
generated.greeter_pb2.HelloReply,
)
)
Loading