Install & Compatibility
Where this runs
tested against v2.0.3 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.910 runs
installs and imports cleanly · install 0.0s · import 0.381s · 23.8MB
glibcpy 3.10–3.910 runs
installs and imports cleanly · install 2.9s · import 0.342s · 25MB
22MB installed
● package 22MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Message
✓ from betterproto import Message
✗ import betterproto
ABC
✓ from betterproto import ABC
✗ import betterproto
Enum
✓ from betterproto import Enum
✗ import betterproto
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`)
Debug
Known issues
breakingBetterproto-fw is not a 1:1 drop-in replacement for Google's official Python Protobuf plugin. Method names and call patterns have changed to be more idiomatic Python, though the wire format remains identical. Code written for the official plugin will require migration.fixReview the betterproto-fw documentation and examples to adapt your code. Use `bytes(message)` for serialization instead of `SerializeToString()` and `message.parse(data)` for deserialization instead of `FromString()` for idiomatic usage.
affects: All versions (fundamental design)
breakingAs of version 2.0.0b7 (and thus 2.0.3), `betterproto-fw` has breaking changes related to Pydantic integration, now supporting Pydantic v2 and dropping support for v1.fixUpgrade Pydantic to version 2 or higher if you are using Pydantic models with betterproto-fw. Review Pydantic v2 migration guides if necessary.
affects: >=2.0.0b7
breakingAccessing an unset `oneof` field directly will now raise an `AttributeError` instead of returning a default value.fixUse `betterproto.which_one_of(message_instance, 'oneof_group_name')` to safely check and access `oneof` fields.
affects: >=2.0.0b7
breakingBetterproto-fw implements a custom `Enum` class. Checks like `isinstance(enum_member, enum.Enum)` or `issubclass(EnumSubclass, enum.Enum)` will now return `False`. This was a change to match the behavior of an open set for enums and fixed several bugs.fixAdjust type checks for betterproto-fw generated enums. Direct equality checks `enum_member == MyEnum.VALUE` or checking against the custom base `betterproto.Enum` class should still work.
affects: >=2.0.0b7
gotchaTo determine if a Protobuf message field was explicitly sent on the wire (especially relevant for wrapper types), use `betterproto.serialized_on_wire(message_instance)`. This differs from patterns in Google's official generated code. Note it only supports Proto 3 message fields, not scalar fields.fixAlways use `betterproto.serialized_on_wire()` when you need to distinguish between an unset field and a field set to its default (zero) value.
affects: All versions
Upgrade
Version history
2.0.3latest on PyPI · released May 18, 2025
Audit
Dependencies
setuptools-betterprotooptionalOptional build dependency for automatic .proto compilation in setuptools-based projects.