CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-proto-plus

Beautiful, Pythonic protocol buffers that make protocol buffer message classes behave like native Python types

Pending
Overview
Eval results
Files

field-system.mddocs/

Field System

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.

Capabilities

Basic Field Definition

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)

Repeated Fields

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)

Map Fields

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}")

Field Types and Protocol Buffer Integration

Primitive Types

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)

Message Types

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)

Enum Types

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)

Oneof Fields

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 oneof

Optional Fields

Mark 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 string

Advanced Field Features

JSON Field Names

Customize 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"

Field Validation and Conversion

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

docs

enum-system.md

field-system.md

index.md

marshal-system.md

message-system.md

module-system.md

tile.json