Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to list attributes that you defined in google protocol buffers?

I have a google protocol buffer definition that looks something like the followwing:

message Foo {
   required string Name = 1;
   optional string Address = 2;
   optional string NickName = 3;
   optional  int32  height = 4;
}

Now in python, I'd like to list all the the attributes above, but only those attributes. However, interactive python, I see that there's a whole lot more fields that are defined by google. So this seems problematic.

I've looked at some of the stackoverflow posts on introspection. There's an inspect module that looks nice, but the problem is the other members that google protocol buffers defines for me.

Is there a way to do so?

Here's what I'd like to do. I have a python utility that fills out the above fields from the command line. I use argparse, and I will:

parser = argparse.ArgumentParser(...)
parser.add_argument( "--attribute_name", blah)

I would like to place add_argument() in a loop and make it dynamic based off the proto file definition. Basically, I don't want to keep modifying the utility's code, everytime I change the proto file. It seems like I should be able to do this in python. I just don't know how.

Anyone have a suggestion?

thanks.

For additional information, I took the above example, and compiled it with protoc. Here's the interactive output:

>>> import hello_pb2
>>> h = hello_pb2.Foo()
>>> dir(h)
['ADDRESS_FIELD_NUMBER', 'Address', 'ByteSize', 'Clear', 'ClearExtension', 'ClearField', 'CopyFrom', 'DESCRIPTOR', 'FindInitializationErrors', 'FromString', 'HEIGHT_FIELD_NUMBER', 'HasExtension', 'HasField', 'IsInitialized', 'ListFields', 'MergeFrom', 'MergeFromString', 'NAME_FIELD_NUMBER', 'NICKNAME_FIELD_NUMBER', 'Name', 'NickName', 'ParseFromString', 'RegisterExtension', 'SerializePartialToString', 'SerializeToString', 'SetInParent', '_InternalParse', '_InternalSerialize', '_Modified', '_SetListener', '__class__', '__deepcopy__', '__delattr__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_cached_byte_size', '_cached_byte_size_dirty', '_decoders_by_tag', '_extensions_by_name', '_extensions_by_number', '_fields', '_is_present_in_parent', '_listener', '_listener_for_children', 'height']

I inpected some promising fields like _fields, but that was empty.

Here is the answer: (given by Kenton Varda's reply)

import hello_pb2
h = hello_pb2.Foo()
f = hello_pb2.Foo()
f.DESCRIPTOR.fields_by_name.keys()
like image 686
Bitdiot Avatar asked Dec 26 '22 02:12

Bitdiot


2 Answers

You want to iterate over Foo.DESCRIPTOR.fields. See the Descriptor class:

https://developers.google.com/protocol-buffers/docs/reference/python/google.protobuf.descriptor.Descriptor-class

Every message class has a static member DESCRIPTOR which is the descriptor for that type.

like image 130
Kenton Varda Avatar answered Jan 04 '23 05:01

Kenton Varda


If you want to convert a protobuf object to a dictionary:

import hello_pb2

def proto2dict(msg):
    hsh_val = dict(msg.ListFields())
    d = dict((k, hsh_val[hsh]) for k, hsh in msg.DESCRIPTOR.fields_by_name.items())
    return d

data = {"Name":"name", "Address":"address", "NickName":"nick", "height": 6}
msg = hello_pb2.Foo(**data)

assert proto2dict(msg) == data

Note: looks inefficient and may not work for nested definitions.

like image 35
yang5 Avatar answered Jan 04 '23 04:01

yang5