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

message-system.mddocs/

Message System

The Message system provides the core functionality for defining protocol buffer messages that behave like native Python classes. Messages automatically handle serialization, deserialization, type conversion, and provide a Pythonic interface for working with structured data.

Capabilities

Message Definition

Define protocol buffer messages as Python classes using the Message base class. The metaclass automatically generates protocol buffer descriptors and provides seamless integration.

class Message(metaclass=MessageMeta):
    """
    Abstract base class for protocol buffer messages.
    
    Args:
        mapping (Union[dict, Message]): A dictionary or message to be used to 
            determine the values for this message.
        ignore_unknown_fields (Optional[bool]): If True, do not raise errors for 
            unknown fields. Only applied if mapping is a mapping type or there 
            are keyword parameters.
        kwargs (dict): Keys and values corresponding to the fields of the message.
    """
    def __init__(self, mapping=None, *, ignore_unknown_fields=False, **kwargs): ...

Example usage:

class Address(proto.Message):
    street = proto.Field(proto.STRING, number=1)
    city = proto.Field(proto.STRING, number=2)
    zip_code = proto.Field(proto.STRING, number=3)

class Person(proto.Message):
    name = proto.Field(proto.STRING, number=1)
    age = proto.Field(proto.INT32, number=2)
    address = proto.Field(Address, number=3)

# Create message instances
person = Person(
    name="John Doe",
    age=25,
    address={"street": "123 Main St", "city": "Anytown", "zip_code": "12345"}
)

# Or using keyword arguments
person = Person(name="Jane Doe", age=30)

Protocol Buffer Integration

Access the underlying protocol buffer objects and perform low-level operations when needed.

@classmethod
def pb(cls, obj=None, *, coerce: bool = False):
    """
    Return the underlying protobuf Message class or instance.
    
    Args:
        obj: If provided, and an instance of cls, return the underlying 
             protobuf instance.
        coerce (bool): If provided, will attempt to coerce obj to cls 
                      if it is not already an instance.
    
    Returns:
        The protobuf class (if obj is None) or protobuf instance
    """

@classmethod  
def wrap(cls, pb):
    """
    Return a Message object that shallowly wraps the descriptor.
    
    Args:
        pb: A protocol buffer object, such as would be returned by pb().
    
    Returns:
        Message instance wrapping the protobuf object
    """

Example usage:

# Get the underlying protobuf class
pb_class = Person.pb()

# Get the underlying protobuf instance  
pb_instance = Person.pb(person)

# Wrap an existing protobuf object
wrapped = Person.wrap(pb_instance)

Serialization and Deserialization

Convert messages to and from binary format for storage and transmission.

@classmethod
def serialize(cls, instance) -> bytes:
    """
    Return the serialized proto.
    
    Args:
        instance: An instance of this message type, or something compatible 
                 (accepted by the type's constructor).
    
    Returns:
        bytes: The serialized representation of the protocol buffer.
    """

@classmethod
def deserialize(cls, payload: bytes) -> "Message":
    """
    Given a serialized proto, deserialize it into a Message instance.
    
    Args:
        payload (bytes): The serialized proto.
    
    Returns:
        Message: An instance of the message class against which this 
                method was called.
    """

Example usage:

# Serialize to bytes
data = Person.serialize(person)

# Deserialize from bytes
person_copy = Person.deserialize(data)

JSON Conversion

Convert messages to and from JSON format with extensive customization options.

@classmethod
def to_json(cls, instance, *, use_integers_for_enums=True, 
            including_default_value_fields=None, preserving_proto_field_name=False,
            sort_keys=False, indent=2, float_precision=None,
            always_print_fields_with_no_presence=None) -> str:
    """
    Given a message instance, serialize it to json.
    
    Args:
        instance: An instance of this message type, or something compatible.
        use_integers_for_enums (Optional[bool]): Whether enum values should be 
            represented by strings (False) or integers (True). Default is True.
        including_default_value_fields (Optional[bool]): If True, include fields 
            that are set to their default values in the output.
        preserving_proto_field_name (Optional[bool]): Whether field name 
            representations preserve proto case (snake_case) or use lowerCamelCase.
        sort_keys (Optional[bool]): If True, then the output will be sorted by field names.
        indent (Optional[int]): The JSON object will be pretty-printed with this indent level.
        float_precision (Optional[int]): If set, use this to specify float field valid digits.
        always_print_fields_with_no_presence (Optional[bool]): If True, fields without 
            presence will always be serialized.
    
    Returns:
        str: The json string representation of the protocol buffer.
    """

@classmethod
def from_json(cls, payload, *, ignore_unknown_fields=False) -> "Message":
    """
    Given a json string representing an instance, parse it into a message.
    
    Args:
        payload: A json string representing a message.
        ignore_unknown_fields (Optional[bool]): If True, do not raise errors 
            for unknown fields.
    
    Returns:
        Message: An instance of the message class against which this method was called.
    """

Example usage:

# Convert to JSON
json_str = Person.to_json(person, indent=4, sort_keys=True)

# Parse from JSON
person_from_json = Person.from_json(json_str)

Dictionary Conversion

Convert messages to and from Python dictionaries for easy integration with Python data structures.

@classmethod  
def to_dict(cls, instance, *, use_integers_for_enums=True,
            preserving_proto_field_name=True, including_default_value_fields=None,
            float_precision=None, always_print_fields_with_no_presence=None) -> dict:
    """
    Given a message instance, return its representation as a python dict.
    
    Args:
        instance: An instance of this message type, or something compatible.
        use_integers_for_enums (Optional[bool]): Whether enum values should be 
            represented by strings (False) or integers (True). Default is True.
        preserving_proto_field_name (Optional[bool]): Whether field name 
            representations preserve proto case (snake_case) or use lowerCamelCase.
        including_default_value_fields (Optional[bool]): If True, include fields 
            that are set to their default values in the output.
        float_precision (Optional[int]): If set, use this to specify float field valid digits.
        always_print_fields_with_no_presence (Optional[bool]): If True, fields without 
            presence will always be serialized.
    
    Returns:
        dict: A representation of the protocol buffer using pythonic data structures.
    """

Example usage:

# Convert to dictionary  
person_dict = Person.to_dict(person)

# Create from dictionary
person_from_dict = Person(person_dict)

Message Copying

Copy data from other messages or dictionaries into existing message instances.

@classmethod
def copy_from(cls, instance, other):
    """
    Equivalent for protobuf.Message.CopyFrom.
    
    Args:
        instance: An instance of this message type
        other (Union[dict, Message]): A dictionary or message to reinitialize 
            the values for this message.
    """

Example usage:

person1 = Person(name="Alice", age=25)
person2 = Person()

# Copy data from another message
Person.copy_from(person2, person1)

# Copy data from a dictionary
Person.copy_from(person2, {"name": "Bob", "age": 30})

Message Behavior

Messages behave like native Python objects with standard Python protocols:

  • Attribute access: person.name, person.age
  • Containment: 'name' in person checks if field was set
  • Boolean conversion: bool(person) returns True if any field is set
  • Equality: person1 == person2 compares message contents
  • String representation: str(person) and repr(person) show message contents
  • Attribute setting: person.name = "New Name"
  • Attribute deletion: del person.name clears the field

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