Betterproto 2
Betterproto2 is a fork of the original betterproto library, providing an improved experience for Protobuf and gRPC in modern Python environments. It generates readable, idiomatic Python code leveraging features like dataclasses, async/await, and type hinting. While currently in active development with a release cadence of minor versions, its documentation is still evolving, and the project is subject to breaking changes. It supports Protobuf 3 & gRPC code generation, with built-in binary and JSON serialization.
Warnings
- breaking Betterproto2 is a redesign of the original betterproto library and is not a 1:1 drop-in replacement. While the wire format is identical, method names and call patterns have changed, requiring code updates for migration.
- gotcha The project is still under active development, and the documentation is incomplete. Users should be aware that the library is still subject to breaking changes.
- gotcha To generate Python code, you must have the `protoc` compiler installed and accessible in your PATH, or install `grpcio-tools` to use its bundled `protoc`. The `betterproto2[compiler]` extra installs necessary Python dependencies for the `protoc` plugin but not `protoc` itself.
- breaking Accessing an unset `oneof` field now raises an `AttributeError`. Previously, it might have returned a default or `None` without an error.
- breaking Custom `Enum` implementations in betterproto2 do not behave like standard `enum.Enum` for `isinstance()` or `issubclass()` checks. This also affects direct passthrough of `Enum` members.
Install
-
pip install betterproto2 -
pip install "betterproto2[compiler]"
Imports
- Message
import betterproto
- GeneratedMessage
from my_proto_package.my_proto_file import MyMessage
Quickstart
# 1. Define your .proto file (e.g., example.proto)
# syntax = "proto3";
# package hello;
# message Greeting {
# string message = 1;
# }
import os
import subprocess
import sys
from pathlib import Path
# Create a dummy .proto file for demonstration
proto_content = """
syntax = "proto3";
package hello;
message Greeting {
string message = 1;
}
"""
proto_dir = Path("./temp_proto_gen")
proto_dir.mkdir(exist_ok=True)
proto_file = proto_dir / "example.proto"
proto_file.write_text(proto_content)
output_dir = Path("temp_betterproto_out")
output_dir.mkdir(exist_ok=True)
# 2. Generate Python code using protoc
try:
# Using grpcio_tools if available, otherwise direct protoc
if subprocess.run([sys.executable, '-m', 'grpc_tools.protoc', '--version'], capture_output=True, check=False).returncode == 0:
print("Using grpcio_tools.protoc")
subprocess.run(
[
sys.executable,
'-m',
'grpc_tools.protoc',
f'-I={proto_dir}',
f'--python_betterproto_out={output_dir}',
str(proto_file)
],
check=True
)
else:
print("Using system protoc (ensure it's installed)")
subprocess.run(
[
'protoc',
f'-I={proto_dir}',
f'--python_betterproto_out={output_dir}',
str(proto_file)
],
check=True
)
sys.path.insert(0, str(output_dir))
from temp_proto_gen.example import Greeting # Adjusted import based on package and file
# 3. Use the generated classes
greeting_instance = Greeting(message="Hello betterproto2!")
print(f"Created message: {greeting_instance}")
# Serialize to bytes
serialized_data = bytes(greeting_instance)
print(f"Serialized: {serialized_data}")
# Deserialize from bytes
deserialized_instance = Greeting().parse(serialized_data)
print(f"Deserialized: {deserialized_instance}")
# Convert to dict and JSON
print(f"To dict: {deserialized_instance.to_dict()}")
print(f"To JSON: {deserialized_instance.to_json(indent=2)}")
except Exception as e:
print(f"Error during quickstart: {e}")
print("Ensure 'protoc' is installed or 'pip install grpcio-tools'")
finally:
# Clean up generated files and directories
import shutil
if proto_dir.exists():
shutil.rmtree(proto_dir)
if output_dir.exists():
shutil.rmtree(output_dir)
if str(output_dir) in sys.path:
sys.path.remove(str(output_dir))