Protobuf to Pydantic
protobuf-to-pydantic is a Python library that generates `pydantic.BaseModel` classes with parameter verification functions directly from Protobuf files (proto3 syntax). It supports both runtime object generation and plugin-based code generation, enabling robust data validation for Protobuf messages within Python applications. The library is actively maintained with frequent minor releases, currently at version 0.3.3.1.
Common errors
-
ModuleNotFoundError: No module named 'protobuf_to_pydantic.desc_template'
cause Attempting to import `DescTemplate` after the `v0.3.0` release, where its import path was changed to `Template`.fixChange the import statement to `from protobuf_to_pydantic.template import Template`. -
FileNotFoundError: [Errno 2] No such file or directory: 'protoc'
cause The `protoc` command-line tool, required for compiling `.proto` files in plugin mode, is not installed or not found in the system's PATH.fixInstall the Protocol Buffer Compiler (`protoc`) for your operating system and ensure its executable is discoverable in your system's PATH environment variable. -
TypeError: 'Struct' object cannot be interpreted as a dict (or similar error when passing google.protobuf.struct_pb2.Struct)
cause Older versions of the library might have had issues correctly processing `google.protobuf.Struct` types into Pydantic models. [cite: GitHub v0.2.5 fix]fixUpgrade to `protobuf-to-pydantic` version `0.2.5` or newer, which includes a fix for handling `google.protobuf.Struct` types correctly.
Warnings
- breaking The import path for custom template functionality changed significantly in `v0.3.0`. Older code using `DescTemplate` will break.
- breaking Version `0.2.7` included urgent fixes that may have introduced breaking changes in internal APIs, impacting custom integrations or advanced usage.
- gotcha For 'Plugin Mode' (generating code from `.proto` files using `protoc`), the `protoc` compiler must be installed and accessible in your system's PATH. The library itself does not bundle `protoc`.
Install
-
pip install protobuf-to-pydantic -
pip install protobuf-to-pydantic[mypy-protobuf]
Imports
- msg_to_pydantic_model
from protobuf_to_pydantic import msg_to_pydantic_model
- Template
from protobuf_to_pydantic.desc_template import DescTemplate
from protobuf_to_pydantic.template import Template
Quickstart
import sys
from typing import Type
from pydantic import BaseModel, Field
from google.protobuf.message import Message
from google.protobuf import descriptor_pool, descriptor_pb2
from protobuf_to_pydantic import msg_to_pydantic_model
# Simulate a generated protobuf message `demo_pb2.UserMessage`
# In a real scenario, this would come from `import demo_pb2`
pool = descriptor_pool.Default()
descriptor = descriptor_pb2.DescriptorProto()
descriptor.name = 'UserMessage'
descriptor.field.add(name='uid', number=1, type=descriptor_pb2.FieldDescriptorProto.TYPE_STRING)
descriptor.field.add(name='age', number=2, type=descriptor_pb2.FieldDescriptorProto.TYPE_INT32)
descriptor.field.add(name='user_name', number=3, type=descriptor_pb2.FieldDescriptorProto.TYPE_STRING)
pool.Add(descriptor)
# Create a mock Message class based on the descriptor
class UserMessage(Message):
def __init__(self, uid='', age=0, user_name='', **kwargs):
super().__init__(**kwargs)
self.uid = uid
self.age = age
self.user_name = user_name
DESCRIPTOR = pool.FindMessageTypeByName('UserMessage')
# Convert the Protobuf Message object to a Pydantic BaseModel
UserModel: Type[BaseModel] = msg_to_pydantic_model(UserMessage)
# Example usage of the generated Pydantic model
user_data = UserModel(uid='123', age=30, user_name='John Doe')
print(user_data.model_dump_json(indent=2))
# Access field info
# print({k: v.field_info for k, v in UserModel.__fields__.items()}) # For Pydantic v1
print({k: v.json_schema_extra for k, v in UserModel.model_fields.items()}) # For Pydantic v2+