Skip to main content

Translate a protobuf message to Google's RPC array format.

Project description

protobuf2arr

Translate a protobuf message to Google's RPC array format. Many non-public Google services use an array wire format for protobuf messages where each field number is mapped to an index of an array (one-based).

Simple Example

The following protobuf message Message gets translated to the array: [field1_value, field2_value].

Protobuf implementation:

syntax = "proto3";

message Message {
   int32 field1 = 1;
   string field2 = 2;
}

Python implementation:

import message_pb2
message = message_pb2.Message()
message.field1 = 77
message.field2 = "Hello World"

RPC array format:

[77, "Hello World"]

Skipping field numbers

Since fields are mapped to indexes of an array, missing field numbers are included as None or null types.

Let's add a new field field3 to the protobuf message above, but with a number of 4, skipping 3.

syntax = "proto3";

message Message {
    int32 field1 = 1;
    string field2 = 2;
    bool field3 = 4;
}

Python implementation remains the same, with the addition of enabling field3.

message.field3 = True

RPC array format now includes None or null (serialized string) for the missing field 3 and the value of field3 in the array position 4 (one-based).

[77, "Hello World", null, True]

Installation

You can install the package directly from pypi:

pip install protobuf2arr

Usage

Assuming we have a protobuf message format defined as such:

syntax = "proto3";

package mypackage;

message TaskQueue {
    int32 queue_id = 1;
    string queue_name = 2;
    repeated TaskItem items = 3;

    message TaskItem {
        int32 task_id = 1;
        string task_name = 2;
        string date = 4; 
    }
}

The message is transformed via protoc to taskqueue_pb2.py. Normal protobuf usage applies to the generated message:

import taskqueue_pb2

task_queue = taskqueue_pb2.TaskQueue()
task_queue.queue_id = 78
task_queue.queue_name = "redis:thread:tasks"

for i in range(2):
    task = taskqueue_pb2.TaskQueue.TaskItem()
    task.task_id = i + 1
    task.task_name = f"cleanup:{i + 1}"
    task.date = '2022-04-01'
    task_queue.items.append(task)

print(task_queue)

This produces the standardize protobuf string representation:

queue_id: 78
queue_name: "redis:thread:tasks"
items {
  task_id: 1
  task_name: "cleanup:1"
  date: "2022-04-01"
}
items {
  task_id: 2
  task_name: "cleanup:2"
  date: "2022-04-01"
}

However, when we serialize the protobuf message to an array serialize_msg2arr(task_queue), we get the following:

[78, 'redis:thread:tasks', [[1, 'cleanup:1', '2022-04-01'], [2, 'cleanup:2', '2022-04-01']]]

Nullable Types

Consider the empty state for the task queue with a single empty task.

task_queue = taskqueue_pb2.TaskQueue()
task_queue.items.append(taskqueue_pb2.TaskQueue.TaskItem())
serialize_msg2arr(task_queue)
[0, '', [[0, '', '']]]

Protobuf fields empty state is their type default, so 0 for int32 and the empty string for string. However, many Google api's require None or null for field empty states.

This library allows you to include a nullable option on any field where the default value should be translated to a None type. The nullable custom option can be included either of two ways in your protobuf schema, the serializer only cares about the option name.

Inline declaration

Declare the custom option inline in your proto file:

import "google/protobuf/descriptor.proto";

extend google.protobuf.FieldOptions {
    optional string nullable = 50027;
}

The option extension number does not matter, so long as it is within the proper Protobuf field option range. Now you to tag your fields with this option and the specific value (string) you want to be parsed to None as such:

int32 queue_id = 1 [(nullable) = '0'];

Package declaration

Alteratively, you can define this custom option in a separate proto file and import it into your message.

nullable_option.proto:

syntax = "proto3";

package protobuf2arr;

import "google/protobuf/descriptor.proto";

extend google.protobuf.FieldOptions {
    optional string nullable = 50027;
}

message.proto:

syntax = "proto3";

package mypackage;

import "nullable_option.proto"

message TaskQueue {
    int32 queue_id = 1 [(nullable_option.nullable) = '0'];
}

Output

Assume that we want to ignore the queue_name and date fields and send the None or null type when they are not set, we can use protobuf custom field options. Regardless of which method is choosen, we get the results:

message.py:

task_queue = taskqueue_pb2.TaskQueue()
task_queue.items.append(taskqueue_pb2.TaskQueue.TaskItem())
serialize_msg2arr(task_queue)

output:

[null, '', [[0, '', null]]]

Nullable Messages

Set the nullable tag to the empty message state '' to support lists of messages where an item is None i.e the default state. Alternatively, more complicated default states are supported:

message.proto:

repeated TaskItem items = 3 [(nullable) = 'task_id: 1'];

message.py:

task_queue = taskqueue_pb2.TaskQueue()
task = taskqueue_pb2.TaskQueue.TaskItem()
task.task_id = 1
task_queue.items.append(task)
serialize_msg2arr(task_queue)

output:

[null, '', [null]]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

protobuf2arr-0.1.4.tar.gz (5.7 kB view details)

Uploaded Source

Built Distribution

protobuf2arr-0.1.4-py3-none-any.whl (5.7 kB view details)

Uploaded Python 3

File details

Details for the file protobuf2arr-0.1.4.tar.gz.

File metadata

  • Download URL: protobuf2arr-0.1.4.tar.gz
  • Upload date:
  • Size: 5.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.9.12

File hashes

Hashes for protobuf2arr-0.1.4.tar.gz
Algorithm Hash digest
SHA256 aab0fc58c33478997781248d9539ca1822db8515d9da09f80eac9efdf833c029
MD5 24bc8aaa38ea85722934546bf6e52b83
BLAKE2b-256 30fa0e75ebf642a0e70cfca1326fb2e3b4eb5c6fcdd46b0658c16168cbeaba77

See more details on using hashes here.

File details

Details for the file protobuf2arr-0.1.4-py3-none-any.whl.

File metadata

File hashes

Hashes for protobuf2arr-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 67987070331d1d4cca81ddc624534fa8873e9c5afc30f62adba572a12f0030b9
MD5 73d06983909d89af4cdb52852a88a3fe
BLAKE2b-256 d02a51b35b20d5644df7a6ff025083ed724389edbc2762964ae3b744590dc609

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page