Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a copy constructor for Map Fields in Python Protocol Buffers?

Python Generated Code explains most use-cases of protobuf map fields in Python but not how to copy one map to another.

Given simple map

message Src {
    map<string, string> properties = 1;
    ...
}

message Dst {
    map<string, string> properties = 1;
    ...
}

You cannot assign a value to an embedded message field, so there's no doing:

# Will not work.
dst = Dst()
dst.properties = src.properties

Nor is there an implementation of CopyFrom since map is not itself a message, it's a field within a message.

# Will not work.
dst = Dst()
dst.properties.CopyFrom(src.properties)

I also can't copy the entire message since I only want the map.

# Copies unwanted fields!
dst = Dst()
dst.CopyFrom(src)

I hope I don't have to iterate over all keys and assign one-by-one!

# Iterate over map keys
for key in src.properties:
    dst.properties[key] = src.properties[key]
like image 388
Jack Avatar asked Feb 07 '18 20:02

Jack


2 Answers

Map fields in python protobuf generated code operate pretty similarly to python dicts, so you can use .update() to copy over:

dst.properties.update(src.properties)
like image 61
fwph Avatar answered Nov 05 '22 01:11

fwph


If the maps' values are simple types, dst.properties.update(src.properties) should work. However, if you have a map with Message-type values, e.g.:

message Prop {
  optional string value = 1;
  optional bool is_public = 2;
}

message Src {
    map<string, Prop> properties = 1;
    ...
}

message Dst {
    map<string, Prop> properties = 1;
    ...
}

then .update (or any type of direct assignment) will throw:

ValueError: Direct assignment of submessage not allowed

Instead, you'll have to do something like:

for k, v in src.properties.items():
  dst.properties[k].CopyFrom(v)
like image 31
kristina Avatar answered Nov 05 '22 00:11

kristina