Skip to content

Commit

Permalink
release it but not work
Browse files Browse the repository at this point in the history
  • Loading branch information
Ed-XCF committed Jan 4, 2021
1 parent 3ceb0e0 commit daac351
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 76 deletions.
80 changes: 80 additions & 0 deletions protobuf2pydantic/biz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from os import linesep
from typing import List
from functools import partial
from pathlib import Path

from google.protobuf.reflection import GeneratedProtocolMessageType
from google.protobuf.descriptor import Descriptor, FieldDescriptor
from google.protobuf.struct_pb2 import Struct
from jinja2 import Environment, PackageLoader

tab = " " * 4
one_line, two_lines = linesep * 2, linesep * 3
type_mapping = {
FieldDescriptor.TYPE_DOUBLE: float,
FieldDescriptor.TYPE_FLOAT: float,
FieldDescriptor.TYPE_INT64: int,
FieldDescriptor.TYPE_UINT64: int,
FieldDescriptor.TYPE_INT32: int,
FieldDescriptor.TYPE_FIXED64: float,
FieldDescriptor.TYPE_FIXED32: float,
FieldDescriptor.TYPE_BOOL: bool,
FieldDescriptor.TYPE_STRING: str,
FieldDescriptor.TYPE_BYTES: str,
FieldDescriptor.TYPE_UINT32: int,
FieldDescriptor.TYPE_SFIXED32: float,
FieldDescriptor.TYPE_SFIXED64: float,
FieldDescriptor.TYPE_SINT32: int,
FieldDescriptor.TYPE_SINT64: int,
}


def convert_field(level: int, field: FieldDescriptor) -> str:
level += 1
field_type = field.type
extra = None

if field_type == FieldDescriptor.TYPE_ENUM:
type_statement = field.name
class_statement = f"{tab * level}class {field.name}(IntEnum):"
field_statements = map(
lambda value: f"{tab * (level + 1)}{value.name} = {value.index}",
field.enum_type.values,
)
extra = linesep.join([class_statement, *field_statements])
elif field_type == FieldDescriptor.TYPE_MESSAGE:
type_statement = field.message_type.name
if type_statement == Struct.__name__:
type_statement = "Dict"
else:
extra = msg2pydantic(level, field.message_type)
else:
type_statement = type_mapping[field_type].__name__

if field.label == FieldDescriptor.LABEL_REPEATED:
type_statement = f"List[{type_statement}]"

field_statement = f"{tab * level}{field.name}: {type_statement}"
if not extra:
return field_statement
return linesep + extra + one_line + field_statement


def msg2pydantic(level: int, msg: Descriptor) -> str:
class_statement = f"{tab * level}class {msg.name}(BaseModel):"
field_statements = map(partial(convert_field, level), msg.fields)
return linesep.join([class_statement, *field_statements])


def pb2_to_pydantic(module) -> str:
pydantic_models: List[str] = []
for i in dir(module):
obj = getattr(module, i)
if not isinstance(obj, GeneratedProtocolMessageType):
continue
model_string = msg2pydantic(0, obj.DESCRIPTOR)
pydantic_models.append(model_string)

env = Environment(loader=PackageLoader(Path.cwd().name))
template = env.get_template("pydantic.jinja2")
return template.render(models=pydantic_models)
84 changes: 9 additions & 75 deletions protobuf2pydantic/main.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,14 @@
from os import linesep
from typing import List
from functools import partial
from pathlib import Path
from importlib import import_module

from google.protobuf.reflection import GeneratedProtocolMessageType
from google.protobuf.descriptor import Descriptor, FieldDescriptor
from google.protobuf.struct_pb2 import Struct
from jinja2 import Environment, PackageLoader
from typer import Typer

tab = " " * 4
one_line, two_lines = linesep * 2, linesep * 3
type_mapping = {
FieldDescriptor.TYPE_DOUBLE: float,
FieldDescriptor.TYPE_FLOAT: float,
FieldDescriptor.TYPE_INT64: int,
FieldDescriptor.TYPE_UINT64: int,
FieldDescriptor.TYPE_INT32: int,
FieldDescriptor.TYPE_FIXED64: float,
FieldDescriptor.TYPE_FIXED32: float,
FieldDescriptor.TYPE_BOOL: bool,
FieldDescriptor.TYPE_STRING: str,
FieldDescriptor.TYPE_BYTES: str,
FieldDescriptor.TYPE_UINT32: int,
FieldDescriptor.TYPE_SFIXED32: float,
FieldDescriptor.TYPE_SFIXED64: float,
FieldDescriptor.TYPE_SINT32: int,
FieldDescriptor.TYPE_SINT64: int,
}
from protobuf2pydantic import biz

app = Typer()

def convert_field(level: int, field: FieldDescriptor) -> str:
level += 1
field_type = field.type
extra = None

if field_type == FieldDescriptor.TYPE_ENUM:
type_statement = field.name
class_statement = f"{tab * level}class {field.name}(IntEnum):"
field_statements = map(
lambda value: f"{tab * (level + 1)}{value.name} = {value.index}",
field.enum_type.values,
)
extra = linesep.join([class_statement, *field_statements])
elif field_type == FieldDescriptor.TYPE_MESSAGE:
type_statement = field.message_type.name
if type_statement == Struct.__name__:
type_statement = "Dict"
else:
extra = msg2pydantic(level, field.message_type)
else:
type_statement = type_mapping[field_type].__name__

if field.label == FieldDescriptor.LABEL_REPEATED:
type_statement = f"List[{type_statement}]"

field_statement = f"{tab * level}{field.name}: {type_statement}"
if not extra:
return field_statement
return linesep + extra + one_line + field_statement


def msg2pydantic(level: int, msg: Descriptor) -> str:
class_statement = f"{tab * level}class {msg.name}(BaseModel):"
field_statements = map(partial(convert_field, level), msg.fields)
return linesep.join([class_statement, *field_statements])


def pb2_to_pydantic(module) -> str:
pydantic_models: List[str] = []
for i in dir(module):
obj = getattr(module, i)
if not isinstance(obj, GeneratedProtocolMessageType):
continue
model_string = msg2pydantic(0, obj.DESCRIPTOR)
pydantic_models.append(model_string)

env = Environment(loader=PackageLoader(Path.cwd().name))
template = env.get_template("pydantic.jinja2")
return template.render(models=pydantic_models)
# fixme
@app.command()
def pydantic(pb2_filename: str):
module = import_module(pb2_filename)
biz.pb2_to_pydantic(module)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
grpcio>=1.32.0
grpcio-tools>=1.32.0
jinja2>=2.11.2
typer>=0.3.2
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
],
python_requires='>=3.6',
install_requires=install_requires,
entry_points={"console_scripts": ["pb2pydantic = protobuf2pydantic.main:app"]}
)
2 changes: 1 addition & 1 deletion tests/test_main.py → tests/test_biz.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from protobuf2pydantic.main import pb2_to_pydantic
from protobuf2pydantic.biz import pb2_to_pydantic


class TestPb2ToPydantic:
Expand Down

0 comments on commit daac351

Please sign in to comment.