# Protocol buffer
###### tags: `protobuf`,`gRPC`,`kubernetes`
[Toc]
## Compile protobuf from source
> [protobuf C++ installation](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md)
> [Compile Warning: LD_LIBRAY_PATH](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/B1lFb_TvP#%E5%BB%B6%E4%BC%B8%E9%96%B1%E8%AE%80-LD_LIBRARY_PATH%E7%82%BA%E4%BD%95%E4%B8%8D%E5%A5%BD)
1. To do
3. 折衷方案: Use Wrapper script for application only
``` bash
env LD_LIBRARY_PATH=/opt/protobuf-3.17.3/lib ./bin/jupyter-notebook
env LD_LIBRARY_PATH=/opt/protobuf-3.17.3/lib python3
```
## protocol buffer syntax(proto2)
> ref:
> [protocol buffer syntax guide](https://developers.google.com/protocol-buffers/docs/overview)
1. 宣告syntax必須是開頭如:
``` proto
syntax = "proto2"
// syntax = "proto3"
package tutorial
```
### 宣告每個 field 的 rule
3. `field`被宣告**rules**
- `required`
* 完整合格的訊息必須包含正好一個此field,否則此message會被當成"uninitialized",當`protoc`欲序列化(serialize)一個"uninitialized"的message,將會有exception
* 基於以下兩種情況,請謹慎使用:
1. `required`是永久的!! 原本是`required`的field欲改成`optional`,試想發送方已做此改變,但早些時間的reader會認為此`message`怎麼會缺少這個field? 故會不認可此訊息. 實務上若真有此需求,不應該更改這field的rule,反而需在application層增加任何驗證protocal buffer的實踐
2. 如果此field正好是`enum`,任何一方在protocal buffer對此`enum`新增了新的選項,當`message`裡包含新的`enum`的選項,會被當做"此field遺失",造成"required value fail"
* `proto3` **已經不支援required用法**,且google內部在`proto2`也不傾向使用required,都用optional或repeated
- `optional`
* 完整合格的訊息包含0個或正好一個此field
* 當massage中的`optional` field正好沒給(即0個此field),則原先定義的default value將被採用:
``` proto
optional int32 result_per_page = 3 [default = 10];
// result_per_page 沒給,則就用10
```
* 如果`optional` field的default value也沒給,則依照不同field type給定default value:
* `string`: empty string
* `bytes`: empty byte string
* `bools`: false
* numeric types: 0
* `enum`: enum list的第一個選項
* embbeded message: the "default instance" or "prototype" of the message, which has none of its fields set. (embedded message如下結構)
``` proto
message Person {
.....
message PhoneNumber {
.....
}
optional PhoneNumber phones = 4
}
```
* Calling the accessor to get the value of an optional (or required) field which has not been explicitly set always returns that field's default value.
- `repeated`
* 完整合格的訊息包含0個或任何次數的此field,且會保留順序(可以把`repeated`欄位想像成可變大小的array)
* 其中`repeated scalar type`需要加上`[packed=true]`,encoding才會更有效率,原因詳[此](https://developers.google.com/protocol-buffers/docs/encoding#packed),如下
``` proto
repeated int32 samples = 4 [packed=true];
repeated ProtoEnum results = 5 [packed=true];
```
### 每個 field 可以有哪些 type ?
1. `field`可以有哪些type?
- [scalar types](https://developers.google.com/protocol-buffers/docs/proto#scalar)
- string
- [message](https://developers.google.com/protocol-buffers/docs/proto#other) 一個定義好的message被別的message拿來當成typed field
``` proto
message SearchResponse {
repeated Result result = 1;
}
message Result {
required string url = 1;
optional string title = 2;
repeated string snippets =3;
}
```
- [composite type: enum](https://developers.google.com/protocol-buffers/docs/proto#enum)
* pre-defined list,界定此field只能在這幾個選項中選擇
* 給每個選項一個編號(Enumerator constants),且如果想要給不同選項同個編號的話,要開啟`option allow_alias = true`,否則報錯
* 編號在32-bit integer範圍內,不推薦使用負數,原因詳[此:varint encoding](https://developers.google.com/protocol-buffers/docs/encoding)
``` proto
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3 [default = 10];
// 先創建enum type
enum Corpus {
option allow_alias = true;
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
// 同樣編號不同選項
ALIAS_VEDIO = 6;
}
// 使用enum type
optional Corpus corpus = 4 [default = UNIVERSAL];
}
```
* 如果在一 `message{ }` 內定義了 `enum`,而想要在"外面"另一個`message{ }`內使用這個`enum`可以使用下例的語法:
``` proto
message Person {
optional string foo = 1;
enum BarType {
A_kind = 0;
B_kind = 1;
C_kind = 2;
}
optional BarType person_bar_type = 2;
}
message Other {
optional Person.BarType other_bar_type = 1
}
```
### field 自訂名稱, field 被分配 unique number
1. `field`自訂名稱
1. `field`被分配**unique number**
- 此數字是用來是以[message binary format](https://developers.google.com/protocol-buffers/docs/encoding)來識別各個field
- 一旦決定後就不能更動
- 1~15只要用1 byte就能encode(包含field的number與type,此部分詳[protocol buffer encoding](https://developers.google.com/protocol-buffers/docs/encoding#structure)),所以此範圍的數字最好留給常出現的field. 16~2047需要用2 bytes
- (不懂)Each element in a repeated field requires re-encoding the tag number, so repeated fields are particularly good candidates for this optimization.
- unique number最小用1, 最大用 $2^{29}$-1(536870911)
- 19000~19999保留給protocol buffer內部使用
- 用[reserved](https://developers.google.com/protocol-buffers/docs/proto#reserved)留起來的數字也不能用
``` proto
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
```
- 似乎每個`message{ }`內視為一個namespace,每個namespace都是從1開始,所以碰上embedded message加上多個message,大概長這樣:
``` proto
message Person {
optional string foo = 1;
optional string bar = 2;
message {
optional string a = 1;
optional string b = 2;
}
repeated string baz = 3;
}
message List {
repeated Person people = 1;
}
```
### 何謂一個`message{ }`?
* 是一個集合,裡面放多個或一個typed field
* 一個```.proto```可以有多個`message{ }`
* `message{ }`可以彼此[nested](https://developers.google.com/protocol-buffers/docs/proto#nested),不管幾層都行沒限制.
- [Deprecated: Nested Type,groups]([https://developers.google.com/protocol-buffers/docs/proto#nested](https://developers.google.com/protocol-buffers/docs/proto#groups))
- 且同一`message{ }`內的field的unique number編號,似乎有自己的namespace? (編號重新開始)
- 定義好的`message type`,可以用作別的 `message{ }` 內的typed field
- 且如果要在 parent message 外使用其 child message, 可用此語法 `_Parent_._Type_`
``` proto
message Person {
optional string name = 1;
optional int32 id = 2;
optional string e_mail = 3;
enum PhopeType {
HOME = 0;
WORK = 1;
CELL = 2;
}
// 在一個message內nested另一個message
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = HOME];
}
// 被當成使用typed field,注意編號4
repeated PhoneNumber phones = 4;
}
// 多個message type
message AddressBook {
// 在一個message內nested另一個message
repeated Person people = 1;
}
// 在parent message外使用其child message
message TrivalMessage {
optional Person.PhoneNumber phone_without_owner = 1;
}
```
### 如何安全的[updating a message{ } ](https://developers.google.com/protocol-buffers/docs/proto#updating)
* 不要修改已經有的field的nuique number
* 如果要新加field進message中,記得rule都要選`optional`或者`repeated`,且要特別注意其default value的設置. 如此舊的message format可以被新的parsed(因為沒有用`required`);新的message也可以被舊的message format辨認(視新加的field為0次即可)
* 非`required`的field可以移除,只是**其field unique number 不能在被用了**,善用`reserved`將已被移除的field的unique number保留起來,並且修改field的name,前面增加上prefix,如:`OBSOLETE_`
``` proto
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
```
* 以下集合內的field type可以互換
1. int32, uint32, int64, uint64, bool
2. sint32, sint64
3. string, bytes
4. (不懂) Embedded messages are compatible with bytes if the bytes contain an encoded version of the message.
5. fixed32, sfixed32
6. fixed64, sfixed64
7. 如果Field type是 string,bytes與message,則`optional`與`repeated`可以互換
* TO DO
### 使用 [Oneof](https://developers.google.com/protocol-buffers/docs/proto#oneof)
### 使用 [Map](https://developers.google.com/protocol-buffers/docs/proto#maps)
### gdgfg
- `service`
- [imporing definition](https://developers.google.com/protocol-buffers/docs/proto#importing_definitions): 還不懂
6. 使用 [oneof](https://developers.google.com/protocol-buffers/docs/proto#oneof) 限制多個`optional` field的行為
### define a RPC services in `.proto`
1. [plug in your own existing RPC system](https://developers.google.com/protocol-buffers/docs/proto#services)
2. Use [gRPC](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/SkUekpri_)
### 如何註解?
8. 新增註解:
- `/* multiple lines comment */`
- `/ one line command /`
- 延伸閱讀
* [使用awk移除 c-style 註解/\*...*/ ](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/SJyZH4DSv#Plain-getline-remove-C-style-comment%E2%80%99%E2%80%A6%E2%80%99-from-input)
# [Lab]透過protocol buffer python API實作address book傳遞
> ref:
> [protobuf python basic](https://developers.google.com/protocol-buffers/docs/pythontutorial)
## Download protobuf-all-version source package and build
可以直接看第三步驟
1. 安裝protobuf compiler(use to compile ```.proto```)
- Binary install from [protobuf github release](https://github.com/protocolbuffers/protobuf/releases)
``` bash
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.17.3/protoc-3.17.3-linux-x86_64.zip
unzip protoc-3.17.3-linux-x86_64.zip
ln -s /opt/protobuf/protoc-3.17.3/bin/protoc /usr/local/bin/protoc
```
- build protoc from source, 參考[build protoc from source](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md)
``` bash
yum install automake autoconf libtool git -y
scl enable gcc-toolset-9 bash
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive
./autogen.sh
# make
mkdir /opt/protoc
./configure --prefix=/opt/protoc
make
make check
# 如果"make check"失敗則依然可以安裝,只是一些功能可能在目前OS不能用
sudo make install
# protoc的runtime library
mkdir /usr/lib64/protobuf
ln -s /opt/protoc/lib/libprotobuf-lite.so.28.0.3 /usr/lib64/protobuf/libprotobuf-lite.so.28.0.3
ln -s /opt/protoc/lib/libprotobuf-lite.so.28.0.3 /usr/lib64/protobuf/libprotobuf-lite.so.28
ln -s /opt/protoc/lib/libprotobuf-lite.so.28.0.3 /usr/lib64/protobuf/libprotobuf-lite.so
ln -s /opt/protoc/lib/libprotobuf.so.28.0.3 /usr/lib64/protobuf/libprotobuf.so.28.0.3
ln -s /opt/protoc/lib/libprotobuf.so.28.0.3 /usr/lib64/protobuf/libprotobuf.so.28
ln -s /opt/protoc/lib/libprotobuf.so.28.0.3 /usr/lib64/protobuf/libprotobuf.so
ln -s /opt/protoc/lib/libprotoc.so.28.0.3 /usr/lib64/protobuf/libprotoc.so.28.0.3
ln -s /opt/protoc/lib/libprotoc.so.28.0.3 /usr/lib64/protobuf/libprotoc.so.28
ln -s /opt/protoc/lib/libprotoc.so.28.0.3 /usr/lib64/protobuf/libprotoc.so
echo "/usr/lib64/protobuf" > /etc/ld.conf.so.d/protobuf-x86_64.conf
# create cache
ldconfig
# verify cache
ldconfig -p | grep proto
# 確定可以執行
ln -s /opt/protobuf/bin/protoc /usr/local/bin/protoc
protoc --version
```
* 有關RHEL `gcc-toolset-9` 的取得可先看[此](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/rkvLfY5PP#How-to-get-gcc-toolset--on-RHEL-83)
* 有關`LD_LIBRARY_PATH`的討論可先看[此](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/B1lFb_TvP#debug-OpenSSL%EF%BC%9Aerror-while-loading-shared-libraries-libcryptoso11-cannot-open-shared-object-file-No-such-file-or-directory)
* 上述是將所有```.so```全部symlink到`/usr/lib64/`底下,或者也可以直接把`/etc/ld.so.conf.d/protobuf-x86_64.conf`的路徑指到`/opt/protoc/lib`
2. 安裝protobuf runtime(python)
- Binary install from [protobuf github release](https://github.com/protocolbuffers/protobuf/releases)
``` bash
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.17.3/protobuf-python-3.17.3.tar.gz
tar -zxf protobuf-python-3.17.3.tar.gz
```
- 確認python setuptools有裝
``` bash
python3.8 -m pip --upgrade pip setuptools wheel
```
- 確保protobuf runtime與protobut compiler版本號一致後,Build and run the test
- 並且用 C++ implementation runtime library加強python效能
``` bash
protoc --version
cd /opt/protobuf-3.17.3/python
# 讓libprotobuf.so dyamically available
# 注意!!!, 用LD_LIBRAY_PATH沒用
export LIBRARY_PATH=/opt/protoc/lib${LIBRARY_PATH:+:${LIBRARY_PATH}}
python3.8 setup.py build --cpp_implementation
python3.8 setup.py test --cpp_implementation
python3.8 setup.py install --cpp_implementation
```
* 節錄自[此](https://github.com/protocolbuffers/protobuf/tree/master/python)
```
You must make libprotobuf.so dynamically available. You can either install libprotobuf you built earlier, or set LD_LIBRARY_PATH:
```
- 但用 `LD_LIBRARY_PATH`, `/usr/bin/ld`依然找不到`protobuf`
- 用`/etc/ld.so.conf.d/protobuf-x86_64.conf`路徑指到`/opt/protoc/lib`(buid protoc from soure產生的lib,如上一小節),並且`ldconfig`後確認cache裡有此library,`/usr/bin/ld`依然找不到`protobuf`
* 結果用`LIBRARY_PATH`就可以
* `LD_LIBRARY_PATH`與`LIBRARY_PATH`的不同可詳[此](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/B1lFb_TvP#%E5%BB%B6%E4%BC%B8%E9%96%B1%E8%AE%80-LIBRARY_PATH%E8%88%87LD_LIBRARY_PATH%E7%9A%84%E5%B7%AE%E5%88%A5)
*
3. (前面兩步驟的精簡版 baszinga!) 直接下載 [protobuf-all-3.17.3.tar.gz](https://github.com/protocolbuffers/protobuf/releases)
- build
``` bash
tar -zxf protobuf-all-3.17.3.tar.gz -C ~
mkdir /opt/protobuf-3.17.3
cd ~/protobuf-3.17.3
yum install automake autoconf libtool git -y
./autogen.sh
./configure --prefix=/opt/protobuf-3.17.3
make;
make check;
sudo make install;
# /opt/protobuf-3.17.3/{bin,lib,include}
mv ~/protobuf-3.17.3 /opt/protobuf-3.17.3
cd /opt/protobuf-3.17.3/protobuf-3.17.3/python/
python3.8 -m pip install --upgrade pip setuptool wheel
python3.8 setup.py build --cpp_implementation
env LD_LIBRARY_PATH=/opt/protobuf-3.17.3/lib python3.8 setup.py test --cpp_implementation
python3.8 setup.py install --cpp_implementation
# done
```
## Compile ```.proto```
1. 撰寫 addressbook```.proto```
``` proto
syntax = "proto2";
package tutorial;
// 在python的 .proto定義 package是無效的
// 因為python package結構取決於directory structure
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
```
### (python) compile protocol buffer
``` bash
protoc \
--proto_path=/opt/protobuf-3.17.3/python-example \
--python_out=/opt/protobuf-3.17.3/python-example \
/opt/protobuf-3.17.3/python-example/addressbook.proto
```
- `--proto_path=/the/path`或者`-I=/the/path` 代表source directory,即我的`.proto`所在的目錄
- 這目錄一定要包含後面argument指定的所有`.proto`, 也就是在compile前,最好先把`.proto`集中在一起
- 如果沒給,則會是執行的當前目錄
- `--python_out`,`$DST_DIR` 代表destination directory compile後產生的python code(```??_pb2.py```)要放去哪?
- 此指定的目錄(不管相對或絕對)都必須是存在的,`protoc`不會幫你建
- 這通常跟`$SRC_DIR`一樣
- 第三個與接下來數個arguments給上欲compile的```.proto```路徑
- `_pb2`表示protocal buffer version2,目前(20210618)儘管```.proto```裡面syntax指定為proto3,compile出來還是會以`_pb2`結尾
- 如果```.proto```檔名裡面有不准的符號(如`-`),將被自動換成`_`
- 也可以指定多個`.proto`最後將所有的`.py`與其目錄結構弄成一個`.zip`
``` bash
protoc \
--proto_path=/opt/python_venv/protobuf/home/jupyter/protobuf_python_example/src \
--python_out=/opt/python_venv/protobuf/home/jupyter/protobuf_python_example/build/gen/two.zip \
/opt/python_venv/protobuf/home/jupyter/protobuf_python_example/src/addressbook.proto \
/opt/python_venv/protobuf/home/jupyter/protobuf_python_example/src/deeper-src/searchRequest.proto
```
- `.zip`解開後,目錄結構長這樣: `{addressbook_pb2.py,deep_src/searchRequest_pb2.py}`
- 以下使用相對路徑的範例
``` bash
protoc \
--proto_path=src \
--python_out=build/gen \
src/foo.proto src/bar/baz.proto
```
- `build/gen`目錄必須存在
- 產生兩個檔案`build/gen/foo_pb2.py`,`build/gen/bar/baz_pb2.py`,其中`build/gen/bar`目錄被自動創建
### Python generated code break down
3. `addressbook_pb2.py`
``` python
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: addressbook.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='addressbook.proto',
package='tutorial',
syntax='proto2',
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x11\x61\x64\x64ressbook.proto\x12\x08tutorial\"\xdb\x01\n\x06Person\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x05\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12,\n\x06phones\x18\x04 \x03(\x0b\x32\x1c.tutorial.Person.PhoneNumber\x1aM\n\x0bPhoneNumber\x12\x0e\n\x06number\x18\x01 \x01(\t\x12.\n\x04type\x18\x02 \x01(\x0e\x32\x1a.tutorial.Person.PhoneType:\x04HOME\"+\n\tPhoneType\x12\n\n\x06MOBILE\x10\x00\x12\x08\n\x04HOME\x10\x01\x12\x08\n\x04WORK\x10\x02\"/\n\x0b\x41\x64\x64ressBook\x12 \n\x06people\x18\x01 \x03(\x0b\x32\x10.tutorial.Person'
)
_PERSON_PHONETYPE = _descriptor.EnumDescriptor(
name='PhoneType',
full_name='tutorial.Person.PhoneType',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='MOBILE', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='HOME', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='WORK', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=208,
serialized_end=251,
)
_sym_db.RegisterEnumDescriptor(_PERSON_PHONETYPE)
_PERSON_PHONENUMBER = _descriptor.Descriptor(
name='PhoneNumber',
full_name='tutorial.Person.PhoneNumber',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='number', full_name='tutorial.Person.PhoneNumber.number', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='type', full_name='tutorial.Person.PhoneNumber.type', index=1,
number=2, type=14, cpp_type=8, label=1,
has_default_value=True, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=129,
serialized_end=206,
)
_PERSON = _descriptor.Descriptor(
name='Person',
full_name='tutorial.Person',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='tutorial.Person.name', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='id', full_name='tutorial.Person.id', index=1,
number=2, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='email', full_name='tutorial.Person.email', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='phones', full_name='tutorial.Person.phones', index=3,
number=4, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[_PERSON_PHONENUMBER, ],
enum_types=[
_PERSON_PHONETYPE,
],
serialized_options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=32,
serialized_end=251,
)
_ADDRESSBOOK = _descriptor.Descriptor(
name='AddressBook',
full_name='tutorial.AddressBook',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='people', full_name='tutorial.AddressBook.people', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=253,
serialized_end=300,
)
_PERSON_PHONENUMBER.fields_by_name['type'].enum_type = _PERSON_PHONETYPE
_PERSON_PHONENUMBER.containing_type = _PERSON
_PERSON.fields_by_name['phones'].message_type = _PERSON_PHONENUMBER
_PERSON_PHONETYPE.containing_type = _PERSON
_ADDRESSBOOK.fields_by_name['people'].message_type = _PERSON
DESCRIPTOR.message_types_by_name['Person'] = _PERSON
DESCRIPTOR.message_types_by_name['AddressBook'] = _ADDRESSBOOK
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Person = _reflection.GeneratedProtocolMessageType('Person', (_message.Message,), {
'PhoneNumber' : _reflection.GeneratedProtocolMessageType('PhoneNumber', (_message.Message,), {
'DESCRIPTOR' : _PERSON_PHONENUMBER,
'__module__' : 'addressbook_pb2'
# @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber)
})
,
'DESCRIPTOR' : _PERSON,
'__module__' : 'addressbook_pb2'
# @@protoc_insertion_point(class_scope:tutorial.Person)
})
_sym_db.RegisterMessage(Person)
_sym_db.RegisterMessage(Person.PhoneNumber)
AddressBook = _reflection.GeneratedProtocolMessageType('AddressBook', (_message.Message,), {
'DESCRIPTOR' : _ADDRESSBOOK,
'__module__' : 'addressbook_pb2'
# @@protoc_insertion_point(class_scope:tutorial.AddressBook)
})
_sym_db.RegisterMessage(AddressBook)
# @@protoc_insertion_point(module_scope)
```