Search code examples
pythonprotocol-buffersargparseintrospection

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()

Solution

  • 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.