Beautiful, Pythonic protocol buffers that make protocol buffer message classes behave like native Python types
—
The Field system provides the building blocks for defining message structure in proto-plus. Fields define the data members of protocol buffer messages, handling type validation, serialization, and providing the mapping between Python attributes and protocol buffer fields.
Define individual fields within protocol buffer messages using the Field class.
class Field:
"""
A representation of a type of field in protocol buffers.
Args:
proto_type: The protocol buffer type (ProtoType enum value or message/enum class)
number (int): The field number in the protocol buffer definition
message: Message class for MESSAGE type fields
enum: Enum class for ENUM type fields
oneof (str): Name of oneof group this field belongs to
json_name (str): Custom JSON field name
optional (bool): Whether this is an optional field (proto3)
"""
def __init__(self, proto_type, *, number: int, message=None, enum=None,
oneof: str = None, json_name: str = None, optional: bool = False): ...
@property
def name(self) -> str:
"""Return the name of the field."""
@property
def package(self) -> str:
"""Return the package of the field."""
@property
def pb_type(self):
"""Return the composite type of the field, or the primitive type if a primitive."""
@property
def descriptor(self):
"""Return the descriptor for the field."""Example usage:
class Product(proto.Message):
# Primitive fields
name = proto.Field(proto.STRING, number=1)
price = proto.Field(proto.FLOAT, number=2)
in_stock = proto.Field(proto.BOOL, number=3)
# Optional field
description = proto.Field(proto.STRING, number=4, optional=True)
# Custom JSON name
product_id = proto.Field(proto.STRING, number=5, json_name="productId")
# Message field
category = proto.Field(Category, number=6)
# Enum field
status = proto.Field(ProductStatus, number=7)Define fields that can contain multiple values using RepeatedField.
class RepeatedField(Field):
"""
A representation of a repeated field in protocol buffers.
Inherits all parameters from Field. The resulting field will contain
a sequence of values of the specified type.
"""Example usage:
class Order(proto.Message):
# Repeated primitive fields
item_ids = proto.RepeatedField(proto.STRING, number=1)
quantities = proto.RepeatedField(proto.INT32, number=2)
# Repeated message fields
line_items = proto.RepeatedField(LineItem, number=3)
# Repeated enum fields
statuses = proto.RepeatedField(OrderStatus, number=4)
# Usage
order = Order(
item_ids=["item1", "item2", "item3"],
quantities=[1, 2, 1],
line_items=[
LineItem(name="Widget", price=10.0),
LineItem(name="Gadget", price=25.0)
]
)
# Access like Python lists
print(len(order.item_ids)) # 3
order.item_ids.append("item4")
for item_id in order.item_ids:
print(item_id)Define key-value mapping fields using MapField.
class MapField(Field):
"""
A representation of a map field in protocol buffers.
Args:
key_type: The protocol buffer type for map keys
value_type: The protocol buffer type for map values
number (int): The field number in the protocol buffer definition
message: Message class for MESSAGE type values
enum: Enum class for ENUM type values
"""
def __init__(self, key_type, value_type, *, number: int, message=None, enum=None): ...Example usage:
class Configuration(proto.Message):
# String to string map
settings = proto.MapField(proto.STRING, proto.STRING, number=1)
# String to integer map
limits = proto.MapField(proto.STRING, proto.INT32, number=2)
# String to message map
services = proto.MapField(proto.STRING, ServiceConfig, number=3)
# String to enum map
permissions = proto.MapField(proto.STRING, PermissionLevel, number=4)
# Usage
config = Configuration(
settings={
"debug": "true",
"log_level": "info"
},
limits={
"max_connections": 100,
"timeout_seconds": 30
}
)
# Access like Python dictionaries
print(config.settings["debug"]) # "true"
config.settings["new_setting"] = "value"
for key, value in config.limits.items():
print(f"{key}: {value}")All protocol buffer primitive types are supported:
# Numeric types
age = proto.Field(proto.INT32, number=1)
balance = proto.Field(proto.FLOAT, number=2)
precise_value = proto.Field(proto.DOUBLE, number=3)
user_id = proto.Field(proto.UINT64, number=4)
# String and bytes
name = proto.Field(proto.STRING, number=5)
data = proto.Field(proto.BYTES, number=6)
# Boolean
is_active = proto.Field(proto.BOOL, number=7)Reference other proto-plus messages:
class Address(proto.Message):
street = proto.Field(proto.STRING, number=1)
city = proto.Field(proto.STRING, number=2)
class Person(proto.Message):
name = proto.Field(proto.STRING, number=1)
# Message field - reference another message type
address = proto.Field(Address, number=2)Reference proto-plus enums:
class Priority(proto.Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
class Task(proto.Message):
title = proto.Field(proto.STRING, number=1)
# Enum field - reference an enum type
priority = proto.Field(Priority, number=2)Group fields into oneof constructs where only one field can be set:
class Contact(proto.Message):
name = proto.Field(proto.STRING, number=1)
# Oneof group - only one of these can be set
email = proto.Field(proto.STRING, number=2, oneof="contact_method")
phone = proto.Field(proto.STRING, number=3, oneof="contact_method")
address = proto.Field(Address, number=4, oneof="contact_method")
# Usage
contact = Contact(name="John", email="john@example.com")
# contact.phone would be unset since email is set in the same oneofMark fields as optional in proto3:
class User(proto.Message):
# Required fields (will have default values if not set)
username = proto.Field(proto.STRING, number=1)
# Optional field (can distinguish between unset and default value)
middle_name = proto.Field(proto.STRING, number=2, optional=True)
age = proto.Field(proto.INT32, number=3, optional=True)
# Usage
user = User(username="alice")
print("middle_name" in user) # False - not set
print("username" in user) # True - set
user.middle_name = ""
print("middle_name" in user) # True - set to empty stringCustomize JSON serialization field names:
class Product(proto.Message):
internal_id = proto.Field(proto.STRING, number=1, json_name="productId")
display_name = proto.Field(proto.STRING, number=2, json_name="displayName")
# JSON will use "productId" and "displayName" instead of "internal_id" and "display_name"Fields automatically handle type validation and conversion:
class Stats(proto.Message):
count = proto.Field(proto.INT32, number=1)
# Automatic conversion
stats = Stats(count="123") # String converted to int
print(stats.count) # 123 (integer)
# Type validation
try:
stats = Stats(count="invalid") # Raises error
except ValueError:
print("Invalid value")Install with Tessl CLI
npx tessl i tessl/pypi-proto-plus