Skip to content

Commit

Permalink
Merge pull request #16 from zerlok/feature/docstrings
Browse files Browse the repository at this point in the history
feature/docstrings
  • Loading branch information
zerlok authored Nov 21, 2024
2 parents f232e65 + bf36288 commit 9a62c88
Show file tree
Hide file tree
Showing 62 changed files with 1,779 additions and 692 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,19 @@ a protoc plugin that generates MyPy stubs

package greeting;

// RPC request for greeting
message GreetRequest {
string name = 1;
}

// RPC response for greeting
message GreetResponse {
string text = 1;
}

// RPC service that provides greet functionality
service Greeter {
// RPC method for greeting
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
```
Expand Down Expand Up @@ -98,6 +102,7 @@ import google.protobuf.message
import typing

class GreetRequest(google.protobuf.message.Message):
"""RPC request for greeting"""

def __init__(self, *, name: builtins.str) -> None:...

Expand All @@ -109,6 +114,7 @@ class GreetRequest(google.protobuf.message.Message):
def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:...

class GreetResponse(google.protobuf.message.Message):
"""RPC response for greeting"""

def __init__(self, *, text: builtins.str) -> None:...

Expand All @@ -131,15 +137,21 @@ import grpc.aio
import typing

class GreeterServicer(metaclass=abc.ABCMeta):
"""RPC service that provides greet functionality"""

@abc.abstractmethod
async def Greet(self, request: greeting_pb2.GreetRequest, context: grpc.aio.ServicerContext[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]) -> greeting_pb2.GreetResponse:...
async def Greet(self, request: greeting_pb2.GreetRequest, context: grpc.aio.ServicerContext[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]) -> greeting_pb2.GreetResponse:
"""RPC method for greeting"""
...

def add_GreeterServicer_to_server(servicer: GreeterServicer, server: grpc.aio.Server) -> None:...

class GreeterStub:
"""RPC service that provides greet functionality"""

def __init__(self, channel: grpc.aio.Channel) -> None:...

def Greet(self, request: greeting_pb2.GreetRequest, *, timeout: typing.Optional[builtins.float]=None, metadata: typing.Optional[grpc.aio.MetadataType]=None, credentials: typing.Optional[grpc.CallCredentials]=None, wait_for_ready: typing.Optional[builtins.bool]=None, compression: typing.Optional[grpc.Compression]=None) -> grpc.aio.UnaryUnaryCall[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]:...
def Greet(self, request: greeting_pb2.GreetRequest, *, timeout: typing.Optional[builtins.float]=None, metadata: typing.Optional[grpc.aio.MetadataType]=None, credentials: typing.Optional[grpc.CallCredentials]=None, wait_for_ready: typing.Optional[builtins.bool]=None, compression: typing.Optional[grpc.Compression]=None) -> grpc.aio.UnaryUnaryCall[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]:
"""RPC method for greeting"""
...
```
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyprotostuben"
version = "0.1.3"
version = "0.2.0"
description = "Generate Python MyPy stub modules from protobuf files."
authors = ["zerlok <[email protected]>"]
readme = "README.md"
Expand Down Expand Up @@ -28,12 +28,15 @@ pytest-cov = "^6.0.0"
ruff = "^0.7.4"
grpc-stubs = "^1.53.0.5"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.ruff]
include = ["src/**/*.py", "tests/**/*.py"]
extend-exclude = ["tests/**/expected_gen/**.py"]
force-exclude = true
line-length = 120

[tool.ruff.lint]
Expand All @@ -54,6 +57,12 @@ ignore = [
files = ["src", "tests"]
strict = true


[[tool.mypy.overrides]]
module = ["tests.integration.cases.*.expected_gen.*"]
ignore_missing_imports = true
ignore_errors = true

[tool.pytest.ini_options]
pythonpath = [
"src",
Expand All @@ -74,5 +83,6 @@ exclude_lines = [
"pragma: no cover",
"@abc.abstractmethod",
"if __name__ == .__main__.:",
"if t.TYPE_CHECKING:",
]
show_missing = true
4 changes: 2 additions & 2 deletions src/pyprotostuben/codegen/abc.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import abc
import typing as t
from pathlib import Path

from google.protobuf.compiler.plugin_pb2 import CodeGeneratorRequest, CodeGeneratorResponse

from pyprotostuben.codegen.model import GeneratedItem
from pyprotostuben.protobuf.file import ProtoFile


Expand All @@ -15,5 +15,5 @@ def run(self, request: CodeGeneratorRequest) -> CodeGeneratorResponse:

class ProtoFileGenerator(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self, file: ProtoFile) -> t.Sequence[t.Tuple[ProtoFile, Path, str]]:
def run(self, file: ProtoFile) -> t.Sequence[GeneratedItem]:
raise NotImplementedError
11 changes: 11 additions & 0 deletions src/pyprotostuben/codegen/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dataclasses import dataclass
from pathlib import Path

from pyprotostuben.protobuf.file import ProtoFile


@dataclass(frozen=True)
class GeneratedItem:
source: ProtoFile
path: Path
content: str
52 changes: 24 additions & 28 deletions src/pyprotostuben/codegen/module_ast.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,47 @@
import abc
import ast
import typing as t
from dataclasses import dataclass
from pathlib import Path

from pyprotostuben.codegen.abc import ProtoFileGenerator
from pyprotostuben.codegen.model import GeneratedItem
from pyprotostuben.logging import Logger, LoggerMixin
from pyprotostuben.protobuf.file import ProtoFile
from pyprotostuben.protobuf.visitor.abc import visit
from pyprotostuben.protobuf.visitor.decorator import ProtoVisitorDecorator
from pyprotostuben.protobuf.visitor.dfs import DFSWalkingProtoVisitor
from pyprotostuben.protobuf.visitor.walker import Walker


class ModuleASTProtoVisitorDecoratorFactory(metaclass=abc.ABCMeta):
@abc.abstractmethod
def create_proto_visitor_decorator(self, modules: t.MutableMapping[Path, ast.Module]) -> ProtoVisitorDecorator:
raise NotImplementedError
@dataclass(frozen=True)
class ModuleASTContext:
file: ProtoFile
modules: t.Mapping[Path, ast.Module]


class ModuleASTBasedProtoFileGenerator(ProtoFileGenerator, LoggerMixin):
def __init__(self, factory: ModuleASTProtoVisitorDecoratorFactory) -> None:
self.__factory = factory
T = t.TypeVar("T", bound=ModuleASTContext)

def run(self, file: ProtoFile) -> t.Sequence[t.Tuple[ProtoFile, Path, str]]:
log = self._log.bind_details(file_name=file.name)
log.debug("file received")

modules: t.Dict[Path, ast.Module] = {}
class ModuleASTBasedProtoFileGenerator(t.Generic[T], ProtoFileGenerator, LoggerMixin):
def __init__(self, context_factory: t.Callable[[ProtoFile], T], visitor: ProtoVisitorDecorator[T]) -> None:
self.__context_factory = context_factory
self.__walker = Walker(visitor)

generator = self.__factory.create_proto_visitor_decorator(modules)
log = log.bind_details(generator=generator)
def run(self, file: ProtoFile) -> t.Sequence[GeneratedItem]:
log = self._log.bind_details(file_name=file.name)
log.debug("file received")

visit(DFSWalkingProtoVisitor(generator), file.descriptor)
log.debug("proto visited", modules=modules)
context = self.__walker.walk(self.__context_factory(file), file.descriptor)
log.debug("proto visited", context=context)

return list(self.__gen_modules(file, modules, log))
return list(self.__gen_modules(context, log))

def __gen_modules(
self,
file: ProtoFile,
modules: t.Mapping[Path, ast.Module],
log: Logger,
) -> t.Iterable[t.Tuple[ProtoFile, Path, str]]:
for path, module_ast in modules.items():
def __gen_modules(self, context: ModuleASTContext, log: Logger) -> t.Iterable[GeneratedItem]:
for path, module_ast in context.modules.items():
if not module_ast.body:
continue

module_content = ast.unparse(module_ast)
log.info("module generated", path=path)
module = GeneratedItem(context.file, path, module_content)

log.info("module generated", module=module)

yield file, path, module_content
yield module
Loading

0 comments on commit 9a62c88

Please sign in to comment.