pyease-grpc


Namepyease-grpc JSON
Version 1.7.0 PyPI version JSON
download
home_pagehttps://github.com/dipu-bd/pyease-grpc
SummaryEasy gRPC-web client in python
upload_time2024-08-12 18:31:23
maintainerNone
docs_urlNone
authorSudipto Chandra
requires_pythonNone
licenseNone
keywords grpc protobuf grpc-web requests
VCS
bugtrack_url
requirements requests protobuf grpcio
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pyease-grpc

[![Build](https://github.com/dipu-bd/pyease-grpc/actions/workflows/commit.yml/badge.svg)](https://github.com/dipu-bd/pyease-grpc/actions/workflows/commit.yml)
[![Release](https://github.com/dipu-bd/pyease-grpc/actions/workflows/release.yml/badge.svg)](https://github.com/dipu-bd/pyease-grpc/actions/workflows/release.yml)
[![PyPI version](https://img.shields.io/pypi/v/pyease-grpc.svg?logo=python)](https://pypi.org/project/pyease-grpc)
[![Python version](https://img.shields.io/pypi/pyversions/pyease-grpc.svg)](https://pypi.org/project/pyease-grpc)
[![GitHub License](https://img.shields.io/github/license/dipu-bd/pyease-grpc)](https://github.com/dipu-bd/pyease-grpc/blob/master/LICENSE)
[![Downloads](https://pepy.tech/badge/pyease-grpc/month)](https://pepy.tech/project/pyease-grpc)

Easy to use gRPC-web client in python

### Installation

Install the package using:

```
$ pip install pyease-grpc
```

Run the following to check if it has been installed correctly:

```
$ pyease-grpc --version
```

## Tutorial

> Before you start, you need to have a basic understanding of [how gRPC works](https://grpc.io/docs/what-is-grpc/introduction/).

This package provides a `requests` like interface to make calls to native gRPC and gRPC-Web servers.

### Example Server

An example server and client can be found in the `example` folder.

```
> cd example
> docker compose up
```

It uses two ports:

- Native gRPC server: `localhost:50050`
- gRPC-Web server using envoy: `http://localhost:8080`

You can test the native serve with the client:

```
$ python example/server/client.py
Calling SayHello:
reply: "Hello, world!"

Calling LotsOfReplies:
reply: "Hello, world no. 0!"
reply: "Hello, world no. 1!"
reply: "Hello, world no. 2!"
reply: "Hello, world no. 3!"
reply: "Hello, world no. 4!"

Calling LotsOfGreetings:
reply: "Hello, A, B, C!"

Calling BidiHello:
reply: "Hello, A!"
reply: "Hello, B!"
reply: "Hello, C!"
```

### Loading the Protobuf

The proto file is located at `example/server/abc.proto`

```proto
// file: example/server/abc.proto
syntax = "proto3";

package pyease.sample.v1;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse);
  rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
  rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
  rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string reply = 1;
}
```

You can directly load this file using `pyease_grpc` without generating any stubs:

```py
from pyease_grpc import Protobuf

protobuf = Protobuf.from_file("example/server/abc.proto")
```

Internally, it converts the proto file into `FileDescriptorSet` message.

It is recommended to use the `FileDescriptorSet` json to load the `Protobuf` faster.

To generate the `FileDescriptorSet` json from a proto file:

```
$ pyease-grpc -I example/server example/server/abc.proto --output abc_fds.json
```

Now you can use this descriptor file directly to create a `Protobuf` instance.

```py
protobuf = Protobuf.restore_file('abc_fds.json')
```

### Getting response from gRPC-Web

For **Unary RPC** request:

```py
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
response = session.request(
    RpcUri(
      base_url="http://localhost:8080",
      package="pyease.sample.v1",
      service="Greeter",
      method="SayHello",
    ),
    {
      "name": "world"
    },
)
response.raise_for_status()

print(response.single['reply'])
```

For a **Server-side Streaming RPC** request:

```py
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
response = session.request(
    RpcUri(
      base_url="http://localhost:8080",
      package="pyease.sample.v1",
      service="Greeter",
      method="LotsOfReplies",
    ),
    {
      "name": "world",
    },
)
response.raise_for_status()

for payload in response.iter_payloads():
    print(payload["reply"])
```

> gRPC-Web currently supports 2 RPC modes: Unary RPCs, Server-side Streaming RPCs.
> Client-side and Bi-directional streaming is not currently supported.

### Using the native gRPC protocol

You can also directly call a method using the native gRPC protocol.

For **Unary RPC** request:

```py
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
response = session.call(
    RpcUri(
      base_url="localhost:50050",
      package="pyease.sample.v1",
      service="Greeter",
      method="SayHello",
    ),
    {
      "name": "world",
    }
)

print(response.single["reply"])
print(response.payloads)
```

For a **Server-side Streaming RPC** request:

```py
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
response = session.call(
    RpcUri(
      base_url="localhost:50050",
      package="pyease.sample.v1",
      service="Greeter",
      method="LotsOfReplies",
    ),
    {
      "name": "world",
    },
)

for payload in response.iter_payloads():
    print(payload["reply"])
print(response.payloads)
```

For a **Client-Side Streaming RPC** request:

```py
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
response = session.call(
    RpcUri(
      base_url="localhost:50050",
      package="pyease.sample.v1",
      service="Greeter",
      method="LotsOfGreetings",
    ),
    iter(
      [
        {"name": "A"},
        {"name": "B"},
        {"name": "C"},
      ]
    ),
)

print(response.single["reply"])
print(response.payloads)
```

For a **Bidirectional Streaming RPC** request:

```py
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
response = session.call(
    RpcUri(
      base_url="localhost:50050",
      package="pyease.sample.v1",
      service="Greeter",
      method="BidiHello",
    ),
    iter(
      [
        {"name": "A"},
        {"name": "B"},
        {"name": "C"},
      ]
    ),
)

for payload in response.iter_payloads():
    print(payload["reply"])
print(response.payloads)
```

### Error Handling

Errors are raised as soon as they appear.

List of errors that can appear during `request`:

- `ValueError`: If the requested method, service or package is not found
- `requests.exceptions.InvalidHeader`: If the header of expected length is not found
- `requests.exceptions.ContentDecodingError`: If the data of expected length is not found
- `NotImplementedError`: If compression is enabled in the response headers
- `grpc.RpcError`: If the grpc-status is non-zero

List of errors that can appear during `call`:

- `ValueError`: If the requested method, service or package is not found
- `grpc.RpcError`: If the grpc-status is non-zero

To get the `grpc-status` and `grpc-message`, you can add a try-catch to your call. e.g.:

```py
import grpc
from pyease_grpc import RpcSession, RpcUri

session = RpcSession.from_file("example/server/abc.proto")
rpc_uri = RpcUri(
  base_url="localhost:50050",
  package="pyease.sample.v1",
  service="Greeter",
  method="SayHello",
)
try:
  response = session.call(rpc_uri, {"name": "error"})
  print(response.single["reply"])
except grpc.RpcError as e:
  print('grpc status', e.code())
  print('grpc message', e.details())
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/dipu-bd/pyease-grpc",
    "name": "pyease-grpc",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "grpc, protobuf, grpc-web, requests",
    "author": "Sudipto Chandra",
    "author_email": "dipu.sudipta@gmail.com",
    "download_url": null,
    "platform": null,
    "description": "# pyease-grpc\n\n[![Build](https://github.com/dipu-bd/pyease-grpc/actions/workflows/commit.yml/badge.svg)](https://github.com/dipu-bd/pyease-grpc/actions/workflows/commit.yml)\n[![Release](https://github.com/dipu-bd/pyease-grpc/actions/workflows/release.yml/badge.svg)](https://github.com/dipu-bd/pyease-grpc/actions/workflows/release.yml)\n[![PyPI version](https://img.shields.io/pypi/v/pyease-grpc.svg?logo=python)](https://pypi.org/project/pyease-grpc)\n[![Python version](https://img.shields.io/pypi/pyversions/pyease-grpc.svg)](https://pypi.org/project/pyease-grpc)\n[![GitHub License](https://img.shields.io/github/license/dipu-bd/pyease-grpc)](https://github.com/dipu-bd/pyease-grpc/blob/master/LICENSE)\n[![Downloads](https://pepy.tech/badge/pyease-grpc/month)](https://pepy.tech/project/pyease-grpc)\n\nEasy to use gRPC-web client in python\n\n### Installation\n\nInstall the package using:\n\n```\n$ pip install pyease-grpc\n```\n\nRun the following to check if it has been installed correctly:\n\n```\n$ pyease-grpc --version\n```\n\n## Tutorial\n\n> Before you start, you need to have a basic understanding of [how gRPC works](https://grpc.io/docs/what-is-grpc/introduction/).\n\nThis package provides a `requests` like interface to make calls to native gRPC and gRPC-Web servers.\n\n### Example Server\n\nAn example server and client can be found in the `example` folder.\n\n```\n> cd example\n> docker compose up\n```\n\nIt uses two ports:\n\n- Native gRPC server: `localhost:50050`\n- gRPC-Web server using envoy: `http://localhost:8080`\n\nYou can test the native serve with the client:\n\n```\n$ python example/server/client.py\nCalling SayHello:\nreply: \"Hello, world!\"\n\nCalling LotsOfReplies:\nreply: \"Hello, world no. 0!\"\nreply: \"Hello, world no. 1!\"\nreply: \"Hello, world no. 2!\"\nreply: \"Hello, world no. 3!\"\nreply: \"Hello, world no. 4!\"\n\nCalling LotsOfGreetings:\nreply: \"Hello, A, B, C!\"\n\nCalling BidiHello:\nreply: \"Hello, A!\"\nreply: \"Hello, B!\"\nreply: \"Hello, C!\"\n```\n\n### Loading the Protobuf\n\nThe proto file is located at `example/server/abc.proto`\n\n```proto\n// file: example/server/abc.proto\nsyntax = \"proto3\";\n\npackage pyease.sample.v1;\n\nservice Greeter {\n  rpc SayHello (HelloRequest) returns (HelloResponse);\n  rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);\n  rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);\n  rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);\n}\n\nmessage HelloRequest {\n  string name = 1;\n}\n\nmessage HelloResponse {\n  string reply = 1;\n}\n```\n\nYou can directly load this file using `pyease_grpc` without generating any stubs:\n\n```py\nfrom pyease_grpc import Protobuf\n\nprotobuf = Protobuf.from_file(\"example/server/abc.proto\")\n```\n\nInternally, it converts the proto file into `FileDescriptorSet` message.\n\nIt is recommended to use the `FileDescriptorSet` json to load the `Protobuf` faster.\n\nTo generate the `FileDescriptorSet` json from a proto file:\n\n```\n$ pyease-grpc -I example/server example/server/abc.proto --output abc_fds.json\n```\n\nNow you can use this descriptor file directly to create a `Protobuf` instance.\n\n```py\nprotobuf = Protobuf.restore_file('abc_fds.json')\n```\n\n### Getting response from gRPC-Web\n\nFor **Unary RPC** request:\n\n```py\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nresponse = session.request(\n    RpcUri(\n      base_url=\"http://localhost:8080\",\n      package=\"pyease.sample.v1\",\n      service=\"Greeter\",\n      method=\"SayHello\",\n    ),\n    {\n      \"name\": \"world\"\n    },\n)\nresponse.raise_for_status()\n\nprint(response.single['reply'])\n```\n\nFor a **Server-side Streaming RPC** request:\n\n```py\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nresponse = session.request(\n    RpcUri(\n      base_url=\"http://localhost:8080\",\n      package=\"pyease.sample.v1\",\n      service=\"Greeter\",\n      method=\"LotsOfReplies\",\n    ),\n    {\n      \"name\": \"world\",\n    },\n)\nresponse.raise_for_status()\n\nfor payload in response.iter_payloads():\n    print(payload[\"reply\"])\n```\n\n> gRPC-Web currently supports 2 RPC modes: Unary RPCs, Server-side Streaming RPCs.\n> Client-side and Bi-directional streaming is not currently supported.\n\n### Using the native gRPC protocol\n\nYou can also directly call a method using the native gRPC protocol.\n\nFor **Unary RPC** request:\n\n```py\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nresponse = session.call(\n    RpcUri(\n      base_url=\"localhost:50050\",\n      package=\"pyease.sample.v1\",\n      service=\"Greeter\",\n      method=\"SayHello\",\n    ),\n    {\n      \"name\": \"world\",\n    }\n)\n\nprint(response.single[\"reply\"])\nprint(response.payloads)\n```\n\nFor a **Server-side Streaming RPC** request:\n\n```py\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nresponse = session.call(\n    RpcUri(\n      base_url=\"localhost:50050\",\n      package=\"pyease.sample.v1\",\n      service=\"Greeter\",\n      method=\"LotsOfReplies\",\n    ),\n    {\n      \"name\": \"world\",\n    },\n)\n\nfor payload in response.iter_payloads():\n    print(payload[\"reply\"])\nprint(response.payloads)\n```\n\nFor a **Client-Side Streaming RPC** request:\n\n```py\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nresponse = session.call(\n    RpcUri(\n      base_url=\"localhost:50050\",\n      package=\"pyease.sample.v1\",\n      service=\"Greeter\",\n      method=\"LotsOfGreetings\",\n    ),\n    iter(\n      [\n        {\"name\": \"A\"},\n        {\"name\": \"B\"},\n        {\"name\": \"C\"},\n      ]\n    ),\n)\n\nprint(response.single[\"reply\"])\nprint(response.payloads)\n```\n\nFor a **Bidirectional Streaming RPC** request:\n\n```py\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nresponse = session.call(\n    RpcUri(\n      base_url=\"localhost:50050\",\n      package=\"pyease.sample.v1\",\n      service=\"Greeter\",\n      method=\"BidiHello\",\n    ),\n    iter(\n      [\n        {\"name\": \"A\"},\n        {\"name\": \"B\"},\n        {\"name\": \"C\"},\n      ]\n    ),\n)\n\nfor payload in response.iter_payloads():\n    print(payload[\"reply\"])\nprint(response.payloads)\n```\n\n### Error Handling\n\nErrors are raised as soon as they appear.\n\nList of errors that can appear during `request`:\n\n- `ValueError`: If the requested method, service or package is not found\n- `requests.exceptions.InvalidHeader`: If the header of expected length is not found\n- `requests.exceptions.ContentDecodingError`: If the data of expected length is not found\n- `NotImplementedError`: If compression is enabled in the response headers\n- `grpc.RpcError`: If the grpc-status is non-zero\n\nList of errors that can appear during `call`:\n\n- `ValueError`: If the requested method, service or package is not found\n- `grpc.RpcError`: If the grpc-status is non-zero\n\nTo get the `grpc-status` and `grpc-message`, you can add a try-catch to your call. e.g.:\n\n```py\nimport grpc\nfrom pyease_grpc import RpcSession, RpcUri\n\nsession = RpcSession.from_file(\"example/server/abc.proto\")\nrpc_uri = RpcUri(\n  base_url=\"localhost:50050\",\n  package=\"pyease.sample.v1\",\n  service=\"Greeter\",\n  method=\"SayHello\",\n)\ntry:\n  response = session.call(rpc_uri, {\"name\": \"error\"})\n  print(response.single[\"reply\"])\nexcept grpc.RpcError as e:\n  print('grpc status', e.code())\n  print('grpc message', e.details())\n```\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Easy gRPC-web client in python",
    "version": "1.7.0",
    "project_urls": {
        "Homepage": "https://github.com/dipu-bd/pyease-grpc"
    },
    "split_keywords": [
        "grpc",
        " protobuf",
        " grpc-web",
        " requests"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7026f1437aedd58397cc3b572cedeb10e2f565fd508120a4b47dbcaacea586d9",
                "md5": "7b68967ad340f526af8d6f8493b01c23",
                "sha256": "279047e03f21f4b65ebe251d527504a8f64d84ccfa028b50bba0e7315321f05e"
            },
            "downloads": -1,
            "filename": "pyease_grpc-1.7.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7b68967ad340f526af8d6f8493b01c23",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 16056,
            "upload_time": "2024-08-12T18:31:23",
            "upload_time_iso_8601": "2024-08-12T18:31:23.046928Z",
            "url": "https://files.pythonhosted.org/packages/70/26/f1437aedd58397cc3b572cedeb10e2f565fd508120a4b47dbcaacea586d9/pyease_grpc-1.7.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-12 18:31:23",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dipu-bd",
    "github_project": "pyease-grpc",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "requests",
            "specs": [
                [
                    ">=",
                    "2.25.0"
                ]
            ]
        },
        {
            "name": "protobuf",
            "specs": [
                [
                    ">=",
                    "3.19.0"
                ]
            ]
        },
        {
            "name": "grpcio",
            "specs": []
        }
    ],
    "lcname": "pyease-grpc"
}
        
Elapsed time: 0.97054s