Giter Site home page Giter Site logo

so1n / protobuf_to_pydantic Goto Github PK

View Code? Open in Web Editor NEW
54.0 1.0 5.0 1.94 MB

Generate a pydantic.BaseModel with parameter verification function from the Python Message object(by the Protobuf file).

License: Apache License 2.0

Shell 0.06% Python 99.90% Dockerfile 0.03%
grpc protobuf pydantic python proto-plugin buf

protobuf_to_pydantic's Issues

No module named 'mypy_protobuf'

python -m grpc_tools.protoc -I. --protobuf-to-pydantic_out=. proxy.proto

Traceback (most recent call last):
File "/Users/wangmingli/.local/share/virtualenvs/pythonProject3-USoLf3Xu/bin/protoc-gen-protobuf-to-pydantic", line 5, in
from protobuf_to_pydantic.plugin.main import main
File "/Users/wangmingli/.local/share/virtualenvs/pythonProject3-USoLf3Xu/lib/python3.8/site-packages/protobuf_to_pydantic/plugin/main.py", line 13, in
from mypy_protobuf.main import Descriptors, code_generation
ModuleNotFoundError: No module named 'mypy_protobuf'
--protobuf-to-pydantic_out: protoc-gen-protobuf-to-pydantic: Plugin failed with status code 1.
(pythonProject3) (base) wangmingli@wangminglidemac pythonProject3 % python -m grpc_tools.protoc -I. --protobuf-to-pydantic_out=. proxy.proto
Traceback (most recent call last):
File "/Users/wangmingli/.local/share/virtualenvs/pythonProject3-USoLf3Xu/bin/protoc-gen-protobuf-to-pydantic", line 5, in
from protobuf_to_pydantic.plugin.main import main
File "/Users/wangmingli/.local/share/virtualenvs/pythonProject3-USoLf3Xu/lib/python3.8/site-packages/protobuf_to_pydantic/plugin/main.py", line 13, in
from mypy_protobuf.main import Descriptors, code_generation
ModuleNotFoundError: No module named 'mypy_protobuf'
--protobuf-to-pydantic_out: protoc-gen-protobuf-to-pydantic: Plugin failed with status code 1.
(pythonProject3) (base) wangmingli@wangminglidemac pythonProject3 % python -m grpc_tools.protoc -I. --protobuf-to-pydantic_out=config_path=plugin_config.py:. proxy.proto
Traceback (most recent call last):
File "/Users/wangmingli/.local/share/virtualenvs/pythonProject3-USoLf3Xu/bin/protoc-gen-protobuf-to-pydantic", line 5, in
from protobuf_to_pydantic.plugin.main import main
File "/Users/wangmingli/.local/share/virtualenvs/pythonProject3-USoLf3Xu/lib/python3.8/site-packages/protobuf_to_pydantic/plugin/main.py", line 13, in
from mypy_protobuf.main import Descriptors, code_generation
ModuleNotFoundError: No module named 'mypy_protobuf'
--protobuf-to-pydantic_out: protoc-gen-protobuf-to-pydantic: Plugin failed with status code 1.

`protoc > 3.19` support

Is your feature request related to a problem? Please describe.
I got an error when using the plugin, I was on latest protoc at the time:

TypeError: Descriptors cannot not be created directly.
If this call came from a _pb2.py file, your generated code is out of date and must be regenerated with protoc >= 3.19.0.
If you cannot immediately regenerate your protos, some other possible workarounds are:
 1. Downgrade the protobuf package to 3.20.x or lower.
 2. Set PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python (but this will use pure-Python parsing and will be much slower).

Describe the solution you'd like
protoc > 3.19 support

Describe alternatives you've considered
Downgrade.

feat: Handle google protobuf FieldMask

Is your feature request related to a problem? Please describe.
I would like to use FieldMask, but I have an error when I try to do so:

syntax = "proto3";

package chat;

import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
import "validate.proto";


...

message ChatUpdate {
    optional string name = 1;
    optional bool is_group = 2;
    optional bool is_personal_assistant = 3;
    optional string starred_at = 4;

    optional google.protobuf.FieldMask field_mask = 100;
}

When I try to convert my protos, I get this:

docker run --rm -it -v `pwd`:/data --workdir /data naas-models-builder:latest python3 -m grpc_tools.protoc \
                -I=protos \
                -I=/usr/local/include/include/google/protobuf \
                -I=lib/protoc-gen-validate/validate \
                --protobuf-to-pydantic_out=python/naas_models/pydantic \
                --python_out=python/naas_models \
                --go_out=go \
                --validate_out="lang=go:go" \
                space.proto registry.proto iam.proto aimodel.proto chat.proto credit.proto secret.proto workspace.proto validate.proto
iam.proto:7:1: warning: Import validate.proto is unused.
chat.proto:9:1: warning: Import google/protobuf/struct.proto is unused.
credit.proto:7:1: warning: Import validate.proto is unused.
secret.proto:7:1: warning: Import validate.proto is unused.
parse command-line error:not enough values to unpack (expected 2, got 1)
Writing protobuf-to-pydantic code to space_p2p.py
Writing protobuf-to-pydantic code to registry_p2p.py
Writing protobuf-to-pydantic code to iam_p2p.py
Writing protobuf-to-pydantic code to aimodel_p2p.py
Not support type .google.protobuf.FieldMask
Traceback (most recent call last):
  File "/usr/local/bin/protoc-gen-protobuf-to-pydantic", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.10/dist-packages/protobuf_to_pydantic/plugin/main.py", line 67, in main
    generate_pydantic_model(Descriptors(request), response, parse_param(request))
  File "/usr/local/lib/python3.10/dist-packages/protobuf_to_pydantic/plugin/main.py", line 32, in generate_pydantic_model
    file.content = config.file_descriptor_proto_to_code(fd=fd, descriptors=descriptors, config=config).content
  File "/usr/local/lib/python3.10/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 53, in __init__
    self._parse_field_descriptor()
  File "/usr/local/lib/python3.10/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 396, in _parse_field_descriptor
    self._content_deque.append(self._message(desc, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
  File "/usr/local/lib/python3.10/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 309, in _message
    if field.type == 11 and self._get_protobuf_type_model(field).type_factory is AnyMessage:
  File "/usr/local/lib/python3.10/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 379, in _get_protobuf_type_model
    return ProtobufTypeModel(
  File "/usr/local/lib/python3.10/dist-packages/pydantic/main.py", line 341, in __init__
    raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for ProtobufTypeModel
rule_type_str
  none is not an allowed value (type=type_error.none.not_allowed)
--protobuf-to-pydantic_out: protoc-gen-protobuf-to-pydantic: Plugin failed with status code 1.
make: *** [generate] Error 1

Describe the solution you'd like

I would just like the FieldMask to work by default.

Describe alternatives you've considered

I considered created my own version of the FieldMask message but I would like to avoid this:

message FieldMask {
    // The set of field mask paths.
    repeated string paths = 1;
  }

message ChatUpdate {
    optional string name = 1;
    optional bool is_group = 2;
    optional bool is_personal_assistant = 3;
    optional string starred_at = 4;

    FieldMask field_mask = 100;
}

Syntax Errors generating model from mapbox vector tile spec

Describe the bug
When generating pydantic models for the mapbox tile definition,
the following errors occur in the resulting model:

  1. Feature and Value classes are duplicated outside of Tile class. They should not exist outside of the Tile class.
  2. GeomType, Feature, Value typed attributes are reported as not found by pyright.

To Reproduce
Steps to reproduce the behavior:

  1. Run python -m grpc_tools.protoc -I. --protobuf-to-pydantic_out=. vector_tile.proto
  2. Open "vector_tile_p2p.py"
  3. See error

The repo located here reconstructs the scenario.

Expected behavior

  1. The initial occurences of Feature and Value classes should not be present.

Actual behaviour
The following file is generated.

# This is an automatically generated file, please do not change
# gen by protobuf_to_pydantic[v0.2.5](https://github.com/so1n/protobuf_to_pydantic)
# Protobuf Version: 4.25.3 
# Pydantic Version: 2.6.3 
from enum import IntEnum
from google.protobuf.message import Message  # type: ignore
from pydantic import BaseModel
from pydantic import Field
import typing


    class Feature(BaseModel):

        id: int = Field(default=0) 
        tags: typing.List[int] = Field(default_factory=list) 
        type: GeomType = Field(default=0) 
        geometry: typing.List[int] = Field(default_factory=list) 

    class Value(BaseModel):

        string_value: str = Field(default="") 
        float_value: float = Field(default=0.0) 
        double_value: float = Field(default=0.0) 
        int_value: int = Field(default=0) 
        uint_value: int = Field(default=0) 
        sint_value: int = Field(default=0) 
        bool_value: bool = Field(default=False) 

class Tile(BaseModel):

    class Value(BaseModel):

        string_value: str = Field(default="") 
        float_value: float = Field(default=0.0) 
        double_value: float = Field(default=0.0) 
        int_value: int = Field(default=0) 
        uint_value: int = Field(default=0) 
        sint_value: int = Field(default=0) 
        bool_value: bool = Field(default=False) 
    class Feature(BaseModel):

        id: int = Field(default=0) 
        tags: typing.List[int] = Field(default_factory=list) 
        type: GeomType = Field(default=0) 
        geometry: typing.List[int] = Field(default_factory=list) 
    class Layer(BaseModel):

        version: int = Field(default=0) 
        name: str = Field(default="") 
        features: typing.List[Feature] = Field(default_factory=list) 
        keys: typing.List[str] = Field(default_factory=list) 
        values: typing.List[Value] = Field(default_factory=list) 
        extent: int = Field(default=0) 
    class GeomType(IntEnum):
        UNKNOWN = 0
        POINT = 1
        LINESTRING = 2
        POLYGON = 3



    layers: typing.List[Layer] = Field(default_factory=list) 

Environment

  • OS: Manjaro
  • Python version: 3.11.6
  • protobuf==4.25.3
  • mypy-protobuf==3.5.0
  • protobuf-to-pydantic==0.2.5

Making a field required

GRPC proto have all fields optional in messages.

with p2p we can annotate defailt values, title, etc. however is there a way to have no default, i.e. making a field required?

Support of protobuf services

This library generating messages in good format. But no support of generating proto services to python and creating server and client

Oneof for non primitive types

Describe the bug
Unusable pydantic model is generated.

To Reproduce
Steps to reproduce the behavior:

syntax = "proto3";
message A {
  bool a = 1;
}
message B {
  bool b = 1;
}
message C {
  oneof z {
    A a = 1;
    B b = 2;
  }
  optional B c = 3;
}

generates this:

class A(BaseModel):
    a: bool = Field(default=False)


class B(BaseModel):
    b: bool = Field(default=False)


class C(BaseModel):
    _one_of_dict = {"C._c": {"fields": {"c"}}, "C.z": {"fields": {"a", "b"}}}
    one_of_validator = model_validator(mode="before")(check_one_of)

    a: A = Field()
    b: B = Field()
    c: typing.Optional[B] = Field(default=None)

Expected behavior

class A(BaseModel):
    a: bool = Field(default=False)


class B(BaseModel):
    b: bool = Field(default=False)


class C(BaseModel):
    _one_of_dict = {"C._c": {"fields": {"c"}}, "C.z": {"fields": {"a", "b"}}}
    one_of_validator = model_validator(mode="before")(check_one_of)

    a: typing.Optional[A] = Field(default=None)
    b: typing.Optional[B] = Field(default=None)
    c: typing.Optional[B] = Field(default=None)

Document how to convert objects between proto and pydantic and reverse

Is your feature request related to a problem? Please describe.

I tried to find if there is an easy way to convert objects form one format into another. The easiest I found is using dicts as a intermediate format.

Describe the solution you'd like

  • Something documented in the readme showcasing the conversion in both directions
  • Maybe: a nice <PydanticType>.from_proto(msg: <ProtoType>) -> <PydanticType> class method and <PydanticType>.to_proto() -> <ProtoType> method.

Describe alternatives you've considered

The dict method: convert proto/pydantic to dict, use that dict with two stars as input.

default_factory and default generated together

Describe the bug
Follow up on #22

For certain protobufs, this plugin generates a default and a default_factory in Pydantic which causes an error.

To Reproduce
Steps to reproduce the behavior:

syntax = "proto3";

message A {
    optional string a = 2;
    repeated string b = 3;
}

Output:

# This is an automatically generated file, please do not change
# gen by protobuf_to_pydantic[v0.2.0.1](https://github.com/so1n/protobuf_to_pydantic)
# Protobuf Version: 4.24.3
# Pydantic Version: 1.10.12
from google.protobuf.message import Message  # type: ignore
from pydantic import BaseModel
from pydantic import Field
import typing


class A(BaseModel):
    a: typing.Optional[str] = Field(default="")
    b: typing.Optional[typing.List[str]] = Field(default_factory=list, default=None)

Expected behavior
It should only generate the default factory list for repeated and not default=None.

Issue with google.protobuf.DoubleValue

Describe the bug

Using google.protobuf.DoubleValue from wrappers import "google/protobuf/wrappers.proto"; does not seem to work

To Reproduce
Steps to reproduce the behavior:

  1. Create a .proto with DoubleValue
    e.g.

    syntax = "proto3";
    
    import "google/protobuf/wrappers.proto";
    
    message CalculatedValue {
        // p2p: {"type": "p2p@builtin|float"}
        google.protobuf.DoubleValue myValue = 1; 
    }
  2. It tries to get google.protobuf.double_value_pb2, which is not from wrappers

  3. Error: No module named 'google.protobuf.double_value_pb2'

Full Error:

ERROR:protobuf_to_pydantic.plugin.main:No module named 'google.protobuf.double_value_pb2'
Traceback (most recent call last):
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/main.py", line 14, in main
    CodeGen(ConfigModel)
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/code_gen.py", line 32, in __init__
    self.generate_pydantic_model(Descriptors(request), response)
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/code_gen.py", line 136, in generate_pydantic_model
    file.content = self.config.file_descriptor_proto_to_code(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 73, in __init__
    self._parse_field_descriptor()
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 631, in _parse_field_descriptor
    self._content_deque.append(self._message(desc, desc, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 484, in _message
    _content_tuple: Optional[Tuple[str, str]] = self._message_field_handle(
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 198, in _message_field_handle
    self._message(message, root_desc, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER])
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 481, in _message
    if field.type == 11 and self._get_protobuf_type_model(field).use_custom_type:
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/build-env-h9195zep/lib/python3.11/site-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 604, in _get_protobuf_type_model
    type_factory = getattr(importlib.import_module(type_module_name), _type_str)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/bb/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)

Expected behavior

Expect it to work

Typos in the word "pydantic" in the README.md file

Both files (README.md and README_ZH.md) contain a typo in some of the words "pydantic". In some places it says "pydanitc":

the command python -m pip install protobuf-to-pydanitc[mypy-protobuf].

indicates the use of the prorobuf-to-pydanitc plugin

Genereted code does not pass mypy

Describe the bug
The generated code does not pass mypy checks, as the field line is generated as

    id: int = FieldInfo(default=0)

instead of

    id: int = Field(default=0)
λ  mypy protobuf_p2p.py
protobuf_p2p.py:8: error: Incompatible types in assignment (expression has type "FieldInfo", variable has type "int")  [assignment]
protobuf_p2p.py:9: error: Incompatible types in assignment (expression has type "FieldInfo", variable has type "int")  [assignment]
Found 2 errors in 1 file (checked 1 source file)

Expected behavior

The resulting file is mypy clean

ideas

I tried outputting Fields instead of FieldInfo in the two lines below and for my usecase, that was fine.

field_name = "FieldInfo"
self._add_import_code("pydantic.fields", "FieldInfo")

But I guess there might be content which does not fit into a Field, but only into a FieldInfo?

The other idea how to fix this would be to add a # type: ignore[assignment] (specific, because otherwise ruff complains...) in the generated field code:

field_info_str: str = ", ".join([f"{k}={self._get_value_code(v)}" for k, v in field_info_dict.items()]) or ""
class_field_content: str = (
" " * (self.code_indent + indent) + f"{field.name}: {type_str} = {field_name}({field_info_str}) \n"
)

-            " " * (self.code_indent + indent) + f"{field.name}: {type_str} = {field_name}({field_info_str})\n"
+            " " * (self.code_indent + indent) + f"{field.name}: {type_str} = {field_name}({field_info_str}) # type: ignore[assignment]\n"

relevant mypy config

I use the pydantic mypy plugin:

[tool.mypy]
plugins = [
    # Better support for pydantic models
    "pydantic.mypy"
]

Missing typing import for optional field

Describe the bug

The following (shortend) proto file creates a non-valid pydantic python file:

syntax = "proto3";
package whatever.v1;

message Blub {
  int64 whatever = 1;
}

message CheckCreateCommitFromPreparationResponse {
  optional Blub blib = 1;
}

Converting with protobuf-to-pydantic gives this

# This is an automatically generated file, please do not change
# gen by protobuf_to_pydantic[v0.2.3](https://github.com/so1n/protobuf_to_pydantic)
# Protobuf Version: 4.25.2 
# Pydantic Version: 2.6.1 
from google.protobuf.message import Message  # type: ignore
from pydantic import BaseModel
from pydantic import Field


class Blub(BaseModel):

    whatever: int = Field(default=0) 


class CheckCreateCommitFromPreparationResponse(BaseModel):

    blib: typing.Optional[Blub] = Field(default=None) 

Which misses the import for typing and therefore the last lines errors on import.

To Reproduce
Steps to reproduce the behavior:

  1. Install the above versions (see python version)
  2. convert proto to pydantic
  3. try to import the pydantic file
  4. See error: NameError: name 'typing' is not defined. Did you forget to import 'typing'

Expected behavior
There is a typing import in the generated file and I can import the generated file without an error

dependencies clash with protobuf-5.26.1

Adding protobuf>=5 together with the latest protobuf-to-pydantic to a python project clashes, as dependecy constraints are contradicting.

Because no versions of grpcio-tools match >1.40.0,<1.41.0 || >1.41.0,<1.41.1 || >1.41.1,<1.42.0 || >1.42.0,<1.43.0 || >1.43.0,<1.44.0 || >1.44.0,<1.45.0 || >1.45.0,<1.46.0 || >1.46.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.0rc1 || >1.54.0rc1,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.0rc2 || >1.56.0rc2,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0
and grpcio-tools (1.54.0rc1) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>1.40.0,<1.41.0 || >1.41.0,<1.41.1 || >1.41.1,<1.42.0 || >1.42.0,<1.43.0 || >1.43.0,<1.44.0 || >1.44.0,<1.45.0 || >1.45.0,<1.46.0 || >1.46.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.0rc2 || >1.56.0rc2,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=4.21.6,<5.0dev).
And because grpcio-tools (1.56.0rc2) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>1.40.0,<1.41.0 || >1.41.0,<1.41.1 || >1.41.1,<1.42.0 || >1.42.0,<1.43.0 || >1.43.0,<1.44.0 || >1.44.0,<1.45.0 || >1.45.0,<1.46.0 || >1.46.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=4.21.6,<5.0dev).
And because grpcio-tools (1.40.0) depends on protobuf (>=3.5.0.post1,<4.0dev)
and grpcio-tools (1.41.0) depends on protobuf (>=3.5.0.post1,<4.0dev), grpcio-tools (>=1.40.0,<1.41.1 || >1.41.1,<1.42.0 || >1.42.0,<1.43.0 || >1.43.0,<1.44.0 || >1.44.0,<1.45.0 || >1.45.0,<1.46.0 || >1.46.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.41.1) depends on protobuf (>=3.5.0.post1,<4.0dev)
and grpcio-tools (1.42.0) depends on protobuf (>=3.5.0.post1,<4.0dev), grpcio-tools (>=1.40.0,<1.43.0 || >1.43.0,<1.44.0 || >1.44.0,<1.45.0 || >1.45.0,<1.46.0 || >1.46.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.43.0) depends on protobuf (>=3.5.0.post1,<4.0dev)
and grpcio-tools (1.44.0) depends on protobuf (>=3.5.0.post1,<4.0dev), grpcio-tools (>=1.40.0,<1.45.0 || >1.45.0,<1.46.0 || >1.46.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.45.0) depends on protobuf (>=3.5.0.post1,<4.0dev)
and grpcio-tools (1.46.0) depends on protobuf (>=3.12.0,<4.0dev), grpcio-tools (>=1.40.0,<1.46.1 || >1.46.1,<1.46.3 || >1.46.3,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.46.1) depends on protobuf (>=3.12.0,<4.0dev)
and grpcio-tools (1.46.3) depends on protobuf (>=3.12.0,<4.0dev), grpcio-tools (>=1.40.0,<1.46.5 || >1.46.5,<1.47.0 || >1.47.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.46.5) depends on protobuf (>=3.12.0,<4.0dev)
and grpcio-tools (1.47.0) depends on protobuf (>=3.12.0,<4.0dev), grpcio-tools (>=1.40.0,<1.47.2 || >1.47.2,<1.47.5 || >1.47.5,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.47.2) depends on protobuf (>=3.12.0,<4.0dev)
and grpcio-tools (1.47.5) depends on protobuf (>=3.12.0,<4.0dev), grpcio-tools (>=1.40.0,<1.48.0 || >1.48.0,<1.48.1 || >1.48.1,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.48.0) depends on protobuf (>=3.12.0,<4.0dev)
and grpcio-tools (1.48.1) depends on protobuf (>=3.12.0,<4.0dev), grpcio-tools (>=1.40.0,<1.48.2 || >1.48.2,<1.49.0 || >1.49.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.6,<5.0dev).
And because grpcio-tools (1.48.2) depends on protobuf (>=3.12.0,<4.0dev)
and grpcio-tools (1.49.0) depends on protobuf (>=4.21.3,<5.0dev), grpcio-tools (>=1.40.0,<1.49.1 || >1.49.1,<1.50.0 || >1.50.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.49.1) depends on protobuf (>=4.21.3,<5.0dev)
and grpcio-tools (1.50.0) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.51.0 || >1.51.0,<1.51.1 || >1.51.1,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.51.0) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.51.1) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.51.3 || >1.51.3,<1.52.0 || >1.52.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.51.3) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.52.0) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.53.0 || >1.53.0,<1.53.1 || >1.53.1,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.53.0) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.53.1) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.53.2 || >1.53.2,<1.54.3 || >1.54.3,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.53.2) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.54.3) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.55.0 || >1.55.0,<1.55.3 || >1.55.3,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.55.0) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.55.3) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.56.2 || >1.56.2,<1.57.0 || >1.57.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.56.2) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.57.0) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.58.0 || >1.58.0,<1.59.0 || >1.59.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.58.0) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.59.0) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.59.2 || >1.59.2,<1.59.3 || >1.59.3,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.59.2) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.59.3) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.60.0 || >1.60.0,<1.60.1 || >1.60.1,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.60.0) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.60.1) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<1.62.0 || >1.62.0,<1.62.1 || >1.62.1,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
And because grpcio-tools (1.62.0) depends on protobuf (>=4.21.6,<5.0dev)
and grpcio-tools (1.62.1) depends on protobuf (>=4.21.6,<5.0dev), grpcio-tools (>=1.40.0,<2.0.0) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
Because no versions of protobuf-to-pydantic match >0.2.6
and protobuf-to-pydantic[mypy-protobuf] (0.2.6) depends on grpcio-tools (>=1.40.0,<2.0.0), protobuf-to-pydantic[mypy-protobuf] (>=0.2.6) requires grpcio-tools (>=1.40.0,<2.0.0).
Thus, protobuf-to-pydantic[mypy-protobuf] (>=0.2.6) requires protobuf (>=3.5.0.post1,<4.0dev || >=4.21.3,<5.0dev).
So, because mds-schema depends on both protobuf (>=5) and protobuf-to-pydantic[mypy-protobuf] (>=0.2.6), version solving failed.

Remote buf plugin doesn't exist

Describe the bug
Seems the buf registry plugin doesn't exist.

To Reproduce

  - plugin: buf.build/python-pai/protobuf-to-pydantic:v0.2.1
    opt:
      - plugin_config_py_code_base64=aW1wb3J0IGxvZ2dpbmcKCmxvZ2dpbmcuYmFzaWNDb25maWcoZm9ybWF0PSJbJShhc2N0aW1lKXMgJShsZXZlbG5hbWUpc10gJShtZXNzYWdlKXMiLCBkYXRlZm10PSIleS0lbS0lZCAlSDolTTolUyIsIGxldmVsPWxvZ2dpbmcuSU5GTykKCgpkZWYgZGVmYXVsdF9mdW5jKCkgLT4gaW50OgogICAgcmV0dXJuIDEKCmxvY2FsX2RpY3QgPSB7CiAgICAiZGVmYXVsdF9mdW5jIjogZGVmYXVsdF9mdW5jLAp9Cg
      - plugin_config_module_name=plugin_config

Failure: plugin "buf.build/python-pai/protobuf-to-pydantic" was not found

RecursionError: maximum recursion depth exceeded in __instancecheck__

Describe the bug
After update proto in #2 , I tried to parse my proto, and I get this error

Error Log

maximum recursion depth exceeded in instancecheck
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/main.py", line 12, in main
CodeGen(ConfigModel)
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/code_gen.py", line 30, in init
self.generate_pydantic_model(Descriptors(request), response)
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/code_gen.py", line 82, in generate_pydantic_model
file.content = self.config.file_descriptor_proto_to_code(
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 57, in init
self._parse_field_descriptor()
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 405, in _parse_field_descriptor
self._content_deque.append(self._message(desc, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 321, in _message
_content_tuple: Optional[Tuple[str, str]] = self._message_field_handle(
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 168, in _message_field_handle
self._content_deque.append(self._message(message, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 321, in _message
_content_tuple: Optional[Tuple[str, str]] = self._message_field_handle(
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 168, in _message_field_handle
self._content_deque.append(self._message(message, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 321, in _message
_content_tuple: Optional[Tuple[str, str]] = self._message_field_handle(
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 168, in _message_field_handle
self._content_deque.append(self._message(message, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 321, in _message
_content_tuple: Optional[Tuple[str, str]] = self._message_field_handle(

......

File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 168, in _message_field_handle
self._content_deque.append(self._message(message, [FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER]))
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 321, in _message
_content_tuple: Optional[Tuple[str, str]] = self._message_field_handle(
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 183, in _message_field_handle
protobuf_type_model = self._get_protobuf_type_model(field)
File "/usr/local/lib/python3.9/dist-packages/protobuf_to_pydantic/plugin/field_desc_proto_to_code.py", line 359, in _get_protobuf_type_model
return ProtobufTypeModel(
File "pydantic/main.py", line 339, in pydantic.main.BaseModel.init
File "pydantic/main.py", line 1076, in pydantic.main.validate_model
File "pydantic/fields.py", line 884, in pydantic.fields.ModelField.validate
File "pydantic/fields.py", line 1101, in pydantic.fields.ModelField._validate_singleton
File "pydantic/fields.py", line 1151, in pydantic.fields.ModelField._apply_validators
File "pydantic/class_validators.py", line 337, in pydantic.class_validators._generic_validator_basic.lambda13
File "pydantic/validators.py", line 61, in pydantic.validators.str_validator
RecursionError: maximum recursion depth exceeded in instancecheck

add `pass` to generated class when message body is empty

Is your feature request related to a problem? Please describe.
When message is empty, the generated code is empty after class.

message Empty{}
message Something {
string s = 1;
}

Generated code is like

class Empty(BaseModel):


class Something(BaseModel):
    s : ...

Describe the solution you'd like
Add pass statement to generated class if the message body is empty

Typo in readme.

Describe the bug
There is a typo in the readme. It says prorobuf_to_pydantic instead of protobuf-to-pydantic
To Reproduce
Steps to reproduce the behavior:

  1. Go to readme file
  2. Click on scroll down to 1.1.1 Use plugins section
  3. See grammatical error

Expected behavior
The text should read protobuf-to-pydantic

Screenshots
If applicable, add screenshots to help explain your problem.
image

Desktop (please complete the following information):

  • OS: [e.g. iOS] N/A
  • Browser [e.g. chrome, safari] Chrome
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari] Chrome
  • Version [e.g. 22] Version 120.0.6099.131 (Official Build) (64-bit)

Additional context
N/A

Integrate plugin with buf cli

Is your feature request related to a problem? Please describe.
Our team is using the buf cli to manage protobuf builds because
managing protoc calls can be clunky.

Describe the solution you'd like
I'd like this plugin integrated with the CodeGeneratorRequest so that a Dockerfile can be implemented that will allow a custom plugin to be implemented.

Additionally, this will facilitate adoption by Buf to make it a hosted plugin, which will likely increase adoption of this plugin.

Describe alternatives you've considered
The alternative is to use the CLI as documented. However, buf generate is a much friendlier interface to using this.

Additional context
bufbuild/plugins#589

Support `BaseModel` serialization to `Protobuf Message`

The current step of converting BaseModel to Protobuf Message is tedious and has poor performance. e.g:

from google.protobuf.json_format import ParseDict
from pydantic import BaseModel
from demo_pb2 import DemoMessage

class Demo(BaseModel):
    pass


ParseDict(Demo().dict(), DemoMessage())

Serialization can be optimized in the following ways:

  • 1.Convert directly to Message:

    from protobuf_to_pydantic.p2p_model import P2PBaseModel
    
    class Demo(P2PBaseModel):
        pass
    
    Demo().to_message()
  • 2.Serialized to dict, then converted to Message by the developer (pydantic/pydantic#1409 (comment))

    from google.protobuf.json_format import ParseDict
    from pydantic import BaseModel
    from demo_pb2 import DemoMessage
    
    class Demo(BaseModel):
        pass
    
    
    DemoMessage(**Demo().dict())

make the model field to snake_case

Is your feature request related to a problem? Please describe.
When the field name in *.proto file is PascalCase or camelCase
I want the p2p can convert the field name to snake_case when generate python file.

Describe the solution you'd like
Add some cli option or plugin , so I can given the output filed case.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Optional fields do not generate correct defaults in Pydantic

Describe the bug
A clear and concise description of what the bug is.
Using proto v3

Marking a field as optional in Proto doesn't seem to change anything in the generated Pydantic model.

To Reproduce
.proto

syntax = "proto3";

message A {
    string a = 1;
}
message B {
    optional A a = 1;
}

Output:

class A(BaseModel):
    a: str = Field(default="")

class B(BaseModel):
    a: typing.Optional[A] = Field()

Expected behavior
This is the expected output:

class A(BaseModel):
    a: str = Field(default="")


class B(BaseModel):
    a: typing.Optional[A] = Field(default=None)

Note that without setting default = None in Pydantic, it's impossible to construct B() with no args. You have to construct it via B(a=None).

ImportError: cannot import name 'PydanticGeneralMetadata' from 'pydantic._internal._fields' with latest pydantic

Describe the bug

After udating to the latest pydantic version, I get this:

cd ./build/proto && poetry run python -m grpc_tools.protoc -I. --protobuf-to-pydantic_out=. example_model/v1/classes.proto
/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/grpc_tools/protoc.py:21: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
  import pkg_resources
Traceback (most recent call last):
  File "/Users/jankatins/projects/project1/.venv/bin/protoc-gen-protobuf-to-pydantic", line 5, in <module>
    from protobuf_to_pydantic.plugin.main import main
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/__init__.py", line 2, in <module>
    from .gen_code import pydantic_model_to_py_code, pydantic_model_to_py_file
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/gen_code.py", line 33, in <module>
    from protobuf_to_pydantic import _pydantic_adapter, customer_validator, gen_model
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/gen_model.py", line 18, in <module>
    from protobuf_to_pydantic.get_desc import (
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/get_desc/__init__.py", line 1, in <module>
    from .from_pb_option import get_desc_from_p2p, get_desc_from_pgv
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/get_desc/from_pb_option/__init__.py", line 1, in <module>
    from .from_p2p import get_desc_from_p2p
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/get_desc/from_pb_option/from_p2p.py", line 5, in <module>
    from .base import DescFromOptionTypedDict, ParseFromPbOption
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/get_desc/from_pb_option/base.py", line 7, in <module>
    from protobuf_to_pydantic.customer_con_type import (
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/customer_con_type/__init__.py", line 6, in <module>
    from .v2 import *  # noqa
    ^^^^^^^^^^^^^^^^^
  File "/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/protobuf_to_pydantic/customer_con_type/v2.py", line 6, in <module>
    from pydantic._internal._fields import PydanticGeneralMetadata
ImportError: cannot import name 'PydanticGeneralMetadata' from 'pydantic._internal._fields' (/Users/jankatins/projects/project1/.venv/lib/python3.11/site-packages/pydantic/_internal/_fields.py)
--protobuf-to-pydantic_out: protoc-gen-protobuf-to-pydantic: Plugin failed with status code 1.

To Reproduce

Install the following versions:

λ  poetry show
...
grpcio               1.59.3    HTTP/2-based RPC framework
grpcio-tools         1.59.3    Protobuf code generator for gRPC
mypy                 1.7.1     Optional static typing for Python
mypy-extensions      1.0.0     Type system extensions for programs checked with the mypy type checker.
mypy-protobuf        3.5.0     Generate mypy stub files from protobuf specs
protobuf             4.25.1
protobuf-to-pydantic 0.2.1     Generate the `pydantic.BaseModel` class (and the corresponding source code) with parameter verification function through the Protobuf file
pydantic             2.5.2     Data validation using Python type hints
pydantic-core        2.14.5
types-protobuf       4.24.0.4  Typing stubs for protobuf

Expected behavior

This should work (and it did work with older versions) :-) -> will downgrade pydantic for now...

reorder generated classes to avoid unresolved references

Thanks for the nice module!

Is your feature request related to a problem? Please describe.
The order of generated pydantic classes (from python -m grpc_tools.protoc -I. --protobuf-to-pydantic_out=. example.proto) can cause unresolved references: a class definition comes after its references in other classes.

Describe the solution you'd like
I'm using the following script to reorder generated classes, but it may be better to integrate this step.

import ast

def reorder_classes(source):
    tree = ast.parse(source)

    # Extract the classes from the AST and sort them topologically
    class_nodes = [node for node in tree.body if isinstance(node, ast.ClassDef)]
    class_dependencies = {}
    for class_node in class_nodes:
        class_dependencies[class_node] = {
            dep.id for dep in ast.walk(class_node) if
            isinstance(dep, ast.Name) and dep.id in [cls.name for cls in class_nodes]
        }
    sorted_classes = []
    while class_dependencies:
        acyclic_nodes = [node for node, deps in class_dependencies.items() if not deps]
        if not acyclic_nodes:
            raise ValueError('Circular dependency found')
        for node in acyclic_nodes:
            del class_dependencies[node]
            sorted_classes.append(node)
            for deps in class_dependencies.values():
                deps.discard(node.name)

    # Rewrite the source code with the sorted classes
    sorted_source = '\n\n'.join([ast.unparse(node) for node in sorted_classes])
    return sorted_source

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.