protobuf_to_pydantic icon indicating copy to clipboard operation
protobuf_to_pydantic copied to clipboard

Exclude pydantic 2.12

Open carlesonielfa opened this issue 1 month ago • 1 comments

Newly released pydantic 2.12 breaks compatibility with this library:

'pydantic_core._pydantic_core.ValidationInfo' object has no attribute 'template'
      Traceback (most recent call last):
        File "/Users/.../.cache/uv/builds-v0/.tmpDBckzd/lib/python3.12/site-packages/protobuf_to_pydantic/plugin/main.py", line 14, in main
          CodeGen(ConfigModel)
        File "/Users/.../.cache/uv/builds-v0/.tmpDBckzd/lib/python3.12/site-packages/protobuf_to_pydantic/plugin/code_gen.py", line 32, in __init__
          self.gen_config()
        File "/Users/.../.cache/uv/builds-v0/.tmpDBckzd/lib/python3.12/site-packages/protobuf_to_pydantic/plugin/code_gen.py", line 141, in gen_config
          self.config = self.config_class()
                        ^^^^^^^^^^^^^^^^^^^
        File "/Users/.../.cache/uv/builds-v0/.tmpDBckzd/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
          validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/.../.cache/uv/builds-v0/.tmpDBckzd/lib/python3.12/site-packages/protobuf_to_pydantic/plugin/config.py", line 117, in after_init
          values.template_instance = values.template(values.local_dict, values.comment_prefix)
                                     ^^^^^^^^^^^^^^^
      AttributeError: 'pydantic_core._pydantic_core.ValidationInfo' object has no attribute 'template'

This PR excludes 2.12 from the valid pydantic versions until a fix can be implemented

carlesonielfa avatar Oct 08 '25 07:10 carlesonielfa

Hi folks, I ran into this and Claude suggested a fix; with all the caveats regarding LLM-generated code, I offer here what it wrote. For what it's worth, I did test the suggested protobuf_to_pydantic patch + pydantic 2.12 and generation succeeded. The same protobufs also work just fine with unpatched protobuf_to_pydantic + pydantic 2.11

Problem Description

The protobuf-to-pydantic plugin fails during code generation when using Pydantic 2.12.0 or later with the following error:

AttributeError: 'pydantic_core._pydantic_core.ValidationInfo' object has no attribute 'template'

The error occurs in protobuf_to_pydantic/plugin/config.py at line 145 in the after_init method.

Root Cause

Pydantic 2.12.0 introduced a breaking change in the @model_validator(mode="after") decorator. The validator function signature changed:

  • Pydantic 2.11.x and earlier: Validator receives model instance directly as first parameter
  • Pydantic 2.12.0+: Validator function signature handling changed

The current implementation in config.py line 137-146 has an incorrect function signature:

  @_pydantic_adapter.model_validator(mode="after")
  def after_init(cls, values: Any) -> Any:
      if _pydantic_adapter.is_v1:
          # values: Dict[str, Any]
          values["template_instance"] = values["template"](values["local_dict"], values["comment_prefix"])
          return values
      else:
          # values: "ConfigModel"
          values.template_instance = values.template(values.local_dict, values.comment_prefix)
      return values

The function signature uses cls, values (suggesting a classmethod pattern), but it's not decorated as @classmethod. In Pydantic v2, mode="after" validators should receive the model instance as self.

Fix

Change the function signature on line 138:

Before:

  @_pydantic_adapter.model_validator(mode="after")
  def after_init(cls, values: Any) -> Any:
      if _pydantic_adapter.is_v1:
          # values: Dict[str, Any]
          values["template_instance"] = values["template"](values["local_dict"], values["comment_prefix"])
          return values
      else:
          # values: "ConfigModel"
          values.template_instance = values.template(values.local_dict, values.comment_prefix)
      return values

After:

  @_pydantic_adapter.model_validator(mode="after")
  def after_init(self) -> Any:
      if _pydantic_adapter.is_v1:
          # In v1, this is called as root_validator and receives values dict
          self["template_instance"] = self["template"](self["local_dict"], self["comment_prefix"])
          return self
      else:
          # In v2, self is the ConfigModel instance
          self.template_instance = self.template(self.local_dict, self.comment_prefix)
      return self

This fix:

  1. Changes the parameter from cls, values to just self
  2. Uses self consistently in both branches
  3. Works with both Pydantic 2.11.x and 2.12.0+

Verification

I've tested this patch with:

  • ✅ Pydantic 2.11.10 - works perfectly
  • ✅ Pydantic 2.12.0 - works with the patch, fails without it

dvryaboy avatar Oct 09 '25 22:10 dvryaboy