betterproto2-compiler 0.2.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- betterproto2_compiler/__init__.py +0 -0
- betterproto2_compiler/casing.py +140 -0
- betterproto2_compiler/compile/__init__.py +0 -0
- betterproto2_compiler/compile/importing.py +180 -0
- betterproto2_compiler/compile/naming.py +21 -0
- betterproto2_compiler/known_types/__init__.py +14 -0
- betterproto2_compiler/known_types/any.py +36 -0
- betterproto2_compiler/known_types/duration.py +25 -0
- betterproto2_compiler/known_types/timestamp.py +45 -0
- betterproto2_compiler/lib/__init__.py +0 -0
- betterproto2_compiler/lib/google/__init__.py +0 -0
- betterproto2_compiler/lib/google/protobuf/__init__.py +3338 -0
- betterproto2_compiler/lib/google/protobuf/compiler/__init__.py +235 -0
- betterproto2_compiler/lib/message_pool.py +3 -0
- betterproto2_compiler/plugin/__init__.py +3 -0
- betterproto2_compiler/plugin/__main__.py +3 -0
- betterproto2_compiler/plugin/compiler.py +70 -0
- betterproto2_compiler/plugin/main.py +47 -0
- betterproto2_compiler/plugin/models.py +643 -0
- betterproto2_compiler/plugin/module_validation.py +156 -0
- betterproto2_compiler/plugin/parser.py +272 -0
- betterproto2_compiler/plugin/plugin.bat +2 -0
- betterproto2_compiler/plugin/typing_compiler.py +163 -0
- betterproto2_compiler/py.typed +0 -0
- betterproto2_compiler/settings.py +9 -0
- betterproto2_compiler/templates/header.py.j2 +59 -0
- betterproto2_compiler/templates/template.py.j2 +258 -0
- betterproto2_compiler-0.2.0.dist-info/LICENSE.md +22 -0
- betterproto2_compiler-0.2.0.dist-info/METADATA +35 -0
- betterproto2_compiler-0.2.0.dist-info/RECORD +32 -0
- betterproto2_compiler-0.2.0.dist-info/WHEEL +4 -0
- betterproto2_compiler-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# sources: google/protobuf/compiler/plugin.proto
|
3
|
+
# plugin: python-betterproto2
|
4
|
+
# This file has been @generated
|
5
|
+
|
6
|
+
__all__ = (
|
7
|
+
"CodeGeneratorResponseFeature",
|
8
|
+
"CodeGeneratorRequest",
|
9
|
+
"CodeGeneratorResponse",
|
10
|
+
"CodeGeneratorResponseFile",
|
11
|
+
"Version",
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
from dataclasses import dataclass
|
16
|
+
from typing import (
|
17
|
+
List,
|
18
|
+
Optional,
|
19
|
+
)
|
20
|
+
|
21
|
+
import betterproto2
|
22
|
+
|
23
|
+
from ....message_pool import default_message_pool
|
24
|
+
|
25
|
+
betterproto2.check_compiler_version("0.2.0")
|
26
|
+
|
27
|
+
|
28
|
+
class CodeGeneratorResponseFeature(betterproto2.Enum):
|
29
|
+
"""
|
30
|
+
Sync with code_generator.h.
|
31
|
+
"""
|
32
|
+
|
33
|
+
FEATURE_NONE = 0
|
34
|
+
|
35
|
+
FEATURE_PROTO3_OPTIONAL = 1
|
36
|
+
|
37
|
+
FEATURE_SUPPORTS_EDITIONS = 2
|
38
|
+
|
39
|
+
|
40
|
+
@dataclass(eq=False, repr=False)
|
41
|
+
class CodeGeneratorRequest(betterproto2.Message):
|
42
|
+
"""
|
43
|
+
An encoded CodeGeneratorRequest is written to the plugin's stdin.
|
44
|
+
"""
|
45
|
+
|
46
|
+
file_to_generate: "List[str]" = betterproto2.field(1, betterproto2.TYPE_STRING, repeated=True)
|
47
|
+
"""
|
48
|
+
The .proto files that were explicitly listed on the command-line. The
|
49
|
+
code generator should generate code only for these files. Each file's
|
50
|
+
descriptor will be included in proto_file, below.
|
51
|
+
"""
|
52
|
+
|
53
|
+
parameter: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
54
|
+
"""
|
55
|
+
The generator parameter passed on the command-line.
|
56
|
+
"""
|
57
|
+
|
58
|
+
proto_file: "List[__protobuf__.FileDescriptorProto]" = betterproto2.field(
|
59
|
+
15, betterproto2.TYPE_MESSAGE, repeated=True
|
60
|
+
)
|
61
|
+
"""
|
62
|
+
FileDescriptorProtos for all files in files_to_generate and everything
|
63
|
+
they import. The files will appear in topological order, so each file
|
64
|
+
appears before any file that imports it.
|
65
|
+
|
66
|
+
Note: the files listed in files_to_generate will include runtime-retention
|
67
|
+
options only, but all other files will include source-retention options.
|
68
|
+
The source_file_descriptors field below is available in case you need
|
69
|
+
source-retention options for files_to_generate.
|
70
|
+
|
71
|
+
protoc guarantees that all proto_files will be written after
|
72
|
+
the fields above, even though this is not technically guaranteed by the
|
73
|
+
protobuf wire format. This theoretically could allow a plugin to stream
|
74
|
+
in the FileDescriptorProtos and handle them one by one rather than read
|
75
|
+
the entire set into memory at once. However, as of this writing, this
|
76
|
+
is not similarly optimized on protoc's end -- it will store all fields in
|
77
|
+
memory at once before sending them to the plugin.
|
78
|
+
|
79
|
+
Type names of fields and extensions in the FileDescriptorProto are always
|
80
|
+
fully qualified.
|
81
|
+
"""
|
82
|
+
|
83
|
+
source_file_descriptors: "List[__protobuf__.FileDescriptorProto]" = betterproto2.field(
|
84
|
+
17, betterproto2.TYPE_MESSAGE, repeated=True
|
85
|
+
)
|
86
|
+
"""
|
87
|
+
File descriptors with all options, including source-retention options.
|
88
|
+
These descriptors are only provided for the files listed in
|
89
|
+
files_to_generate.
|
90
|
+
"""
|
91
|
+
|
92
|
+
compiler_version: "Optional[Version]" = betterproto2.field(3, betterproto2.TYPE_MESSAGE, optional=True)
|
93
|
+
"""
|
94
|
+
The version number of protocol compiler.
|
95
|
+
"""
|
96
|
+
|
97
|
+
|
98
|
+
default_message_pool.register_message("google.protobuf.compiler", "CodeGeneratorRequest", CodeGeneratorRequest)
|
99
|
+
|
100
|
+
|
101
|
+
@dataclass(eq=False, repr=False)
|
102
|
+
class CodeGeneratorResponse(betterproto2.Message):
|
103
|
+
"""
|
104
|
+
The plugin writes an encoded CodeGeneratorResponse to stdout.
|
105
|
+
"""
|
106
|
+
|
107
|
+
error: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
108
|
+
"""
|
109
|
+
Error message. If non-empty, code generation failed. The plugin process
|
110
|
+
should exit with status code zero even if it reports an error in this way.
|
111
|
+
|
112
|
+
This should be used to indicate errors in .proto files which prevent the
|
113
|
+
code generator from generating correct code. Errors which indicate a
|
114
|
+
problem in protoc itself -- such as the input CodeGeneratorRequest being
|
115
|
+
unparseable -- should be reported by writing a message to stderr and
|
116
|
+
exiting with a non-zero status code.
|
117
|
+
"""
|
118
|
+
|
119
|
+
supported_features: "int" = betterproto2.field(2, betterproto2.TYPE_UINT64)
|
120
|
+
"""
|
121
|
+
A bitmask of supported features that the code generator supports.
|
122
|
+
This is a bitwise "or" of values from the Feature enum.
|
123
|
+
"""
|
124
|
+
|
125
|
+
file: "List[CodeGeneratorResponseFile]" = betterproto2.field(15, betterproto2.TYPE_MESSAGE, repeated=True)
|
126
|
+
|
127
|
+
|
128
|
+
default_message_pool.register_message("google.protobuf.compiler", "CodeGeneratorResponse", CodeGeneratorResponse)
|
129
|
+
|
130
|
+
|
131
|
+
@dataclass(eq=False, repr=False)
|
132
|
+
class CodeGeneratorResponseFile(betterproto2.Message):
|
133
|
+
"""
|
134
|
+
Represents a single generated file.
|
135
|
+
"""
|
136
|
+
|
137
|
+
name: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
138
|
+
"""
|
139
|
+
The file name, relative to the output directory. The name must not
|
140
|
+
contain "." or ".." components and must be relative, not be absolute (so,
|
141
|
+
the file cannot lie outside the output directory). "/" must be used as
|
142
|
+
the path separator, not "\".
|
143
|
+
|
144
|
+
If the name is omitted, the content will be appended to the previous
|
145
|
+
file. This allows the generator to break large files into small chunks,
|
146
|
+
and allows the generated text to be streamed back to protoc so that large
|
147
|
+
files need not reside completely in memory at one time. Note that as of
|
148
|
+
this writing protoc does not optimize for this -- it will read the entire
|
149
|
+
CodeGeneratorResponse before writing files to disk.
|
150
|
+
"""
|
151
|
+
|
152
|
+
insertion_point: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
153
|
+
"""
|
154
|
+
If non-empty, indicates that the named file should already exist, and the
|
155
|
+
content here is to be inserted into that file at a defined insertion
|
156
|
+
point. This feature allows a code generator to extend the output
|
157
|
+
produced by another code generator. The original generator may provide
|
158
|
+
insertion points by placing special annotations in the file that look
|
159
|
+
like:
|
160
|
+
@@protoc_insertion_point(NAME)
|
161
|
+
The annotation can have arbitrary text before and after it on the line,
|
162
|
+
which allows it to be placed in a comment. NAME should be replaced with
|
163
|
+
an identifier naming the point -- this is what other generators will use
|
164
|
+
as the insertion_point. Code inserted at this point will be placed
|
165
|
+
immediately above the line containing the insertion point (thus multiple
|
166
|
+
insertions to the same point will come out in the order they were added).
|
167
|
+
The double-@ is intended to make it unlikely that the generated code
|
168
|
+
could contain things that look like insertion points by accident.
|
169
|
+
|
170
|
+
For example, the C++ code generator places the following line in the
|
171
|
+
.pb.h files that it generates:
|
172
|
+
// @@protoc_insertion_point(namespace_scope)
|
173
|
+
This line appears within the scope of the file's package namespace, but
|
174
|
+
outside of any particular class. Another plugin can then specify the
|
175
|
+
insertion_point "namespace_scope" to generate additional classes or
|
176
|
+
other declarations that should be placed in this scope.
|
177
|
+
|
178
|
+
Note that if the line containing the insertion point begins with
|
179
|
+
whitespace, the same whitespace will be added to every line of the
|
180
|
+
inserted text. This is useful for languages like Python, where
|
181
|
+
indentation matters. In these languages, the insertion point comment
|
182
|
+
should be indented the same amount as any inserted code will need to be
|
183
|
+
in order to work correctly in that context.
|
184
|
+
|
185
|
+
The code generator that generates the initial file and the one which
|
186
|
+
inserts into it must both run as part of a single invocation of protoc.
|
187
|
+
Code generators are executed in the order in which they appear on the
|
188
|
+
command line.
|
189
|
+
|
190
|
+
If |insertion_point| is present, |name| must also be present.
|
191
|
+
"""
|
192
|
+
|
193
|
+
content: "str" = betterproto2.field(15, betterproto2.TYPE_STRING)
|
194
|
+
"""
|
195
|
+
The file contents.
|
196
|
+
"""
|
197
|
+
|
198
|
+
generated_code_info: "Optional[__protobuf__.GeneratedCodeInfo]" = betterproto2.field(
|
199
|
+
16, betterproto2.TYPE_MESSAGE, optional=True
|
200
|
+
)
|
201
|
+
"""
|
202
|
+
Information describing the file content being inserted. If an insertion
|
203
|
+
point is used, this information will be appropriately offset and inserted
|
204
|
+
into the code generation metadata for the generated files.
|
205
|
+
"""
|
206
|
+
|
207
|
+
|
208
|
+
default_message_pool.register_message(
|
209
|
+
"google.protobuf.compiler", "CodeGeneratorResponse.File", CodeGeneratorResponseFile
|
210
|
+
)
|
211
|
+
|
212
|
+
|
213
|
+
@dataclass(eq=False, repr=False)
|
214
|
+
class Version(betterproto2.Message):
|
215
|
+
"""
|
216
|
+
The version number of protocol compiler.
|
217
|
+
"""
|
218
|
+
|
219
|
+
major: "int" = betterproto2.field(1, betterproto2.TYPE_INT32)
|
220
|
+
|
221
|
+
minor: "int" = betterproto2.field(2, betterproto2.TYPE_INT32)
|
222
|
+
|
223
|
+
patch: "int" = betterproto2.field(3, betterproto2.TYPE_INT32)
|
224
|
+
|
225
|
+
suffix: "str" = betterproto2.field(4, betterproto2.TYPE_STRING)
|
226
|
+
"""
|
227
|
+
A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
|
228
|
+
be empty for mainline stable releases.
|
229
|
+
"""
|
230
|
+
|
231
|
+
|
232
|
+
default_message_pool.register_message("google.protobuf.compiler", "Version", Version)
|
233
|
+
|
234
|
+
|
235
|
+
from ... import protobuf as __protobuf__
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import os.path
|
2
|
+
import subprocess
|
3
|
+
import sys
|
4
|
+
from importlib import metadata
|
5
|
+
|
6
|
+
from .module_validation import ModuleValidator
|
7
|
+
|
8
|
+
try:
|
9
|
+
# betterproto[compiler] specific dependencies
|
10
|
+
import jinja2
|
11
|
+
except ImportError as err:
|
12
|
+
print(
|
13
|
+
"\033[31m"
|
14
|
+
f"Unable to import `{err.name}` from betterproto plugin! "
|
15
|
+
"Please ensure that you've installed betterproto as "
|
16
|
+
'`pip install "betterproto[compiler]"` so that compiler dependencies '
|
17
|
+
"are included."
|
18
|
+
"\033[0m",
|
19
|
+
)
|
20
|
+
raise SystemExit(1)
|
21
|
+
|
22
|
+
from .models import OutputTemplate
|
23
|
+
|
24
|
+
|
25
|
+
def outputfile_compiler(output_file: OutputTemplate) -> str:
|
26
|
+
templates_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "templates"))
|
27
|
+
|
28
|
+
version = metadata.version("betterproto2_compiler")
|
29
|
+
|
30
|
+
env = jinja2.Environment(
|
31
|
+
trim_blocks=True,
|
32
|
+
lstrip_blocks=True,
|
33
|
+
loader=jinja2.FileSystemLoader(templates_folder),
|
34
|
+
undefined=jinja2.StrictUndefined,
|
35
|
+
)
|
36
|
+
# Load the body first so we have a compleate list of imports needed.
|
37
|
+
body_template = env.get_template("template.py.j2")
|
38
|
+
header_template = env.get_template("header.py.j2")
|
39
|
+
|
40
|
+
code = body_template.render(output_file=output_file)
|
41
|
+
code = header_template.render(output_file=output_file, version=version) + "\n" + code
|
42
|
+
|
43
|
+
try:
|
44
|
+
# Sort imports, delete unused ones
|
45
|
+
code = subprocess.check_output(
|
46
|
+
["ruff", "check", "--select", "I,F401,TCH005", "--fix", "--silent", "-"],
|
47
|
+
input=code,
|
48
|
+
encoding="utf-8",
|
49
|
+
)
|
50
|
+
|
51
|
+
# Format the code
|
52
|
+
code = subprocess.check_output(["ruff", "format", "-"], input=code, encoding="utf-8")
|
53
|
+
except subprocess.CalledProcessError:
|
54
|
+
with open("invalid-generated-code.py", "w") as f:
|
55
|
+
f.write(code)
|
56
|
+
|
57
|
+
raise SyntaxError(
|
58
|
+
f"Can't format the source code:\nThe invalid generated code has been written in `invalid-generated-code.py`"
|
59
|
+
)
|
60
|
+
|
61
|
+
# Validate the generated code.
|
62
|
+
validator = ModuleValidator(iter(code.splitlines()))
|
63
|
+
if not validator.validate():
|
64
|
+
message_builder = ["[WARNING]: Generated code has collisions in the module:"]
|
65
|
+
for collision, lines in validator.collisions.items():
|
66
|
+
message_builder.append(f' "{collision}" on lines:')
|
67
|
+
for num, line in lines:
|
68
|
+
message_builder.append(f" {num}:{line}")
|
69
|
+
print("\n".join(message_builder), file=sys.stderr)
|
70
|
+
return code
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
import os
|
4
|
+
import sys
|
5
|
+
|
6
|
+
from betterproto2_compiler.lib.google.protobuf.compiler import (
|
7
|
+
CodeGeneratorRequest,
|
8
|
+
)
|
9
|
+
from betterproto2_compiler.plugin.parser import generate_code
|
10
|
+
|
11
|
+
|
12
|
+
def main() -> None:
|
13
|
+
"""The plugin's main entry point."""
|
14
|
+
# Read request message from stdin
|
15
|
+
data = sys.stdin.buffer.read()
|
16
|
+
|
17
|
+
# Parse request
|
18
|
+
request = CodeGeneratorRequest()
|
19
|
+
request.parse(data)
|
20
|
+
|
21
|
+
dump_file = os.getenv("BETTERPROTO_DUMP")
|
22
|
+
if dump_file:
|
23
|
+
dump_request(dump_file, request)
|
24
|
+
|
25
|
+
# Generate code
|
26
|
+
response = generate_code(request)
|
27
|
+
|
28
|
+
# Serialise response message
|
29
|
+
output = response.SerializeToString()
|
30
|
+
|
31
|
+
# Write to stdout
|
32
|
+
sys.stdout.buffer.write(output)
|
33
|
+
|
34
|
+
|
35
|
+
def dump_request(dump_file: str, request: CodeGeneratorRequest) -> None:
|
36
|
+
"""
|
37
|
+
For developers: Supports running plugin.py standalone so its possible to debug it.
|
38
|
+
Run protoc (or generate.py) with BETTERPROTO_DUMP="yourfile.bin" to write the request to a file.
|
39
|
+
Then run plugin.py from your IDE in debugging mode, and redirect stdin to the file.
|
40
|
+
"""
|
41
|
+
with open(str(dump_file), "wb") as fh:
|
42
|
+
sys.stderr.write(f"\033[31mWriting input from protoc to: {dump_file}\033[0m\n")
|
43
|
+
fh.write(request.SerializeToString())
|
44
|
+
|
45
|
+
|
46
|
+
if __name__ == "__main__":
|
47
|
+
main()
|