Skip to main content

A tool to dynamically create protobuf message classes from python data schemas

Project description

PY To Proto

This library holds utilities for converting in-memory data schema representations to Protobuf. The intent is to allow python libraries to leverage the power of protobuf while maintaining the source-of-truth for their data in pure python and avoiding static build steps.

Why?

The protobuf langauge is a powerful tool for defining language-agnostic, composable datastructures. Protobuf also offers cross-language compatibility so that a given set of definitions can be compiled into numerous target programming languages. The downside is that protobuf requires_a static built step to perform this proto -> X conversion step. Alternately, there are multiple ways of representing data schemas in pure python which allow a python library to interact with well-typed data objects. The downside here is that these structures can not easily be used from other programming languages. The pros/cons of these generally fall along the following lines:

  • Protobuf:
    • Advantages
      • Compact serialization
      • Auto-generated grpc client and service libraries
      • Client libraries can be used from different programming languages
    • Disadvantages
      • Learning curve to understand the full ecosystem
      • Not a familiar tool outside of service engineering
      • Static compilation step required to use in code
  • Python schemas:
    • Advantages
      • Can be learned quickly using pure-python documentation
      • Can be written inline in pure python
    • Disadvantages
      • Generally, no standard serialization beyond json
      • No automated service implementations
      • No/manual mechanism for usage in other programming languages

This project aims to bring the advantages of both types of schema representation so that a given project can take advantage of the best of both:

  • Define your structures in pure python for simplicity
  • Dynamically create google.protobuf.Descriptor objects to allow for protobuf serialization and deserialization
  • Reverse render a .proto file from the generated Descriptor so that stubs can be generated in other languages
  • No static compiliation needed!

Supported Python Schema Types

Currently, objects can be declared using either python dataclasses or Json TypeDef (JTD). Additional schemas can be added by subclassing ConverterBase.

Dataclass To Proto

The following example illustrates how dataclasses and enums can be converted to proto:

from dataclasses import dataclass
from enum import Enum
from typing import Annotated, Dict, List, Enum
import py_to_proto

# Define the Foo structure as a python dataclass, including a nested enum
@dataclass
class Foo:

    class BarEnum(Enum):
        EXAM: 0
        JOKE_SETTING: 1

    foo: bool
    bar: List[BarEnum]

# Define the Foo protobuf message class
FooProto = py_to_proto.descriptor_to_message_class(
    py_to_proto.dataclass_to_proto(
        package="foobar",
        dataclass_=Foo,
    )
)

# Declare the Bar structure as a python dataclass with a reference to the
# FooProto type
@dataclass
class Bar:
    baz: FooProto

# Define the Bar protobuf message class
BarProto = py_to_proto.descriptor_to_message_class(
    py_to_proto.dataclass_to_proto(
        package="foobar",
        dataclass_=Bar,
    )
)

# Instantiate a BarProto
print(BarProto(baz=FooProto(foo=True, bar=[Foo.BarEnum.EXAM.value])))

def write_protos(proto_dir: str):
    """Write out the .proto files for FooProto and BarProto to the given
    directory
    """
    FooProto.write_proto_file(proto_dir)
    BarProto.write_proto_file(proto_dir)

JTD To Proto

The following example illustrates how JTD schemas can be converted to proto:

import py_to_proto

# Declare the Foo protobuf message class
Foo = py_to_proto.descriptor_to_message_class(
    py_to_proto.jtd_to_proto(
        name="Foo",
        package="foobar",
        jtd_def={
            "properties": {
                # Bool field
                "foo": {
                    "type": "boolean",
                },
                # Array of nested enum values
                "bar": {
                    "elements": {
                        "enum": ["EXAM", "JOKE_SETTING"],
                    }
                }
            }
        },
    )
)

# Declare an object that references Foo as the type for a field
Bar = py_to_proto.descriptor_to_message_class(
    py_to_proto.jtd_to_proto(
        name="Bar",
        package="foobar",
        jtd_def={
            "properties": {
                "baz": {
                    "type": Foo.DESCRIPTOR,
                },
            },
        },
    ),
)

def write_protos(proto_dir: str):
    """Write out the .proto files for Foo and Bar to the given directory"""
    Foo.write_proto_file(proto_dir)
    Bar.write_proto_file(proto_dir)

Similar Projects

There are a number of similar projects in this space that offer slightly different value:

  • jtd-codegen: This project focuses on statically generating language-native code (including python) to represent the JTD schema.
  • py-json-to-proto: This project aims to deduce a schema from an instance of a json object.
  • pure-protobuf: This project has a very similar aim to py-to-proto, but it skips the intermediate descriptor representation and thus is not able to produce native message.Message classes.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

py_to_proto-0.3.1-py311-none-any.whl (32.3 kB view details)

Uploaded Python 3.11

py_to_proto-0.3.1-py310-none-any.whl (32.3 kB view details)

Uploaded Python 3.10

py_to_proto-0.3.1-py39-none-any.whl (32.3 kB view details)

Uploaded Python 3.9

py_to_proto-0.3.1-py38-none-any.whl (32.3 kB view details)

Uploaded Python 3.8

py_to_proto-0.3.1-py37-none-any.whl (32.3 kB view details)

Uploaded Python 3.7

File details

Details for the file py_to_proto-0.3.1-py311-none-any.whl.

File metadata

  • Download URL: py_to_proto-0.3.1-py311-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3.11
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for py_to_proto-0.3.1-py311-none-any.whl
Algorithm Hash digest
SHA256 ee17ea11b98501888261ec2d886b79ae9b86e9792a41912c5950bda3ff76898b
MD5 4e4cd0b556430f5840e776b5ce7fe4ba
BLAKE2b-256 8f11fcf1bb718d0e333efa98c7c5ee486ee000463461281ef047a2b882219cb3

See more details on using hashes here.

File details

Details for the file py_to_proto-0.3.1-py310-none-any.whl.

File metadata

  • Download URL: py_to_proto-0.3.1-py310-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3.10
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.12

File hashes

Hashes for py_to_proto-0.3.1-py310-none-any.whl
Algorithm Hash digest
SHA256 1c4c622067013eb1d7c4bbd76851a889f3f6cd9cb4bc610982dda609c74e3053
MD5 5857834904eec8d2d47900c315a14f94
BLAKE2b-256 1bc38855672be1fe1e47b1ce11a78b380f731d33505201083ca6e3b78957c9cd

See more details on using hashes here.

File details

Details for the file py_to_proto-0.3.1-py39-none-any.whl.

File metadata

  • Download URL: py_to_proto-0.3.1-py39-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3.9
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.17

File hashes

Hashes for py_to_proto-0.3.1-py39-none-any.whl
Algorithm Hash digest
SHA256 c1a10a3910c26582532c7060ecd2517dda9c0c3a4db0affd198d2d3513ed0546
MD5 74d1ee9d51c248da1ee4c74d1ac8a657
BLAKE2b-256 d0c38cc3800a11501108474572a77bba7a04237fd6ec5318c516f83413977674

See more details on using hashes here.

File details

Details for the file py_to_proto-0.3.1-py38-none-any.whl.

File metadata

  • Download URL: py_to_proto-0.3.1-py38-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3.8
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.8.17

File hashes

Hashes for py_to_proto-0.3.1-py38-none-any.whl
Algorithm Hash digest
SHA256 7ce59342f4c8ba019dd13cd0f693893bac57a8d610f52e453693462fd139f1ab
MD5 d77b5ad6034ab92eb7302d2dce54cea6
BLAKE2b-256 4a6a602e98f78e2eda0a6de6f5a33f06eaad17be918fed9c4911d454acb7d912

See more details on using hashes here.

File details

Details for the file py_to_proto-0.3.1-py37-none-any.whl.

File metadata

  • Download URL: py_to_proto-0.3.1-py37-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3.7
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.7.17

File hashes

Hashes for py_to_proto-0.3.1-py37-none-any.whl
Algorithm Hash digest
SHA256 18f8f31955764fb0e31aefbb7d19d5189544fc2d6e74feb42c0f8b4156749ce0
MD5 a0a1faf256df37919b3c9eacd551484d
BLAKE2b-256 e1da70d4dd4fe04864f8d6a18f35f99deda2a6e6d426b377a4bad8eb3a8026f9

See more details on using hashes here.

Supported by

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