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!

2
  • 3
    Thanks for the post. Great to know I am not the only one suffering from the pain Commented Jan 9, 2020 at 1:38
  • 1
    The experience is very catmario Commented Jan 9, 2020 at 1:38

3 Answers 3

10

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.
Sign up to request clarification or add additional context in comments.

4 Comments

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!
got: AttributeError: value
The following .CopyFrom(ts) worked for me.
4

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)

1 Comment

Simplest, most direct answer. Since google brought me here, for anyone using strings as keys, remember to switch the 0 with the string, e.g.: end_point_rslt.properties['yourKey'].CopyFrom(ts)
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.