Skip to content

proto-plus

类型转换

to_dict

在对proto对象进行to_dict转换为dict时, 会对bytes对象进行 base64 编码以保留语义

查看代码示例
import proto
from google.protobuf.json_format import ParseDict

class MyMessage(proto.Message):
    data = proto.Field(proto.BYTES, number=1)

msg = MyMessage(data=b"this is a message")
msg_dict = MyMessage.to_dict(msg)

# Note: the value is the base64 encoded string of the bytes field

# It has a type of str, NOT bytes

assert type(msg_dict['data']) == str

msg_pb = ParseDict(msg_dict, MyMessage.pb())
msg_two = MyMessage(msg_dict)

assert msg == msg_pb == msg_two

赋值拷贝

当一个字段赋值给另外一个字段时,采取拷贝而非引用

可以通过重新赋值来更新拷贝

查看代码示例
composer = Composer(given_name="Johann", family_name="Bach")
song = Song(title="Tocatta and Fugue in D Minor", composer=composer)
composer.given_name = "Wilhelm"

# 'composer' is NOT a reference to song.composer
assert song.composer.given_name == "Johann"

# We CAN update the song's composer by assignment.
song.composer = composer
composer.given_name = "Carl"

# 'composer' is STILL not a reference to song.composer.
assert song.composer.given_name == "Wilhelm"

# It does work in reverse, though,
# if we want a reference we can access then update.
composer = song.composer
composer.given_name = "Gottfried"

assert song.composer.given_name == "Gottfried"

# We can use 'copy_from' if we're concerned that the code
# implies that assignment involves references.
composer = Composer(given_name="Elisabeth", family_name="Bach")
# We could also do Message.copy_from(song.composer, composer) instead.
Composer.copy_from(song.composer, composer)

assert song.composer.given_name == "Elisabeth"

示例

解析 v2fly geosite dat 数据

查看示例代码
import httpx
from enum import IntEnum
import proto

class DomainTypeEnum(IntEnum):
    Domain_Plain = 0
    Domain_Regex = 1
    Domain_RootDomain = 2
    Domain_Full = 3

class Domain_Attribute(proto.Message):
    key = proto.Field(proto.STRING, number=1)
    bool_value = proto.Field(proto.BOOL, number=2, oneof="typed_value")
    int_value = proto.Field(proto.INT64, number=3, oneof="typed_value")

class Domain(proto.Message):
    type = proto.Field(proto.INT32, number=1)
    value = proto.Field(proto.STRING, number=2)
    attribute = proto.RepeatedField(Domain_Attribute, number=3)

class GeoSite(proto.Message):
    country_code = proto.Field(proto.STRING, number=1)
    domain = proto.RepeatedField(Domain, number=2)
    resource_hash = proto.RepeatedField(proto.BYTES, number=3)
    code = proto.Field(proto.STRING, number=4)
    file_path = proto.Field(proto.STRING, number=5)

class GeoSiteList(proto.Message):
    entry = proto.RepeatedField(GeoSite, number=1)

def main():
    res = httpx.get("<https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat>", follow_redirects=True)
    geosite_list = GeoSiteList.deserialize(res.content)
    cnt = 0
    for entry in geosite_list.entry:
        for domain in entry.domain:
            cnt += 1
            print(f"{entry.country_code} - {DomainTypeEnum(domain.type).name} - {domain.value} - {domain.attribute}")
    print(cnt)

main()