protoc -I= <img src="https://www.zhihu.com/equation?tex=SRC_DIR%20--python_out%3D" alt="SRC_DIR --python_out=" class="ee_img tr_noresize" eeimg="1"> DST_DIR $SRC_DIR/addressbook.proto
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
- scalar type有默认值
- 对于新增的optional type,需要注意加
[default = value]
或者用has方法判断是否存在 - allow_alias: 允许alias,属性的数字相同
- reserved values
Importing Definitions
- 允许import proto2,但不能直接在proto3用proto2 syntax
- import public允许pb的替换
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
Nested Types
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
Any Type
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);
// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const Any& detail : status.details()) {
if (detail.Is<NetworkErrorDetails>()) {
NetworkErrorDetails network_error;
detail.UnpackTo(&network_error);
... processing network_error ...
}
}
Oneof Type
- Changing a single value into a member of a new oneof is safe and binary compatible. Moving multiple fields into a new oneof may be safe if you are sure that no code sets more than one at a time. Moving any fields into an existing oneof is not safe.
- 小心oneof出core,设了另一个field会把原先的删掉,不能再设原先的内部field
- Backwards-compatibility issues
Map Type
- When parsing from the wire or when merging, if there are duplicate map keys the last key seen is used. When parsing a map from text format, parsing may fail if there are duplicate keys.
- Backwards compatibility
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
Packages
- 相当于C++的namespace
foo.bar.Open
,从后往前搜索,先搜索bar再搜索foo,如果是.foo.bar.Open
,则从前往后搜索
package foo.bar;
message Open { ... }
message Foo {
...
foo.bar.Open open = 1;
...
}
RPC Service
- 定义
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
- client code
using google::protobuf;
protobuf::RpcChannel* channel;
protobuf::RpcController* controller;
SearchService* service;
SearchRequest request;
SearchResponse response;
void DoSearch() {
// You provide classes MyRpcChannel and MyRpcController, which implement
// the abstract interfaces protobuf::RpcChannel and protobuf::RpcController.
channel = new MyRpcChannel("somehost.example.com:1234");
controller = new MyRpcController;
// The protocol compiler generates the SearchService class based on the
// definition given above.
service = new SearchService::Stub(channel);
// Set up the request.
request.set_query("protocol buffers");
// Execute the RPC.
service->Search(controller, request, response, protobuf::NewCallback(&Done));
}
void Done() {
delete service;
delete channel;
delete controller;
}
- service code
using google::protobuf;
class ExampleSearchService : public SearchService {
public:
void Search(protobuf::RpcController* controller,
const SearchRequest* request,
SearchResponse* response,
protobuf::Closure* done) {
if (request->query() == "google") {
response->add_result()->set_url("http://www.google.com");
} else if (request->query() == "protocol buffers") {
response->add_result()->set_url("http://protobuf.googlecode.com");
}
done->Run();
}
};
int main() {
// You provide class MyRpcServer. It does not have to implement any
// particular interface; this is just an example.
MyRpcServer server;
protobuf::Service* service = new ExampleSearchService;
server.ExportOnPort(1234, service);
server.Run();
delete service;
return 0;
}
Options
google/protobuf/descriptor.proto
option optimize_for = CODE_SIZE; //SPEED(DEFAULT), LITE_RUNTIME
option cc_enable_arenas = true;
int32 old_field = 6 [deprecated = true];
Python API, Python Tutorial, Python Pb Guide
import addressbook_pb2
person = addressbook_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phones.add()
phone.number = "555-4321"
phone.type = addressbook_pb2.Person.HOME
- Wrapping protocol buffers is also a good idea if you don't have control over the design of the
.proto
file - You should never add behaviour to the generated classes by inheriting from them
# serialize proto object
import os
out_dir = "proto_dump"
with open(os.path.join(out_dir, "person.pb"), "wb") as f:
# binary output
f.write(person.SerializeToString())
with open(os.path.join(out_dir, "person.protobuf"), "w") as f:
# human-readable output for debugging
# by default, entries with a value of 0 are never printed
f.write(str(person))
python动态解析oneof字段
data = getattr(config, config.WhichOneof('config')).value
import pathlib
import os
from subprocess import check_call
def generate_proto_code():
proto_interface_dir = "./src/interfaces"
generated_src_dir = "./src/generated/"
out_folder = "src"
if not os.path.exists(generated_src_dir):
os.mkdir(generated_src_dir)
proto_it = pathlib.Path().glob(proto_interface_dir + "/**/*")
proto_path = "generated=" + proto_interface_dir
protos = [str(proto) for proto in proto_it if proto.is_file()]
check_call(["protoc"] + protos + ["--python_out", out_folder, "--proto_path", proto_path])
from setuptools.command.develop import develop
from setuptools import setup, find_packages
class CustomDevelopCommand(develop):
"""Wrapper for custom commands to run before package installation."""
uninstall = False
def run(self):
develop.run(self)
def install_for_development(self):
develop.install_for_development(self)
generate_proto_code()
setup(
name='testpkg',
version='1.0.0',
package_dir={'': 'src'},
cmdclass={
'develop': CustomDevelopCommand, # used for pip install -e ./
},
packages=find_packages(where='src')
)