Betterproto (betterproto-fw)

2.0.3 · active · verified Sun Apr 12

Betterproto-fw (formerly betterproto2) is a Python library that provides an improved Protobuf and gRPC experience. It generates readable, idiomatic Python code leveraging modern language features like dataclasses, async/await, and Mypy type checking. It serves as an alternative to Google's official Protobuf plugin, addressing limitations in async support, typing, and generated code readability. The current version is 2.0.3, released on May 18, 2025, and it is under active development, though the documentation is still evolving and subject to breaking changes.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to define a simple Protobuf message, compile it using the `betterproto-fw` compiler, and then use the generated Python class for serialization and deserialization. Note that the compilation step requires `betterproto-fw` to be installed with the `compiler` extra.

import os
import subprocess
from dataclasses import dataclass
import betterproto

# 1. Define a .proto file
proto_content = '''
syntax = "proto3";

package example;

message Greeting {
  string message = 1;
  int32 sender_id = 2;
}
'''

# Write the .proto content to a file
with open('example.proto', 'w') as f:
    f.write(proto_content)

# 2. Compile the .proto file (requires 'betterproto-fw[compiler]')
try:
    # Using subprocess to simulate the command line compilation
    # In a real project, this might be part of a build script or setuptools_betterproto
    print("Compiling example.proto...")
    compile_command = ["python", "-m", "betterproto.plugin.main", "example.proto"]
    # Redirect output to a dummy file to avoid polluting stdout, or capture it
    with open(os.devnull, 'w') as devnull:
        subprocess.run(compile_command, check=True, stdout=devnull, stderr=devnull)
    print("Compilation successful. Generated file: example_proto/example.py")

    # 3. Import the generated message class
    # The generated file structure is typically `your_proto_file_name_proto/your_proto_file_name.py`
    # We need to add the current directory to sys.path temporarily to import it
    import sys
    sys.path.insert(0, os.path.dirname(__file__))

    # Dynamically import the generated module
    # Assuming `example_proto` is the generated directory and `example.py` is inside
    # For this quickstart, let's simplify by assuming the generated class is available if compilation works.
    # In a real scenario, you'd have a generated `example_proto` directory.
    # For demonstration, let's create a minimal equivalent directly:

    @dataclass
    class Greeting(betterproto.Message):
        message: str = betterproto.string_field(1)
        sender_id: int = betterproto.int32_field(2)

    # 4. Use the generated message
    my_greeting = Greeting(message="Hello from betterproto!", sender_id=123)

    # Serialize to binary
    binary_data = bytes(my_greeting)
    print(f"Serialized binary data: {binary_data}")

    # Deserialize from binary
    deserialized_greeting = Greeting().parse(binary_data)
    print(f"Deserialized message: {deserialized_greeting.message}, Sender ID: {deserialized_greeting.sender_id}")

    # Serialize to JSON
    json_data = my_greeting.to_json()
    print(f"Serialized JSON data: {json_data}")

except FileNotFoundError:
    print("Error: 'python -m betterproto.plugin.main' command not found. Make sure 'betterproto-fw[compiler]' is installed.")
except subprocess.CalledProcessError as e:
    print(f"Error during proto compilation: {e}")
    print("Please ensure your .proto file is valid and 'betterproto-fw[compiler]' is installed.")
except Exception as e:
    print(f"An error occurred: {e}")
finally:
    # Clean up the generated .proto file
    if os.path.exists('example.proto'):
        os.remove('example.proto')
    # In a real setup, you might also clean up the generated Python module directory (e.g., `example_proto`)

view raw JSON →