LoginSignup
0
0

More than 3 years have passed since last update.

gRPC Pythonのコード生成で、相対参照に近い意図したパッケージとして生成する

Last updated at Posted at 2021-05-09

PythonでgRPCを使う場合には、grpcio-toolsパッケージを使いますが、この使い方で目的のパッケージの中にあるコードとして生成しようとした時に、ハマったので解説します。

実現したいこと

用意した定義ファイルはこちら

proto/gisapp.proto
syntax = "proto3";

package proto;

service GISCalc {
  rpc RouteLength(RouteLengthRequest) returns (RouteLengthResponse) {}
}

message Point {
  double latitude = 1;
  double lognitude = 2;
}

message RouteLengthRequest { repeated Point route = 1; }

message RouteLengthResponse { double length = 1; }

ここから、以下のように使えるように proto パッケージの中に生成したコードを収めたいとします。

test.py
import grpc
from proto import gisapp_pb2_grpc, gisapp_pb2

channel = grpc.insecure_channel('localhost:50051')
client = gisapp_pb2_grpc.GISCalcStub(channel)
req = gisapp_pb2.RouteLengthRequest()
res = client.RouteLength()

つまり、以下のようなディレクトリ構成にします。

`- proto
   |- gisapp_pb2.py
   `- gisapp_pb2_grpc.py

どうすればよいか

パッケージにしたいディレクトリの中に、定義ファイルを置きます。

`- proto
   `- gisapp.proto

ルートディレクトリにて、以下のように実行します。

python -m grpc_tools.protoc \
    -I=. \
    --python_out=. \
    --grpc_python_out=. \
    proto/gisapp.proto

すると、以下のような、protoパッケージ下にあるプログラムとして生成されます。

proto/gisapp_pb_grpc.py
import grpc

from proto import gisapp_pb2 as proto_dot_gisapp__pb2

...

解説

パッケージ構成は、-Iで指定したフォルダから、定義ファイル gisapp.proto までのディレクトリ構成になります。以下のような関係になります。

  • -I./proto proto/gisapp.protoimport gisapp_pb2
  • -I. proto/gisapp.protofrom proto import gisapp_pb2

次に、出力先に引数である --python_out--grpc_python_out は、指定したパスをルートとするパッケージとして出力されます。

  • -I./proto proto/gisapp.protoかつ
    • --python_out=./protoproto/proto/gisapp_pb2.pyimport gisapp_pb2 が作られる
    • --python_out=.proto/gisapp_pb2.pyimport gisapp_pb2
  • -I. proto/gisapp.proto かつ
    • --python_out=./protoproto/proto/gisapp_pb2.pyfrom proto import gisapp_pb2 が作られる
    • --python_out=.proto/gisapp_pb2.pyfrom proto import gisapp_pb2 が作られる

よって、前述のコマンドのとおりになります。

ところで

betterproto というツールで生成したところ、どこのパッケージ下にもおける1ファイルが生成されるようになったので、上記のようなパッケージの問題は消失しました。

pip install betterproto[compiler]==2.0.0b3

python -m grpc_tools.protoc \
    -I=. \
    --python_betterproto_out=. \
    proto/gisapp.proto
proto/__init__.py
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# sources: proto/gisapp.proto
# plugin: python-betterproto
from dataclasses import dataclass
from typing import Dict, List, Optional

import betterproto
from betterproto.grpc.grpclib_server import ServiceBase
import grpclib


@dataclass(eq=False, repr=False)
class Point(betterproto.Message):
    latitude: float = betterproto.double_field(1)
    longitude: float = betterproto.double_field(2)


@dataclass(eq=False, repr=False)
class RouteLengthRequest(betterproto.Message):
    route: List["Point"] = betterproto.message_field(1)


@dataclass(eq=False, repr=False)
class RouteLengthResponse(betterproto.Message):
    length: float = betterproto.double_field(1)


class GisCalcStub(betterproto.ServiceStub):
    async def route_length(
        self, *, route: Optional[List["Point"]] = None
    ) -> "RouteLengthResponse":
        ...


class GisCalcBase(ServiceBase):
    async def route_length(
        self, route: Optional[List["Point"]]
    ) -> "RouteLengthResponse":
        ...
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0