129

I am using protocol buffers in python and I have a Person message

repeated uint64 id

but when I try to assign a value to it like:

person.id = [1, 32, 43432]

I get an error: Assigment not allowed for repeated field "id" in protocol message object How to assign a value to a repeated field ?

Makogan
  • 8,208
  • 7
  • 44
  • 112
PaolaJ.
  • 10,872
  • 22
  • 73
  • 111

4 Answers4

167

As per the documentation, you aren't able to directly assign to a repeated field. In this case, you can call extend to add all of the elements in the list to the field.

person.id.extend([1, 32, 43432])
Tim
  • 11,710
  • 4
  • 42
  • 43
  • 13
    Similarly, for adding a single value, use `append()`, e.g. `person.id.append(1)`. This applies for **any** *protobuf* `repeated` fields. – Hindol Nov 18 '15 at 10:40
  • 28
    `append` doesn't work if the field is a message type rather than a raw type (like string, int32, etc). `extend` does work for message types. – abeboparebop Feb 23 '16 at 18:01
  • 11
    If you want to overwrite a repeated message type field then you need to delete first and then extend. `del person.siblings[:]` `person.siblings.extend([Person(), Person()])` – Neil Jan 09 '18 at 11:34
  • 1
    https://developers.google.com/protocol-buffers/docs/reference/python-generated?csw=1#repeated-message-fields – Neil Jan 09 '18 at 12:28
  • 1
    remember to pass in the arguments to extend as an array (or list), wrap them with brackets if you need to! – Nicholas Gentile Mar 06 '18 at 18:54
  • I'd just like to mention that if you're adding a single element, append does not work if the field you're adding is itself a message. In this case, you should use the method `add`. https://developers.google.com/protocol-buffers/docs/reference/python-generated#repeated-message-fields – Max Bileschi Mar 19 '18 at 20:12
  • Also related https://stackoverflow.com/questions/18376190/attributeerror-assignment-not-allowed-to-composite-field-task-in-protocol-mes – Alex Punnen May 10 '19 at 08:43
  • It's probably worth pointing out now that the comments indicating that `append` does not work if the field is a message type, are no longer correct. The documentation linked in the comments now shows the use of `append` in such cases and I have confirmed it works. `add` and `extend` of course also work. – three_pineapples Oct 12 '20 at 06:18
  • I'm trying to use extend , and I keep getting the message "TypeError: Not a cmessage" – kommradHomer Apr 16 '22 at 15:16
55

If you don't want to extend but overwrite it completely, you can do:

person.id[:] = [1, 32, 43432]

This approach will also work to clear the field entirely:

del person.id[:]
kirpit
  • 4,419
  • 1
  • 30
  • 32
  • 12
    For repeated composite types, you can not use person.id[:] = [xxx] to assign replace. You have to first delete them all and then extend – ospider Apr 17 '19 at 12:42
11

For repeated composite types this is what worked for me.

del person.things[:]
person.things.extend([thing1, thing2, ..])

taken from these comments How to assign to repeated field? How to assign to repeated field?

Chandler
  • 1,019
  • 14
  • 23
8

After loosing a lot of sleep trying to get a basic example for repeated fields working, I finally got it.

The problem:

  • Create a calculator.py with two functions, square and multiplier.
  • Using GRPC, create a Proto file for the same.
  • Write a server, a client.
  • Run the server, and run the client to get correct results.

The Proto file:

syntax = "proto3";

message Number {
    int32 value = 1;
}

message NumList {
    string name = 1;
    repeated Number nums = 2;
}

service Calculator {
    rpc Multiplier(NumList) returns (Number) {}
    rpc Square(Number) returns (Number) {}
}

Now the square part is easy, but for the Multiplier, I wanted to pass a list of Numbers (as in Number type as defined in the proto file).

The problem was with the repeated field. And here is the ultimate solution in short.

The solution:

import grpc

# import the generated classes
import calculator_pb2
import calculator_pb2_grpc
# open a gRPC channel
channel = grpc.insecure_channel('localhost:50051')

# create a stub (client)
stub = calculator_pb2_grpc.CalculatorStub(channel)
num_list = calculator_pb2.NumList()
num_list.name = 'MyFirstList'
n1 = num_list.nums.add()
n2 = num_list.nums.add()
n3 = num_list.nums.add()
n1.value = 10
n2.value = 20
n3.value = 30

assert len(num_list.nums) == 3

response = stub.Multiplier(num_list)
print(response.value)

The Calculator Multiplier function (because this needs to be shown):

def multiplier(numlist, name):
    mul = 1
    for num in numlist:
        mul = mul * num.value
    print(f'Result of list {name}')
    return mul

Hope this helps someone. Hope this is as descriptive as it should be.

Arindam Roychowdhury
  • 5,927
  • 5
  • 55
  • 63
  • 1
    Is not it enough to use `repeated int32 nums = 2;` in the message `NumList`? if I am not wrong you want to make a collection of `int data`, eg : `[2,5,9]`. I have checked with `cpp` though. – user10634362 Mar 01 '22 at 09:10
  • 1
    This answer saves my day. This addresses the problem of composing a message with non-primitive repeated field in gRPC. – TaQuangTu Jul 10 '23 at 09:44