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
- Advantages
- 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
- Generally, no standard serialization beyond
- Advantages
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 forprotobuf
serialization and deserialization - Reverse render a
.proto
file from the generatedDescriptor
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 (includingpython
) to represent the JTD schema.py-json-to-proto
: This project aims to deduce a schema from an instance of ajson
object.pure-protobuf
: This project has a very similar aim topy-to-proto
, but it skips the intermediatedescriptor
representation and thus is not able to produce nativemessage.Message
classes.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Hashes for py_to_proto-0.5.0-py311-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 234407d8e3ed46d0b38040ebe32746f1548e90c36639203a5e1ac80e34ce6c21 |
|
MD5 | dcd0ad59c789b12bb13c0eb1e9e67634 |
|
BLAKE2b-256 | c6089a498ee3554d39088ed3f551ac9105b581243f95f2fabcee72d68185da67 |
Hashes for py_to_proto-0.5.0-py310-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5c8c1b53250abb2a1f9aaf7e9cc88d4add5cae24cf9c0ee2a9802fca2319d6c1 |
|
MD5 | b1d381668b7e124c0e29ef0a81837cfc |
|
BLAKE2b-256 | de5684e26e4e6d5e18182dac8069a3641f2777fbc9b52702ab3fbea1954c9987 |
Hashes for py_to_proto-0.5.0-py39-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | ba551059f3074e92d7b8ac728dd3bbe9a64c899e72df6b988eebc652846c16d4 |
|
MD5 | 9b6c5682e02b29b8124d341e1db05508 |
|
BLAKE2b-256 | 16afd89b81e9080a6a19476ad55996e1e9215dfd6d1307cf9bef7b831ea01209 |
Hashes for py_to_proto-0.5.0-py38-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 28bad3aaf9c3503dd620d66442b4e4a95cb05ccb6927681b6061c7f0d8f9324b |
|
MD5 | 0f0c50271b744b9b170a3a2dad861293 |
|
BLAKE2b-256 | 5509226680fa84c87232b1f95905312d9ecf25c72e154b8f0b8feaa5e60b4b7d |
Hashes for py_to_proto-0.5.0-py37-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c05a6dae2ff28961326859b6dbb7950deecdcdbcd2def9ce5fadb33f6f54cafa |
|
MD5 | e74c89f849e6df258268d7178258a7fd |
|
BLAKE2b-256 | b3782f55ad1172d2c7bd0557e72c92b28e8347a6e11bdf179e9f49695e64f255 |