If you use gRPC with python, you will be using the protocolbuffer (pb2) type. There are some points to be aware of when dealing with this protocolbuffer type, so I will summarize it in the article.
For example, if you have the following proto definition: Simple design with one method.
user.proto
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;
}
The following is a server-side implementation example of UserService using the above proto file.
import user_pb2
class UserService():
def GetUser(self, request, context):
#get the user of db
db_user = db.get(request.user_id)
#Define size and add value
size = user_pb2.Size()
size.height = db_user.height
size.weight = db_user.weight
#Define user, add value and return
user = user_pb2.User()
user.id = db_user.id
user.name = db_user.name
user.size = size
return user
When I execute the above GetUser method, I get an error with TypeError. The problem is the penultimate line of code ʻuser.size = size`.
The size, which is an object type of Size type, is the same as the size type defined in User type, but if you assign it as it is, an error will occur. The method used here is CopyFrom. By rewriting the problem part of the above code as follows, the code will be executed without TypeError.
import user_pb2
class UserService():
def GetUser(self, request, context):
#get the user of db
db_user = db.get(request.user_id)
#Define size and add value
size = user_pb2.Size()
size.height = db_user.height
size.weight = db_user.weight
#Define user, add value and return
user = user_pb2.User()
user.id = db_user.id
user.name = db_user.name
user.size.CopyFrom(size)
return user
Even if you compare using the Type method, it is difficult to notice because the size object is a Size type, but when assigning it as a protocol buffer type, you can not assign it correctly unless you use CopyFrom.
Even with repeated definitions, there are some points to be aware of when assigning. This is explained using the following proto file, which is a rewrite of the previous UserService.
User.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;
}
I changed size to a repeated type called items. Below is the UserService code.
import user_pb2
class UserService():
def GetUser(self, request, context):
#get the user of db
db_user = db.get(request.user_id)
#Define multiple items and create an items list
item1 = user_pb2.Item()
item1.id = '1'
item1.name = 'item1'
item2 = user_pb2.Item()
item2.id = '2'
item2.name = 'item2'
items = [item1, item2]
#Define user, add value and return
user = user_pb2.User()
user.id = db_user.id
user.name = db_user.name
user.items = items
return user
When I run the above code, I still get a TypeError. The problem is also ʻuser.items = items`. Use the extend method to assign to a repeated field.
user.items.extend(items)
It is possible to set the value to the array type by using extend as described above.
I checked on google's official page etc. why it is necessary to handle it like this, but it was not explained well. Protocol buffer seems to have a habit on Python, so use it with caution!
Recommended Posts