protoc-gen-pydantic
is a protoc
plugin that automatically generates Pydantic model definitions from .proto
files. This tool is designed to help developers seamlessly integrate protobuf-defined models with Pydantic, a powerful data validation and settings management library for Python.
- Supports all standard
proto3
field types. - Handles nested messages and enums.
- Generates Pydantic models with type annotations and field descriptions.
- Supports
oneof
,optional
,repeated
, andmap
fields. - Retains comments from
.proto
files as docstrings in the generated models.
You can download the binaries from GitHub Releases.
You first need to have Go installed. If you don't have Go installed, you can download it from the Go downloads page.
Clone the repository and build the plugin:
git clone https://github.com/ornew/protoc-gen-pydantic
cd protoc-gen-pydantic
go build -o protoc-gen-pydantic main.go
To generate Pydantic model definitions, use protoc
with your .proto
files specifying --pydantic_out
:
protoc --plugin=protoc-gen-pydantic=./protoc-gen-pydantic \
--pydantic_out=./gen \
--proto_path=./proto_files \
./api/example.proto
If you use buf:
# buf.gen.yaml
version: v2
plugins:
- local: protoc-gen-pydantic
opt:
- paths=source_relative
out: gen
inputs:
- directory: api
buf config init
buf generate
Given a simple .proto
file:
syntax = "proto3";
package example;
// User model representing the example.User message.
message User {
string name = 1;
int32 age = 2;
repeated string emails = 3;
bool is_active = 4;
}
Running protoc
will generate a Pydantic model like this:
from enum import Enum as _Enum
from typing import Optional as _Optional
from pydantic import BaseModel as _BaseModel, Field as _Field
class User(_BaseModel):
"""
User model representing the example.User message.
"""
name: str = _Field(...)
age: int = _Field(...)
emails: list[str] = _Field(...)
is_active: bool = _Field(...)
option | description |
---|---|
preserving_proto_field_name |
Use the proto field naming for the output field name. If false , it will be in caml-case according to protojson rules. Defaults to false . |
auto_trim_enum_prefix |
Automatically remove prefixes from enum fields. Defaults to true |
use_integers_for_enums |
Use integers for enum values instead of enum names. Defaults to false . |
disable_field_description |
Disable generating the field description. Defaults to false . |
use_none_union_syntax_instead_of_optional |
Use `T |
enum Status {
STATUS_UNSPECIFIED = 0;
STATUS_OK = 1;
STATUS_ERROR = 2;
}
If auto_trim_enum_prefix
is false
:
class Status(str, _Enum):
STATUS_UNSPECIFIED = "UNSPECIFIED"
STATUS_OK = "OK"
STATUS_ERROR = "ERROR"
If auto_trim_enum_prefix
is true
(default):
class Status(str, _Enum):
UNSPECIFIED = "UNSPECIFIED"
OK = "OK"
ERROR = "ERROR"
If use_integers_for_enums
is false
:
class Status(str, _Enum):
UNSPECIFIED = "UNSPECIFIED"
OK = "OK"
ERROR = "ERROR"
If use_integers_for_enums
is true
:
class Status(int, _Enum):
UNSPECIFIED = 0
OK = 1
ERROR = 2
If disable_field_description
is true
:
message User {
// User name
string name = 1;
}
class User(_BaseModel):
# User name
name: str = _Field(..., description="User name")
If disable_field_description
is false
:
class User(_BaseModel):
# User name
name: str = _Field(...)
If use_none_union_syntax_instead_of_optional
is true
: Optional[T]
If use_none_union_syntax_instead_of_optional
is false
: T | None
Contributions are welcome! Please open an issue or submit a pull request with your changes.
This project is licensed under the Apache License 2.0. See LICENSE for more details.
If you have any questions, feel free to reach out to the author:
- Name: Arata Furukawa
- Email: old.river.new@gmail.com