How to use a prototbuf map in Python?
Asked Answered
I

3

18

Given a proto definition

message EndpointResult {
    int32 endpoint_id = 1;
    // property id as key
    map<int32, TimeSeries> properties = 2;
}

message TimeSeries {
    repeated TimeEntry value = 2;
}

message TimeEntry {
    int32 time_unit = 1;
    float value = 2;
}

I wish to populate the map in the EndpointResult class. I have tried different approaches suggested in the docs but all raise a error for me.

Setting up a test class

end_point_rslt = nom.EndpointResult()
end_point_rslt.endpoint_id=0

ts = nom.TimeSeries()
te = ts.value.add()
te.time_unit = 0
te.value = 5.

Then trying the different approaches:

end_point_rslt.properties[0] = ts

ValueError: Direct assignment of submessage not allowed

end_point_rslt.properties[0].submessage_field = ts

AttributeError: Assignment not allowed (no field "submessage_field" in protocol message object).

end_point_rslt.properties.get_or_create(0)
end_point_rslt.properties[0] = ts

ValueError: Direct assignment of submessage not allowed

end_point_rslt.properties.get_or_create(0)
end_point_rslt.properties[0].submessage_field = ts

AttributeError: Assignment not allowed (no field "submessage_field" in protocol message object).

end_point_rslt.properties = {0 : ts}

AttributeError: Assignment not allowed to repeated field "properties" in protocol message object.

end_point_rslt.properties.get_or_create(0)
end_point_rslt.properties = {0 : ts}

TypeError: Can't set composite field

Any example of how to use a protocol buffer map in python would be greatly appreciate!

Irrelievable answered 28/11, 2018 at 12:27 Comment(2)
Thanks for the post. Great to know I am not the only one suffering from the painAddressograph
The experience is very catmarioAddressograph
I
11

After staring at the docs, I realized that the problem was me assigning a class to the dictionary.

The correct syntax is

end_point_rslt = nom.EndpointResult()
end_point_rslt.endpoint_id=0
te = end_point_rslt.properties[0].value.add()
te.time_unit = 0
te.value = 5.
Irrelievable answered 28/11, 2018 at 14:12 Comment(2)
how to dynamically add a key - value to the map ? Python protobuf implementation is currently a joke ! spent hours trying to figure simple things out only to find it doesn't work!Eckman
This post has a solution: #52583968Hansel
A
3

A workaround with Struct

Instead of

message EndpointResult {
    int32 endpoint_id = 1;
    // property id as key
    map<int32, TimeSeries> properties = 2;
}

Use

message EndpointResult {
    int32 endpoint_id = 1;
    // property id as key
    google.protobuf.Struct properties = 2;
}

Initiate message using

properties = google.protobuf.struct_pb2.Struct()
properties[key1]=[TimeEntry(time_unit1, val1), TimeEntry(time_unit2, val2)...]
properties[key2]=[TimeEntry(time_unit3, val3)...]

EndpointResult(
    endpoint_id='1',
    properties=properties
)

I've had encountered many issues using map as well and find this solution works

Addressograph answered 13/1, 2020 at 21:11 Comment(0)
C
2

Don't see anyone suggest this here. But one way is to utilize protobuf's CopyFrom (ref).

Example

end_point_rslt.properties[0].CopyFrom(ts)
Crossover answered 15/2 at 21:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.