pythonでgRPCを使用する場合、protocolbuffer(pb2)型を使用することになります。
このprotocolbuffer型は扱う際に注意する点がいくつかあったので記事にまとめます。
CopyFromメソッドを使用したprotocolbuffer型の代入処理
例えば、以下のようなproto定義があるとします。メソッド1つのシンプル設計です。
service UserService {
rpc GetUser(GetUserRequest) returns (User) {}
}
message Size {
int32 height = 1;
int32 weight = 2;
}
message User {
string id = 1;
string name = 2;
Size size = 3;
}
message GetUserRequest {
string user_id = 1;
}
以下は上記のprotoファイルを使用したUserServiceのサーバーサイド実装例です。
import user_pb2
class UserService():
def GetUser(self, request, context):
# dbのユーザをget
db_user = db.get(request.user_id)
# sizeを定義し、値を追加
size = user_pb2.Size()
size.height = db_user.height
size.weight = db_user.weight
# userを定義し、値を追加して返却
user = user_pb2.User()
user.id = db_user.id
user.name = db_user.name
user.size = size
return user
上記のGetUserメソッドを実行すると、TypeErrorでエラーとなってしまいます。
どこが問題かというと、コード最後から2番目の行user.size = size
の部分です。
Size型のオブジェクト型であるsizeはUser型で定義されているsizeの型と同じですが、そのまま代入するとエラーとなってしまいます。
ここで使用するのがCopyFromというメソッドです。
上記のコードの問題箇所を以下のように書き換えることでTypeErrorなくコードが実行されます。
import user_pb2
class UserService():
def GetUser(self, request, context):
# dbのユーザをget
db_user = db.get(request.user_id)
# sizeを定義し、値を追加
size = user_pb2.Size()
size.height = db_user.height
size.weight = db_user.weight
# userを定義し、値を追加して返却
user = user_pb2.User()
user.id = db_user.id
user.name = db_user.name
user.size.CopyFrom(size)
return user
Typeメソッドを使って比較してもsizeオブジェクトはSize型なので気付きにくいですが、protocolbuffer型として代入する際はCopyFromを使用しなければ正しく代入できません。
Extendを使用したrepeatedな型の代入
repeatedな定義でも代入時に気をつける点があります。
前のUserServiceを書き換えた次のprotoファイルを使用して説明します。
service UserService {
rpc GetUser(GetUserRequest) returns (User) {}
}
message Item {
string id = 1;
string name = 2;
}
message User {
string id = 1;
string name = 2;
repeated Item items = 3;
}
message GetUserRequest {
string user_id = 1;
}
sizeをitemsというrepeatedな型に変えました。以下がUserServiceのコードです。
import user_pb2
class UserService():
def GetUser(self, request, context):
# dbのユーザをget
db_user = db.get(request.user_id)
# itemを複数定義し、itemsリストを作成
item1 = user_pb2.Item()
item1.id = '1'
item1.name = 'item1'
item2 = user_pb2.Item()
item2.id = '2'
item2.name = 'item2'
items = [item1, item2]
# userを定義し、値を追加して返却
user = user_pb2.User()
user.id = db_user.id
user.name = db_user.name
user.items = items
return user
上記のコードを実行すると、やはりTypeErrorが発生します。問題箇所は同じくuser.items = items
の部分です。
repeatedなフィールドに代入する場合はextendメソッドを使用します。
user.items.extend(items)
上記のようにextendを使用することで配列の型に値を設定することが可能になります。
なぜこのように扱う必要があるのかはgoogleの公式ページ等で確認しましたが、いまいち説明されていませんでした。
protocolbufferはPython上だと癖がある感じなので注意して使っていきましょう!