Merge branch 'master_gh'
This commit is contained in:
@@ -66,8 +66,7 @@ if sys.version_info >= (3, 10):
|
||||
from types import UnionType as _types_UnionType
|
||||
else:
|
||||
|
||||
class _types_UnionType:
|
||||
...
|
||||
class _types_UnionType: ...
|
||||
|
||||
|
||||
# Proto 3 data types
|
||||
@@ -2014,10 +2013,10 @@ class _Timestamp(Timestamp):
|
||||
return f"{result}Z"
|
||||
if (nanos % 1e6) == 0:
|
||||
# Serialize 3 fractional digits.
|
||||
return f"{result}.{int(nanos // 1e6) :03d}Z"
|
||||
return f"{result}.{int(nanos // 1e6):03d}Z"
|
||||
if (nanos % 1e3) == 0:
|
||||
# Serialize 6 fractional digits.
|
||||
return f"{result}.{int(nanos // 1e3) :06d}Z"
|
||||
return f"{result}.{int(nanos // 1e3):06d}Z"
|
||||
# Serialize 9 fractional digits.
|
||||
return f"{result}.{nanos:09d}"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from enum import (
|
||||
EnumMeta,
|
||||
IntEnum,
|
||||
@@ -90,15 +89,8 @@ class EnumType(EnumMeta if TYPE_CHECKING else type):
|
||||
def __iter__(cls) -> Generator[Enum, None, None]:
|
||||
yield from cls._member_map_.values()
|
||||
|
||||
if sys.version_info >= (3, 8): # 3.8 added __reversed__ to dict_values
|
||||
|
||||
def __reversed__(cls) -> Generator[Enum, None, None]:
|
||||
yield from reversed(cls._member_map_.values())
|
||||
|
||||
else:
|
||||
|
||||
def __reversed__(cls) -> Generator[Enum, None, None]:
|
||||
yield from reversed(tuple(cls._member_map_.values()))
|
||||
def __reversed__(cls) -> Generator[Enum, None, None]:
|
||||
yield from reversed(cls._member_map_.values())
|
||||
|
||||
def __getitem__(cls, key: str) -> Enum:
|
||||
return cls._member_map_[key]
|
||||
@@ -140,6 +132,9 @@ class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType):
|
||||
super().__setattr__(self, "value", value)
|
||||
return self
|
||||
|
||||
def __getnewargs_ex__(self) -> Tuple[Tuple[()], Dict[str, Any]]:
|
||||
return (), {"name": self.name, "value": self.value}
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name or "None"
|
||||
|
||||
|
||||
@@ -1204,9 +1204,9 @@ class EnumDescriptorProto(betterproto.Message):
|
||||
name: str = betterproto.string_field(1)
|
||||
value: List["EnumValueDescriptorProto"] = betterproto.message_field(2)
|
||||
options: "EnumOptions" = betterproto.message_field(3)
|
||||
reserved_range: List[
|
||||
"EnumDescriptorProtoEnumReservedRange"
|
||||
] = betterproto.message_field(4)
|
||||
reserved_range: List["EnumDescriptorProtoEnumReservedRange"] = (
|
||||
betterproto.message_field(4)
|
||||
)
|
||||
"""
|
||||
Range of reserved numeric values. Reserved numeric values may not be used
|
||||
by enum values in the same enum declaration. Reserved ranges may not
|
||||
@@ -1792,9 +1792,9 @@ class FeatureSetDefaults(betterproto.Message):
|
||||
for the closest matching edition, followed by proto merges.
|
||||
"""
|
||||
|
||||
defaults: List[
|
||||
"FeatureSetDefaultsFeatureSetEditionDefault"
|
||||
] = betterproto.message_field(1)
|
||||
defaults: List["FeatureSetDefaultsFeatureSetEditionDefault"] = (
|
||||
betterproto.message_field(1)
|
||||
)
|
||||
minimum_edition: "Edition" = betterproto.enum_field(4)
|
||||
"""
|
||||
The minimum supported edition (inclusive) when this was constructed.
|
||||
|
||||
@@ -53,9 +53,9 @@ class CodeGeneratorRequest(betterproto.Message):
|
||||
parameter: str = betterproto.string_field(2)
|
||||
"""The generator parameter passed on the command-line."""
|
||||
|
||||
proto_file: List[
|
||||
"betterproto_lib_pydantic_google_protobuf.FileDescriptorProto"
|
||||
] = betterproto.message_field(15)
|
||||
proto_file: List["betterproto_lib_pydantic_google_protobuf.FileDescriptorProto"] = (
|
||||
betterproto.message_field(15)
|
||||
)
|
||||
"""
|
||||
FileDescriptorProtos for all files in files_to_generate and everything
|
||||
they import. The files will appear in topological order, so each file
|
||||
@@ -195,9 +195,9 @@ class CodeGeneratorResponseFile(betterproto.Message):
|
||||
content: str = betterproto.string_field(15)
|
||||
"""The file contents."""
|
||||
|
||||
generated_code_info: (
|
||||
"betterproto_lib_pydantic_google_protobuf.GeneratedCodeInfo"
|
||||
) = betterproto.message_field(16)
|
||||
generated_code_info: "betterproto_lib_pydantic_google_protobuf.GeneratedCodeInfo" = betterproto.message_field(
|
||||
16
|
||||
)
|
||||
"""
|
||||
Information describing the file content being inserted. If an insertion
|
||||
point is used, this information will be appropriately offset and inserted
|
||||
|
||||
@@ -1064,9 +1064,9 @@ class EnumDescriptorProto(betterproto.Message):
|
||||
name: str = betterproto.string_field(1)
|
||||
value: List["EnumValueDescriptorProto"] = betterproto.message_field(2)
|
||||
options: "EnumOptions" = betterproto.message_field(3)
|
||||
reserved_range: List[
|
||||
"EnumDescriptorProtoEnumReservedRange"
|
||||
] = betterproto.message_field(4)
|
||||
reserved_range: List["EnumDescriptorProtoEnumReservedRange"] = (
|
||||
betterproto.message_field(4)
|
||||
)
|
||||
"""
|
||||
Range of reserved numeric values. Reserved numeric values may not be used
|
||||
by enum values in the same enum declaration. Reserved ranges may not
|
||||
@@ -1688,9 +1688,9 @@ class FeatureSetDefaults(betterproto.Message):
|
||||
for the closest matching edition, followed by proto merges.
|
||||
"""
|
||||
|
||||
defaults: List[
|
||||
"FeatureSetDefaultsFeatureSetEditionDefault"
|
||||
] = betterproto.message_field(1)
|
||||
defaults: List["FeatureSetDefaultsFeatureSetEditionDefault"] = (
|
||||
betterproto.message_field(1)
|
||||
)
|
||||
minimum_edition: "Edition" = betterproto.enum_field(4)
|
||||
"""
|
||||
The minimum supported edition (inclusive) when this was constructed.
|
||||
|
||||
@@ -46,9 +46,9 @@ class CodeGeneratorRequest(betterproto.Message):
|
||||
parameter: str = betterproto.string_field(2)
|
||||
"""The generator parameter passed on the command-line."""
|
||||
|
||||
proto_file: List[
|
||||
"betterproto_lib_google_protobuf.FileDescriptorProto"
|
||||
] = betterproto.message_field(15)
|
||||
proto_file: List["betterproto_lib_google_protobuf.FileDescriptorProto"] = (
|
||||
betterproto.message_field(15)
|
||||
)
|
||||
"""
|
||||
FileDescriptorProtos for all files in files_to_generate and everything
|
||||
they import. The files will appear in topological order, so each file
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from .module_validation import ModuleValidator
|
||||
@@ -6,8 +7,6 @@ from .module_validation import ModuleValidator
|
||||
|
||||
try:
|
||||
# betterproto[compiler] specific dependencies
|
||||
import black
|
||||
import isort.api
|
||||
import jinja2
|
||||
except ImportError as err:
|
||||
print(
|
||||
@@ -32,6 +31,7 @@ def outputfile_compiler(output_file: OutputTemplate) -> str:
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
loader=jinja2.FileSystemLoader(templates_folder),
|
||||
undefined=jinja2.StrictUndefined,
|
||||
)
|
||||
# Load the body first so we have a compleate list of imports needed.
|
||||
body_template = env.get_template("template.py.j2")
|
||||
@@ -39,20 +39,17 @@ def outputfile_compiler(output_file: OutputTemplate) -> str:
|
||||
|
||||
code = body_template.render(output_file=output_file)
|
||||
code = header_template.render(output_file=output_file) + code
|
||||
code = isort.api.sort_code_string(
|
||||
code=code,
|
||||
show_diff=False,
|
||||
py_version=37,
|
||||
profile="black",
|
||||
combine_as_imports=True,
|
||||
lines_after_imports=2,
|
||||
quiet=True,
|
||||
force_grid_wrap=2,
|
||||
known_third_party=["grpclib", "betterproto"],
|
||||
|
||||
# Sort imports, delete unused ones
|
||||
code = subprocess.check_output(
|
||||
["ruff", "check", "--select", "I,F401", "--fix", "--silent", "-"],
|
||||
input=code,
|
||||
encoding="utf-8",
|
||||
)
|
||||
code = black.format_str(
|
||||
src_contents=code,
|
||||
mode=black.Mode(),
|
||||
|
||||
# Format the code
|
||||
code = subprocess.check_output(
|
||||
["ruff", "format", "-"], input=code, encoding="utf-8"
|
||||
)
|
||||
|
||||
# Validate the generated code.
|
||||
|
||||
@@ -153,11 +153,33 @@ def get_comment(
|
||||
) -> str:
|
||||
pad = " " * indent
|
||||
for sci_loc in proto_file.source_code_info.location:
|
||||
if list(sci_loc.path) == path and sci_loc.leading_comments:
|
||||
lines = sci_loc.leading_comments.strip().split("\n")
|
||||
if list(sci_loc.path) == path:
|
||||
all_comments = list(sci_loc.leading_detached_comments)
|
||||
if sci_loc.leading_comments:
|
||||
all_comments.append(sci_loc.leading_comments)
|
||||
if sci_loc.trailing_comments:
|
||||
all_comments.append(sci_loc.trailing_comments)
|
||||
|
||||
lines = []
|
||||
|
||||
for comment in all_comments:
|
||||
lines += comment.split("\n")
|
||||
lines.append("")
|
||||
|
||||
# Remove consecutive empty lines
|
||||
lines = [
|
||||
line for i, line in enumerate(lines) if line or (i == 0 or lines[i - 1])
|
||||
]
|
||||
|
||||
if lines and not lines[-1]:
|
||||
lines.pop() # Remove the last empty line
|
||||
|
||||
# It is common for one line comments to start with a space, for example: // comment
|
||||
# We don't add this space to the generated file.
|
||||
lines = [line[1:] if line and line[0] == " " else line for line in lines]
|
||||
|
||||
# This is a field, message, enum, service, or method
|
||||
if len(lines) == 1 and len(lines[0]) < 79 - indent - 6:
|
||||
lines[0] = lines[0].strip('"')
|
||||
return f'{pad}"""{lines[0]}"""'
|
||||
else:
|
||||
joined = f"\n{pad}".join(lines)
|
||||
@@ -238,7 +260,7 @@ class OutputTemplate:
|
||||
parent_request: PluginRequestCompiler
|
||||
package_proto_obj: FileDescriptorProto
|
||||
input_files: List[str] = field(default_factory=list)
|
||||
imports: Set[str] = field(default_factory=set)
|
||||
imports_end: Set[str] = field(default_factory=set)
|
||||
datetime_imports: Set[str] = field(default_factory=set)
|
||||
pydantic_imports: Set[str] = field(default_factory=set)
|
||||
builtins_import: bool = False
|
||||
@@ -328,12 +350,6 @@ class MessageCompiler(ProtoContentBase):
|
||||
def py_name(self) -> str:
|
||||
return pythonize_class_name(self.proto_name)
|
||||
|
||||
@property
|
||||
def annotation(self) -> str:
|
||||
if self.repeated:
|
||||
return self.typing_compiler.list(self.py_name)
|
||||
return self.py_name
|
||||
|
||||
@property
|
||||
def deprecated_fields(self) -> Iterator[str]:
|
||||
for f in self.fields:
|
||||
@@ -484,13 +500,6 @@ class FieldCompiler(MessageCompiler):
|
||||
def optional(self) -> bool:
|
||||
return self.proto_obj.proto3_optional
|
||||
|
||||
@property
|
||||
def mutable(self) -> bool:
|
||||
"""True if the field is a mutable type, otherwise False."""
|
||||
return self.annotation.startswith(
|
||||
("typing.List[", "typing.Dict[", "dict[", "list[", "Dict[", "List[")
|
||||
)
|
||||
|
||||
@property
|
||||
def field_type(self) -> str:
|
||||
"""String representation of proto field type."""
|
||||
@@ -532,7 +541,7 @@ class FieldCompiler(MessageCompiler):
|
||||
# Type referencing another defined Message or a named enum
|
||||
return get_type_reference(
|
||||
package=self.output_file.package,
|
||||
imports=self.output_file.imports,
|
||||
imports=self.output_file.imports_end,
|
||||
source_type=self.proto_obj.type_name,
|
||||
typing_compiler=self.typing_compiler,
|
||||
pydantic=self.output_file.pydantic_dataclasses,
|
||||
@@ -661,6 +670,7 @@ class EnumDefinitionCompiler(MessageCompiler):
|
||||
|
||||
@dataclass
|
||||
class ServiceCompiler(ProtoContentBase):
|
||||
source_file: FileDescriptorProto
|
||||
parent: OutputTemplate = PLACEHOLDER
|
||||
proto_obj: DescriptorProto = PLACEHOLDER
|
||||
path: List[int] = PLACEHOLDER
|
||||
@@ -682,6 +692,7 @@ class ServiceCompiler(ProtoContentBase):
|
||||
|
||||
@dataclass
|
||||
class ServiceMethodCompiler(ProtoContentBase):
|
||||
source_file: FileDescriptorProto
|
||||
parent: ServiceCompiler
|
||||
proto_obj: MethodDescriptorProto
|
||||
path: List[int] = PLACEHOLDER
|
||||
@@ -730,7 +741,7 @@ class ServiceMethodCompiler(ProtoContentBase):
|
||||
"""
|
||||
return get_type_reference(
|
||||
package=self.output_file.package,
|
||||
imports=self.output_file.imports,
|
||||
imports=self.output_file.imports_end,
|
||||
source_type=self.proto_obj.input_type,
|
||||
typing_compiler=self.output_file.typing_compiler,
|
||||
unwrap=False,
|
||||
@@ -760,7 +771,7 @@ class ServiceMethodCompiler(ProtoContentBase):
|
||||
"""
|
||||
return get_type_reference(
|
||||
package=self.output_file.package,
|
||||
imports=self.output_file.imports,
|
||||
imports=self.output_file.imports_end,
|
||||
source_type=self.proto_obj.output_type,
|
||||
typing_compiler=self.output_file.typing_compiler,
|
||||
unwrap=False,
|
||||
|
||||
@@ -143,7 +143,7 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
|
||||
for output_package_name, output_package in request_data.output_packages.items():
|
||||
for proto_input_file in output_package.input_files:
|
||||
for index, service in enumerate(proto_input_file.service):
|
||||
read_protobuf_service(service, index, output_package)
|
||||
read_protobuf_service(proto_input_file, service, index, output_package)
|
||||
|
||||
# Generate output files
|
||||
output_paths: Set[pathlib.Path] = set()
|
||||
@@ -249,12 +249,21 @@ def read_protobuf_type(
|
||||
|
||||
|
||||
def read_protobuf_service(
|
||||
service: ServiceDescriptorProto, index: int, output_package: OutputTemplate
|
||||
source_file: FileDescriptorProto,
|
||||
service: ServiceDescriptorProto,
|
||||
index: int,
|
||||
output_package: OutputTemplate,
|
||||
) -> None:
|
||||
service_data = ServiceCompiler(
|
||||
parent=output_package, proto_obj=service, path=[6, index]
|
||||
source_file=source_file,
|
||||
parent=output_package,
|
||||
proto_obj=service,
|
||||
path=[6, index],
|
||||
)
|
||||
for j, method in enumerate(service.method):
|
||||
ServiceMethodCompiler(
|
||||
parent=service_data, proto_obj=method, path=[6, index, 2, j]
|
||||
source_file=source_file,
|
||||
parent=service_data,
|
||||
proto_obj=method,
|
||||
path=[6, index, 2, j],
|
||||
)
|
||||
|
||||
@@ -2,13 +2,26 @@
|
||||
# sources: {{ ', '.join(output_file.input_filenames) }}
|
||||
# plugin: python-betterproto
|
||||
# This file has been @generated
|
||||
|
||||
__all__ = (
|
||||
{%- for enum in output_file.enums -%}
|
||||
"{{ enum.py_name }}",
|
||||
{%- endfor -%}
|
||||
{%- for message in output_file.messages -%}
|
||||
"{{ message.py_name }}",
|
||||
{%- endfor -%}
|
||||
{%- for service in output_file.services -%}
|
||||
"{{ service.py_name }}Stub",
|
||||
"{{ service.py_name }}Base",
|
||||
{%- endfor -%}
|
||||
)
|
||||
|
||||
{% for i in output_file.python_module_imports|sort %}
|
||||
import {{ i }}
|
||||
{% endfor %}
|
||||
|
||||
{% if output_file.pydantic_dataclasses %}
|
||||
from pydantic.dataclasses import dataclass
|
||||
from pydantic.dataclasses import rebuild_dataclass
|
||||
{%- else -%}
|
||||
from dataclasses import dataclass
|
||||
{% endif %}
|
||||
@@ -35,10 +48,6 @@ from betterproto.grpc.grpclib_server import ServiceBase
|
||||
import grpclib
|
||||
{% endif %}
|
||||
|
||||
{% for i in output_file.imports|sort %}
|
||||
{{ i }}
|
||||
{% endfor %}
|
||||
|
||||
{% if output_file.imports_type_checking_only %}
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
@@ -77,14 +77,14 @@ class {{ service.py_name }}Stub(betterproto.ServiceStub):
|
||||
, {{ method.py_input_message_param }}: "{{ method.py_input_message_type }}"
|
||||
{%- else -%}
|
||||
{# Client streaming: need a request iterator instead #}
|
||||
, {{ method.py_input_message_param }}_iterator: {{ output_file.typing_compiler.union(output_file.typing_compiler.async_iterable(method.py_input_message_type), output_file.typing_compiler.iterable(method.py_input_message_type)) }}
|
||||
, {{ method.py_input_message_param }}_iterator: "{{ output_file.typing_compiler.union(output_file.typing_compiler.async_iterable(method.py_input_message_type), output_file.typing_compiler.iterable(method.py_input_message_type)) }}"
|
||||
{%- endif -%}
|
||||
,
|
||||
*
|
||||
, timeout: {{ output_file.typing_compiler.optional("float") }} = None
|
||||
, deadline: {{ output_file.typing_compiler.optional('"Deadline"') }} = None
|
||||
, metadata: {{ output_file.typing_compiler.optional('"MetadataLike"') }} = None
|
||||
) -> {% if method.server_streaming %}{{ output_file.typing_compiler.async_iterator(method.py_output_message_type ) }}{% else %}"{{ method.py_output_message_type }}"{% endif %}:
|
||||
) -> "{% if method.server_streaming %}{{ output_file.typing_compiler.async_iterator(method.py_output_message_type ) }}{% else %}{{ method.py_output_message_type }}{% endif %}":
|
||||
{% if method.comment %}
|
||||
{{ method.comment }}
|
||||
|
||||
@@ -143,6 +143,10 @@ class {{ service.py_name }}Stub(betterproto.ServiceStub):
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% for i in output_file.imports_end %}
|
||||
{{ i }}
|
||||
{% endfor %}
|
||||
|
||||
{% for service in output_file.services %}
|
||||
class {{ service.py_name }}Base(ServiceBase):
|
||||
{% if service.comment %}
|
||||
@@ -211,11 +215,3 @@ class {{ service.py_name }}Base(ServiceBase):
|
||||
}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if output_file.pydantic_dataclasses %}
|
||||
{% for message in output_file.messages %}
|
||||
{% if message.has_message_field %}
|
||||
rebuild_dataclass({{ message.py_name }}) # type: ignore
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user