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()
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With