{"id":45903398,"url":"https://github.com/cjermain/protoc-gen-pydantic","last_synced_at":"2026-05-03T15:03:43.549Z","repository":{"id":338470162,"uuid":"1157599552","full_name":"cjermain/protoc-gen-pydantic","owner":"cjermain","description":"protoc plugin for Pydantic BaseModel","archived":false,"fork":false,"pushed_at":"2026-03-02T01:35:34.000Z","size":1334,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-02T05:17:09.507Z","etag":null,"topics":["protobuf","protoc","protocol-buffers","protovalidate","pydantic","pydantic-v2"],"latest_commit_sha":null,"homepage":"https://cjermain.github.io/protoc-gen-pydantic/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"ornew/protoc-gen-pydantic","license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cjermain.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-14T02:42:27.000Z","updated_at":"2026-03-02T01:30:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cjermain/protoc-gen-pydantic","commit_stats":null,"previous_names":["cjermain/protoc-gen-pydantic"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/cjermain/protoc-gen-pydantic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjermain%2Fprotoc-gen-pydantic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjermain%2Fprotoc-gen-pydantic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjermain%2Fprotoc-gen-pydantic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjermain%2Fprotoc-gen-pydantic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cjermain","download_url":"https://codeload.github.com/cjermain/protoc-gen-pydantic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjermain%2Fprotoc-gen-pydantic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30071932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T03:25:38.285Z","status":"ssl_error","status_checked_at":"2026-03-04T03:25:05.086Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["protobuf","protoc","protocol-buffers","protovalidate","pydantic","pydantic-v2"],"created_at":"2026-02-28T00:55:11.256Z","updated_at":"2026-05-03T15:03:43.532Z","avatar_url":"https://github.com/cjermain.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# protoc-gen-pydantic\n\n[![CI](https://github.com/cjermain/protoc-gen-pydantic/actions/workflows/ci.yml/badge.svg)](https://github.com/cjermain/protoc-gen-pydantic/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/cjermain/protoc-gen-pydantic/branch/main/graph/badge.svg)](https://codecov.io/gh/cjermain/protoc-gen-pydantic)\n[![Go Report Card](https://goreportcard.com/badge/github.com/cjermain/protoc-gen-pydantic)](https://goreportcard.com/report/github.com/cjermain/protoc-gen-pydantic)\n[![Go Reference](https://pkg.go.dev/badge/github.com/cjermain/protoc-gen-pydantic.svg)](https://pkg.go.dev/github.com/cjermain/protoc-gen-pydantic)\n[![Release](https://img.shields.io/github/v/release/cjermain/protoc-gen-pydantic)](https://github.com/cjermain/protoc-gen-pydantic/releases/latest)\n[![Go version](https://img.shields.io/github/go-mod/go-version/cjermain/protoc-gen-pydantic)](go.mod)\n[![License](https://img.shields.io/github/license/cjermain/protoc-gen-pydantic)](LICENSE)\n[![Pydantic v2](https://img.shields.io/badge/pydantic-v2-blue)](https://docs.pydantic.dev/)\n[![buf](https://img.shields.io/badge/buf-compatible-blue)](https://buf.build/)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://pre-commit.com/)\n[![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://cjermain.github.io/protoc-gen-pydantic/)\n\n`protoc-gen-pydantic` is a `protoc` plugin that generates [Pydantic v2](https://docs.pydantic.dev/) model\ndefinitions from `.proto` files — so your schema stays the single source of truth.\n\nIf you work with Protobuf APIs in Python, the usual tradeoff is: use raw `_pb2` classes (no validation,\nno editor support) or hand-write parallel Pydantic models and keep them in sync forever.\n`protoc-gen-pydantic` eliminates that tradeoff: run `buf generate` once and get type-safe, validated\nPython models automatically.\n\n**Full documentation:** [cjermain.github.io/protoc-gen-pydantic](https://cjermain.github.io/protoc-gen-pydantic/)\n\n\u003e Forked from [ornew/protoc-gen-pydantic](https://github.com/ornew/protoc-gen-pydantic) by [Arata Furukawa](https://github.com/ornew), which provided the initial plugin structure and plugin options. This fork adds well-known type mappings, Python builtin/keyword alias handling, cross-package references, enum value options, ProtoJSON-compatible output, conditional imports, and a test suite.\n\n## How it works\n\nRun `buf generate` (or `protoc`) once. The plugin reads your `.proto` files and writes ready-to-use\nPython files alongside them. No runtime dependency on the plugin — only on Pydantic.\n\n```\n.proto  →  buf generate  →  *_pydantic.py + _proto_types.py  →  import and use\n```\n\n## Features\n\n- Supports all standard `proto3` field types\n- Generates true Python nested classes for nested messages and enums (e.g. `Foo.NestedMessage`)\n- Generates Pydantic models with type annotations and field descriptions\n- Supports `oneof`, `optional`, `repeated`, and `map` fields; `oneof` exclusivity is enforced at runtime via a generated `@model_validator`\n- Retains comments from `.proto` files as docstrings in the generated models\n- Maps well-known types to native Python types (e.g. `Timestamp` → `datetime`, `Struct` → `dict[str, Any]`)\n- Handles Python builtin/keyword shadowing with PEP 8 trailing underscore aliases\n- Resolves cross-package message references\n- Preserves enum value options (built-in `deprecated`/`debug_redact` and custom extensions) as accessible metadata on enum members\n- Translates [buf.validate (protovalidate)](https://github.com/bufbuild/protovalidate) field constraints to native Pydantic constructs\n- **Transpiles `buf.validate` CEL expressions** to native Python validators at code-generation time — both the full `cel` rule form and the `cel_expression` shorthand. Supports comparisons, string operations, comprehensions (`all`, `exists`, `filter`, `map`), temporal expressions (`now`, `duration()`, `timestamp()`), timestamp/duration member accessors, and boolean format helpers. No runtime CEL dependency in generated code.\n\n## Installation\n\nYou can download the binaries from GitHub [Releases](https://github.com/cjermain/protoc-gen-pydantic/releases).\n\n### Install with Go\n\n```sh\ngo install github.com/cjermain/protoc-gen-pydantic@latest\n```\n\n### Build from Source\n\nClone the repository and build the plugin:\n\n```sh\ngit clone https://github.com/cjermain/protoc-gen-pydantic\ncd protoc-gen-pydantic\ngo build -o protoc-gen-pydantic .\n```\n\n## Usage\n\nTo generate Pydantic model definitions, use `protoc` with your `.proto` files specifying `--pydantic_out`:\n\n```sh\nprotoc --pydantic_out=./gen \\\n       --proto_path=./proto \\\n       ./proto/example.proto\n```\n\nIf the binary is not on your `PATH`, specify it explicitly with `--plugin=protoc-gen-pydantic=./protoc-gen-pydantic`.\n\nIf you use [buf](https://buf.build/):\n\n```yaml\n# buf.gen.yaml\nversion: v2\nplugins:\n  - local: go run github.com/cjermain/protoc-gen-pydantic@latest\n    opt:\n      - paths=source_relative\n    out: gen\ninputs:\n  - directory: proto\n```\n\n```sh\nbuf generate\n```\n\n## Example\n\n### With validation constraints\n\nAdd `buf.validate` constraints to your proto fields and the generator translates them\ndirectly into Pydantic validation:\n\n```proto\nsyntax = \"proto3\";\n\npackage example;\n\nimport \"buf/validate/validate.proto\";\n\n// A user account.\nmessage ValidatedUser {\n  // Display name (1–50 characters).\n  string name = 1 [\n    (buf.validate.field).string.min_len = 1,\n    (buf.validate.field).string.max_len = 50\n  ];\n\n  // Age in years.\n  int32 age = 2 [(buf.validate.field).int32.gte = 0];\n\n  // Contact email address.\n  string email = 3 [(buf.validate.field).string.email = true];\n\n  enum Role {\n    ROLE_UNSPECIFIED = 0;\n    ROLE_VIEWER = 1;\n    ROLE_EDITOR = 2;\n    ROLE_ADMIN = 3;\n  }\n\n  Role role = 4;\n}\n```\n\nThe generated model:\n\n```python\nclass ValidatedUser(_ProtoModel):\n    \"\"\"\n    A user account.\n    \"\"\"\n\n    class Role(_ProtoEnum):\n        UNSPECIFIED = (\"UNSPECIFIED\", 0)\n        VIEWER = (\"VIEWER\", 1)\n        EDITOR = (\"EDITOR\", 2)\n        ADMIN = (\"ADMIN\", 3)\n\n    # Display name (1–50 characters).\n    name: str = _Field(\n        description=\"Display name (1–50 characters).\",\n        min_length=1,\n        max_length=50,\n    )\n    # Age in years.\n    age: int = _Field(\n        default=0,\n        description=\"Age in years.\",\n        ge=0,\n    )\n    # Contact email address.\n    email: _Annotated[str, _AfterValidator(_validate_email)] = _Field(\n        description=\"Contact email address.\",\n    )\n    role: \"ValidatedUser.Role | None\" = _Field(default=None)\n```\n\nUse it like any Pydantic model:\n\n```python\nfrom user_pydantic import ValidatedUser\nfrom pydantic import ValidationError\n\n# Construct and validate\nuser = ValidatedUser(name=\"Alice\", age=30, email=\"alice@example.com\", role=ValidatedUser.Role.EDITOR)\n\n# Serialize (ProtoJSON — omits zero values, uses original proto field names)\nprint(user.model_dump_json())\n# {\"name\":\"Alice\",\"age\":30,\"email\":\"alice@example.com\",\"role\":\"EDITOR\"}\n\n# Validation errors are raised immediately\nValidatedUser(name=\"\", age=-1)  # raises ValidationError (3 validation errors)\n```\n\n## Options\n\nPassed via `opt:` in buf.gen.yaml or `--pydantic_opt=` with protoc:\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `preserving_proto_field_name` | `true` | Keep snake_case proto field names instead of camelCase |\n| `auto_trim_enum_prefix` | `true` | Remove enum type name prefix from value names |\n| `use_integers_for_enums` | `false` | Use integer values for enums instead of string names |\n| `disable_field_description` | `false` | Omit `description=` from generated fields |\n| `use_none_union_syntax_instead_of_optional` | `true` | Use `T \\| None` instead of `Optional[T]` |\n| `disable_validate` | `false` | Skip all buf.validate constraint translation |\n\nSee [Plugin Options](https://cjermain.github.io/protoc-gen-pydantic/options/) for full details.\n\n## buf.validate\n\nField constraints from [buf.validate (protovalidate)](https://github.com/bufbuild/protovalidate)\nare translated to native Pydantic constructs automatically. Add the dependency to `buf.yaml`\nand run `buf dep update` (use `disable_validate=true` to skip all constraint translation):\n\n```yaml\n# buf.yaml\nversion: v2\nmodules:\n  - path: .\ndeps:\n  - buf.build/bufbuild/protovalidate\n```\n\nPredefined rules (`gt`, `min_len`, `pattern`, `email`, `uuid`, etc.) translate to `Field()`\nkwargs and `Annotated[T, AfterValidator(...)]` wrappers. **CEL expressions** —\n`(buf.validate.field).cel`, its shorthand `cel_expression`, and\n`option (buf.validate.message).cel` / `cel_expression` — are transpiled to\nPython lambdas at code-generation time. No runtime CEL library is needed.\n\n```proto\nmessage Order {\n  // Shorthand cel_expression: id and message are derived from the expression itself.\n  double total = 1 [(buf.validate.field).cel_expression = \"this \u003e 0.0\"];\n\n  // Full cel form with explicit id and message.\n  repeated int32 quantities = 2 [(buf.validate.field).cel = {\n    id: \"positive_quantities\",\n    expression: \"this.all(q, q \u003e 0)\",\n    message: \"all quantities must be positive\"\n  }];\n}\n```\n\n```python\n# Generated:\nclass Order(_ProtoModel):\n    total: _Annotated[\n        float, _AfterValidator(_make_cel_validator(lambda v: v \u003e 0.0, \"total must be positive\"))\n    ] = _Field(default=0.0)\n\n    quantities: _Annotated[\n        list[int],\n        _AfterValidator(\n            _make_cel_validator(lambda v: all((q \u003e 0) for q in v), \"all quantities must be positive\")\n        ),\n    ] = _Field(default_factory=list)\n```\n\nSee [buf.validate guide](https://cjermain.github.io/protoc-gen-pydantic/buf-validate/) for the full constraint and CEL reference.\n\n## Development\n\nThis project uses [mise](https://mise.jdx.dev/) to manage tool versions and\n[just](https://github.com/casey/just) as a command runner.\n\nAfter cloning, install all required tools with mise:\n\n```sh\nmise install\n```\n\nThen set up the project (sync Python venv, install pre-commit hooks):\n\n```sh\njust init\n```\n\nOther useful commands:\n\n```sh\njust dev    # Full rebuild + generate + test cycle\njust lint   # Run all linters (Go + Python + type check)\njust test   # Run Python tests only\n```\n\nRun `just --list` to see all available recipes.\n\n\u003e **Without mise**: install `go`, `buf`, `protoc`, `uv`, `golangci-lint`, `just`, and\n\u003e `pre-commit` manually, then run `just init`.\n\n## Contributing\n\nContributions are welcome! Please open an issue or submit a pull request with your changes.\n\n## License\n\nThis project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjermain%2Fprotoc-gen-pydantic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcjermain%2Fprotoc-gen-pydantic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjermain%2Fprotoc-gen-pydantic/lists"}