protobuf - Registry for Google Protocol Buffer messages and enums

Overview

This module provides a central registry for Google Protocol Buffer message types and enum types generated from proto files. It allows creating message instances and accessing enum information using their fully qualified names (e.g., “my.package.MyMessage”, “my.package.MyEnum”) without needing to directly import the corresponding generated _pb2.py modules throughout the codebase.

Benefits:

  • Decouples code using protobuf messages from the specific generated modules.

  • Provides a single point for managing and discovering available message/enum types.

  • Facilitates dynamic loading of protobuf definitions via entry points.

Core Features:

  • Register message/enum types using their file DESCRIPTOR object.

  • Create new message instances by name using create_message().

  • Access enum descriptors and values by name using get_enum_type().

  • Load protobuf definitions registered by other installed packages via entry points using load_registered().

  • Helpers for common types like google.protobuf.Struct.

Example:

# Assume you have my_proto_pb2.py generated from my_proto.proto
# containing:
# message Sample { required string name = 1; }
# enum Status { UNKNOWN = 0; OK = 1; ERROR = 2; }

from firebird.base.protobuf import (
    register_descriptor, create_message, get_enum_type, is_msg_registered
)
# Import the generated descriptor (only needed once, e.g., at startup)
try:
    from . import my_proto_pb2 # Replace with actual import path
    HAS_MY_PROTO = True
except ImportError:
    HAS_MY_PROTO = False

# 1. Register the types from the descriptor
if HAS_MY_PROTO:
    register_descriptor(my_proto_pb2.DESCRIPTOR)
    print(f"Is 'my_proto.Sample' registered? {is_msg_registered('my_proto.Sample')}")

# 2. Create a message instance by name
if HAS_MY_PROTO:
    try:
        msg = create_message('my_proto.Sample')
        msg.name = "Example"
        print(f"Created message: {msg}")

        # 3. Access enum type and values by name
        status_enum = get_enum_type('my_proto.Status')
        print(f"Status enum name: {status_enum.name}")
        print(f"OK value: {status_enum.OK}") # Access like attribute
        print(f"Name for value 2: {status_enum.get_value_name(2)}") # Access via method
        print(f"Available status keys: {status_enum.keys()}")

    except KeyError as e:
        print(f"Error accessing registered proto type: {e}")

Constants

firebird.base.protobuf.PROTO_EMPTY: str = 'google.protobuf.Empty'

Name of well-known EMPTY protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_ANY: str = 'google.protobuf.Any'

Name of well-known ANY protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_DURATION: str = 'google.protobuf.Duration'

Name of well-known DURATION protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_TIMESTAMP: str = 'google.protobuf.Timestamp'

Name of well-known TIMESTAMP protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_STRUCT: str = 'google.protobuf.Struct'

Name of well-known STRUCT protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_VALUE: str = 'google.protobuf.Value'

Name of well-known VALUE protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_LISTVALUE: str = 'google.protobuf.ListValue'

Name of well-known LISTVALUE protobuf message (for use with create_message())

firebird.base.protobuf.PROTO_FIELDMASK: str = 'google.protobuf.FieldMask'

Name of well-known FIELDMASK protobuf message (for use with create_message())

Functions

firebird.base.protobuf.register_decriptor(file_descriptor) None[source]

Register all message and enum types defined within a protobuf file descriptor.

This is the primary mechanism for adding types to the registry. The descriptor object is typically accessed as DESCRIPTOR from a generated _pb2.py module.

Parameters:

file_descriptor – The google.protobuf.descriptor.FileDescriptor object (e.g., my_proto_pb2.DESCRIPTOR).

Return type:

None

firebird.base.protobuf.load_registered(group: str) None[source]

Load and register protobuf types defined via package entry points.

Searches for installed packages that register entry points under the specified group. Each entry point should load a FileDescriptor object. This allows packages to automatically make their protobuf types available to the registry upon installation.

This function is typically called once during application initialization.

Parameters:

group (str) – The name of the entry-point group to scan (e.g., ‘firebird.base.protobuf’).

Return type:

None

Example

# setup.cfg:

[options.entry_points]
firebird.base.protobuf =
    firebird.base.lib_a = firebird.base.lib_a_pb2:DESCRIPTOR
    firebird.base.lib_b = firebird.base.lib_b_pb2:DESCRIPTOR
    firebird.base.lib_c = firebird.base.lib_c_pb2:DESCRIPTOR

# pyproject.toml

[project.entry-points."firebird.base.protobuf"]
"firebird.base.lib_a" = "firebird.base.lib_a_pb2:DESCRIPTOR"
"firebird.base.lib_b" = "firebird.base.lib_b_pb2:DESCRIPTOR"
"firebird.base.lib_c" = "firebird.base.lib_c_pb2:DESCRIPTOR"

Usage:

# In your application's startup code:
load_registered('firebird.base.protobuf')
# Now messages/enums registered via entry points are available
firebird.base.protobuf.is_msg_registered(name: str) bool[source]

Check if a protobuf message type with the given name is registered.

Parameters:

name (str) – Fully qualified message type name.

Returns:

True if registered, False otherwise.

Return type:

bool

firebird.base.protobuf.is_enum_registered(name: str) bool[source]

Check if a protobuf enum type with the given name is registered.

Parameters:

name (str) – Fully qualified enum type name.

Returns:

True if registered, False otherwise.

Return type:

bool

firebird.base.protobuf.get_enum_type(name: str) ProtoEnumType[source]

Return the ProtoEnumType wrapper for a registered enum type by name.

Provides access to enum members and values via the wrapper object.

Parameters:
  • type. (Fully qualified name of the registered protobuf enum) –

  • name (str) –

Returns:

The ProtoEnumType instance for the requested enum.

Raises:

KeyError – If name does not correspond to a registered enum type.

Return type:

ProtoEnumType

firebird.base.protobuf.get_enum_value_name(enum_type_name: str, value: int) str[source]

Return the string name corresponding to a value within a registered enum type.

Convenience function equivalent to get_enum_type(enum_type_name).get_value_name(value).

Parameters:
  • enum_type_name (str) – Fully qualified name of the registered enum type.

  • value (int) – The integer value of the enum member.

Returns:

The string name of the enum member.

Raises:

KeyError – If enum_type_name is not registered, or if value is not defined within that enum.

Return type:

str

firebird.base.protobuf.create_message(name: str, serialized: bytes | None = None) Message[source]

Create a new instance of a registered protobuf message type by name.

Optionally initializes the message by parsing serialized data.

Parameters:
  • name (str) – Fully qualified name of the registered protobuf message type.

  • serialized (bytes | None) – Optional bytes containing the serialized message data.

Returns:

An instance of the requested protobuf message class.

Raises:
  • KeyError – If name does not correspond to a registered message type.

  • google.protobuf.message.DecodeError – If serialized data is provided but cannot be parsed correctly for the message type.

Return type:

Message

firebird.base.protobuf.get_enum_field_type(msg, field_name: str) str[source]

Return the fully qualified name of the enum type for a message field.

Parameters:
  • msg – An instance of a protobuf message.

  • field_name (str) – The string name of the field within the message.

Returns:

The fully qualified name of the enum type used by the field.

Raises:
  • KeyError – If msg does not have a field named field_name.

  • TypeError – If the specified field is not an enum field.

Return type:

str

firebird.base.protobuf.struct2dict(struct: Struct) dict[str, Any][source]

Unpack a google.protobuf.Struct message into a Python dictionary.

Uses google.protobuf.json_format.MessageToDict.

Parameters:

struct (Struct) – The Struct message instance.

Returns:

A Python dictionary representing the struct’s content.

Return type:

dict[str, Any]

firebird.base.protobuf.dict2struct(value: dict[str, Any]) Struct[source]

Pack a Python dictionary into a google.protobuf.Struct message.

Parameters:

value (dict[str, Any]) – The Python dictionary.

Returns:

A Struct message instance containing the dictionary’s data.

Return type:

Struct

Data classes

class firebird.base.protobuf.ProtoMessageType(name: str, constructor: Callable)[source]

Bases: Distinct

Registry entry representing a registered Protocol Buffer message type.

Stores the fully qualified name and the constructor (the generated class) for a message type, allowing instantiation via the registry.

Parameters:
  • name (str) – Fully qualified message type name (e.g., “package.Message”).

  • constructor (Callable) – The callable (generated message class) used to create instances.

get_key() str[source]

Returns the message name, used as the key in the registry.

Return type:

str

constructor: Callable

The callable (generated message class) used to create instances.

name: str

Fully qualified message type name (e.g., “package.Message”).

class firebird.base.protobuf.ProtoEnumType(descriptor: EnumDescriptor)[source]

Bases: Distinct

Registry entry providing access to a registered Protocol Buffer enum type.

Wraps the EnumDescriptor and provides an API similar to generated enum types, allowing access to names and values without direct import of the generated _pb2 module.

Parameters:

descriptor (EnumDescriptor) – The google.protobuf.descriptor.EnumDescriptor for the enum type.

Example:

# Assuming 'my_proto.Status' enum (UNKNOWN=0, OK=1) is registered
status_enum = get_enum_type('my_proto.Status')

print(status_enum.OK)              # Output: 1 (Access value by name)
print(status_enum.get_value_name(1)) # Output: 'OK' (Get name by value)
print(status_enum.keys())          # Output: ['UNKNOWN', 'OK']
print(status_enum.values())        # Output: [0, 1]
print(status_enum.items())         # Output: [('UNKNOWN', 0), ('OK', 1)]

try:
    print(status_enum.NONEXISTENT)
except AttributeError as e:
    print(e) # Output: Enum my_proto.Status has no value with name 'NONEXISTENT'

try:
    print(status_enum.get_value_name(99))
except KeyError as e:
    print(e) # Output: "Enum my_proto.Status has no name defined for value 99"
__getattr__(name: str)[source]

Return the integer value corresponding to the enum member name name.

Parameters:

name (str) – The string name of the enum member.

Returns:

The integer value of the enum member.

Raises:

AttributeError – If name is not a valid member name for this enum.

get_key() str[source]

Returns the full enum name, used as the key in the registry.

Return type:

str

get_value_name(number: int) str[source]

Return the string name corresponding to the enum member value number.

Parameters:

number (int) – The integer value of the enum member.

Returns:

The string name of the enum member.

Raises:

KeyError – If there is no value for specified name.

Return type:

str

items() list[tuple[str, int]][source]

Return a list of the (name, value) pairs of the enum.

These are returned in the order they were defined in the .proto file.

Return type:

list[tuple[str, int]]

keys() list[str][source]

Return a list of the string names in the enum.

These are returned in the order they were defined in the .proto file.

Return type:

list[str]

values() list[int][source]

Return a list of the integer values in the enum.

These are returned in the order they were defined in the .proto file.

Return type:

list[int]

descriptor: EnumDescriptor

The google.protobuf.descriptor.EnumDescriptor for the enum type.

property name: str

The fully qualified name of the enum type (e.g., “package.MyEnum”).