EmbeddedProto 4.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- EmbeddedProto/EmbeddedProto.py +122 -0
- EmbeddedProto/Features.py +255 -0
- EmbeddedProto/Field.py +1295 -0
- EmbeddedProto/Oneof.py +74 -0
- EmbeddedProto/ProtoFile.py +210 -0
- EmbeddedProto/TypeDefinitions.py +367 -0
- EmbeddedProto/__init__.py +0 -0
- EmbeddedProto/__main__.py +28 -0
- EmbeddedProto/config.py +173 -0
- EmbeddedProto/custom_header.py +513 -0
- EmbeddedProto/embedded_proto_options.proto +62 -0
- EmbeddedProto/embedded_proto_options_pb2.py +37 -0
- EmbeddedProto/field_options.py +344 -0
- EmbeddedProto/main.py +316 -0
- EmbeddedProto/src/EmbeddedProto/BytesStringCallback.h +476 -0
- EmbeddedProto/src/EmbeddedProto/Defines.h +118 -0
- EmbeddedProto/src/EmbeddedProto/EmptyArray.h +81 -0
- EmbeddedProto/src/EmbeddedProto/Errors.h +53 -0
- EmbeddedProto/src/EmbeddedProto/FieldStringBytes.h +793 -0
- EmbeddedProto/src/EmbeddedProto/Fields.h +832 -0
- EmbeddedProto/src/EmbeddedProto/Functional.h +412 -0
- EmbeddedProto/src/EmbeddedProto/MessageCallback.h +688 -0
- EmbeddedProto/src/EmbeddedProto/MessageInterface.h +792 -0
- EmbeddedProto/src/EmbeddedProto/MessageSizeCalculator.h +109 -0
- EmbeddedProto/src/EmbeddedProto/MessageState.h +207 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferFixedSize.h +204 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferInterface.h +181 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferSection.h +232 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedField.h +1053 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedFieldCallback.h +432 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedFieldFixedSize.h +367 -0
- EmbeddedProto/src/EmbeddedProto/Version.h +39 -0
- EmbeddedProto/src/EmbeddedProto/WireFormatter.h +852 -0
- EmbeddedProto/src/EmbeddedProto/WriteBufferFixedSize.h +114 -0
- EmbeddedProto/src/EmbeddedProto/WriteBufferInterface.h +134 -0
- EmbeddedProto/src/EmbeddedProto.h +55 -0
- EmbeddedProto/templates/FieldBasic_Deserialize.h.jinja2 +26 -0
- EmbeddedProto/templates/FieldBasic_GetSet.h.jinja2 +89 -0
- EmbeddedProto/templates/FieldBytes_GetSet.h.jinja2 +86 -0
- EmbeddedProto/templates/FieldEnum_Deserialize.h.jinja2 +50 -0
- EmbeddedProto/templates/FieldEnum_GetSet.h.jinja2 +93 -0
- EmbeddedProto/templates/FieldErrorRecursive_GetSet.h.jinja2 +29 -0
- EmbeddedProto/templates/FieldMap_GetSet.h.jinja2 +203 -0
- EmbeddedProto/templates/FieldMsg_Deserialize.h.jinja2 +44 -0
- EmbeddedProto/templates/FieldMsg_GetSet.h.jinja2 +93 -0
- EmbeddedProto/templates/FieldRepeated_GetSet.h.jinja2 +94 -0
- EmbeddedProto/templates/FieldString_GetSet.h.jinja2 +86 -0
- EmbeddedProto/templates/Field_DeserializePartial.h.jinja2 +33 -0
- EmbeddedProto/templates/Field_Serialize.h.jinja2 +112 -0
- EmbeddedProto/templates/Field_SerializePartial.h.jinja2 +119 -0
- EmbeddedProto/templates/Header.h.jinja2 +108 -0
- EmbeddedProto/templates/TypeDefEnum.h.jinja2 +45 -0
- EmbeddedProto/templates/TypeDefMsg.h.jinja2 +791 -0
- EmbeddedProto/templates/TypeOneof.h.jinja2 +178 -0
- EmbeddedProto/version.json +3 -0
- embeddedproto-4.0.0b1.dist-info/METADATA +141 -0
- embeddedproto-4.0.0b1.dist-info/RECORD +63 -0
- embeddedproto-4.0.0b1.dist-info/WHEEL +5 -0
- embeddedproto-4.0.0b1.dist-info/entry_points.txt +3 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSE +30 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/GPL-3.0-only.txt +674 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/LicenseRef-EmbeddedProto-Commercial.txt +12 -0
- embeddedproto-4.0.0b1.dist-info/top_level.txt +1 -0
EmbeddedProto/main.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Embedded Proto.
|
|
5
|
+
#
|
|
6
|
+
# Embedded Proto is dual licensed. You may use it under the terms of the
|
|
7
|
+
# GNU General Public License version 3 (GPLv3) as published by the Free
|
|
8
|
+
# Software Foundation, or under a commercial license from Embedded AMS B.V.
|
|
9
|
+
#
|
|
10
|
+
# Under the GPLv3 you must release the source code of
|
|
11
|
+
# any product you distribute that includes Embedded Proto or code
|
|
12
|
+
# generated by it. A commercial license removes that obligation.
|
|
13
|
+
# See <https://embeddedproto.com/pricing/>.
|
|
14
|
+
#
|
|
15
|
+
# Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
|
|
16
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
17
|
+
# See the LICENSE file in the root of the repository for details.
|
|
18
|
+
#
|
|
19
|
+
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
|
|
20
|
+
#
|
|
21
|
+
# Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
|
|
22
|
+
#
|
|
23
|
+
|
|
24
|
+
import io
|
|
25
|
+
import sys
|
|
26
|
+
import locale
|
|
27
|
+
import json
|
|
28
|
+
import re
|
|
29
|
+
from datetime import datetime
|
|
30
|
+
from EmbeddedProto.ProtoFile import ProtoFile, is_excluded_proto_file
|
|
31
|
+
from EmbeddedProto import custom_header
|
|
32
|
+
from EmbeddedProto import embedded_proto_options_pb2
|
|
33
|
+
from EmbeddedProto import field_options
|
|
34
|
+
from google.protobuf import descriptor_pb2
|
|
35
|
+
from google.protobuf.compiler import plugin_pb2 as plugin
|
|
36
|
+
import jinja2
|
|
37
|
+
from importlib.resources import path as resource_path
|
|
38
|
+
|
|
39
|
+
def parse_version(version_string):
|
|
40
|
+
"""Split a version string into its numeric parts, tolerating a pre-release or dev suffix.
|
|
41
|
+
|
|
42
|
+
Accepts "4.1.0" as well as PEP 440 forms such as "4.1.0b1", "4.1.0rc2" and "4.1.0.dev7". The
|
|
43
|
+
returned dictionary holds the integer "major", "minor" and "patch" numbers and the full "string".
|
|
44
|
+
"""
|
|
45
|
+
match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)(.*)", version_string)
|
|
46
|
+
if not match:
|
|
47
|
+
raise Exception("Invalid version string: \"" + version_string + "\".")
|
|
48
|
+
return {
|
|
49
|
+
"major": int(match.group(1)),
|
|
50
|
+
"minor": int(match.group(2)),
|
|
51
|
+
"patch": int(match.group(3)),
|
|
52
|
+
"string": version_string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def load_version_info():
|
|
57
|
+
"""Load version information from version.json"""
|
|
58
|
+
with resource_path("EmbeddedProto", "version.json") as filepath:
|
|
59
|
+
with open(filepath) as f:
|
|
60
|
+
version_data = json.load(f)
|
|
61
|
+
return parse_version(version_data["version"])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# -----------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# Describe an exception in the error reported back to protoc.
|
|
68
|
+
def describe_exception(e):
|
|
69
|
+
# Errors this generator raises on purpose are a plain Exception, their text is the whole story. Any other type
|
|
70
|
+
# signals an unexpected failure, there the type name is required as str() may hold nothing but a variable name.
|
|
71
|
+
if type(e) is Exception:
|
|
72
|
+
return str(e)
|
|
73
|
+
return type(e).__name__ + ": " + str(e)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# The name of the message in embedded_proto_options.proto holding the generator options.
|
|
77
|
+
OPTIONS_MESSAGE_NAME = "Options"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# The name of the plugin parameter pointing at an external field options file.
|
|
81
|
+
OPTIONS_FILE_PARAMETER = "options_file"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Return the options file paths given as plugin parameters.
|
|
85
|
+
def parse_parameters(parameter):
|
|
86
|
+
# Protoc gathers the plugin parameters into one string of comma separated key=value pairs. They are written
|
|
87
|
+
# either as separate flags, which is the readable form and the one the documentation uses:
|
|
88
|
+
# --eams_opt=options_file=base.json --eams_opt=options_file=board_x.json
|
|
89
|
+
# or in front of the output directory:
|
|
90
|
+
# --eams_out=options_file=base.json,options_file=board_x.json:./generated_src
|
|
91
|
+
# Both arrive here as the same string. Splitting on the comma only, rather than on whitespace as well, keeps a
|
|
92
|
+
# Windows path with backslashes intact.
|
|
93
|
+
paths = []
|
|
94
|
+
for argument in parameter.split(","):
|
|
95
|
+
argument = argument.strip()
|
|
96
|
+
if not argument:
|
|
97
|
+
continue
|
|
98
|
+
name, separator, value = argument.partition("=")
|
|
99
|
+
if (OPTIONS_FILE_PARAMETER != name) or not separator or not value:
|
|
100
|
+
raise Exception("Unknown generator parameter: " + argument + ". The only parameter supported is "
|
|
101
|
+
+ OPTIONS_FILE_PARAMETER + "=<path to a json file>, it may be given more than once.")
|
|
102
|
+
paths.append(value)
|
|
103
|
+
return paths
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# Read the external field options file(s) named as plugin parameters and check them against the definitions in this
|
|
107
|
+
# run. Returns an empty OptionsFile when no file was requested.
|
|
108
|
+
def load_field_options(request):
|
|
109
|
+
options = field_options.load(parse_parameters(request.parameter))
|
|
110
|
+
if options:
|
|
111
|
+
schema = field_options.schema_from_descriptors(
|
|
112
|
+
[proto_file for proto_file in request.proto_file
|
|
113
|
+
if not is_excluded_proto_file(proto_file.name)])
|
|
114
|
+
options.validate_against(schema)
|
|
115
|
+
return options
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Raise a clear error when the compiled options module lags behind embedded_proto_options.proto.
|
|
119
|
+
def verify_options_module_is_current(request):
|
|
120
|
+
# protoc parses embedded_proto_options.proto on every run, this generator reads the options through
|
|
121
|
+
# embedded_proto_options_pb2.py. That module is generated once, while installing Embedded Proto. When an option
|
|
122
|
+
# is added to the proto file without reinstalling, the module lags behind and reading the new option fails with
|
|
123
|
+
# an AttributeError naming nothing but the option. Compare both and report what is actually wrong.
|
|
124
|
+
compiled_options = {field.name for field in embedded_proto_options_pb2.Options.DESCRIPTOR.fields}
|
|
125
|
+
for proto_file in request.proto_file:
|
|
126
|
+
if "embedded_proto_options.proto" not in proto_file.name:
|
|
127
|
+
continue
|
|
128
|
+
for message in proto_file.message_type:
|
|
129
|
+
if OPTIONS_MESSAGE_NAME != message.name:
|
|
130
|
+
continue
|
|
131
|
+
missing = [field.name for field in message.field if field.name not in compiled_options]
|
|
132
|
+
if missing:
|
|
133
|
+
raise Exception("The generated file embedded_proto_options_pb2.py is out of date, it lacks the "
|
|
134
|
+
"option(s): " + ", ".join(missing) + ". It is regenerated when installing Embedded "
|
|
135
|
+
"Proto, please rerun: python3 install.py")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def generate_code(request, respones):
|
|
139
|
+
verify_options_module_is_current(request)
|
|
140
|
+
|
|
141
|
+
# The options a field may also have been given inline in the .proto, read from an external file instead.
|
|
142
|
+
options_file = load_field_options(request)
|
|
143
|
+
|
|
144
|
+
# Create definitions for al proto files in the request except for the files we never generate code for. Our own
|
|
145
|
+
# options file only holds generator settings. The google descriptor file only holds the definitions required to
|
|
146
|
+
# declare custom options, they are used by protoc and other plugins but not by the embedded target.
|
|
147
|
+
file_definitions = [ProtoFile(proto_file, options_file) for proto_file in request.proto_file
|
|
148
|
+
if not is_excluded_proto_file(proto_file.name)]
|
|
149
|
+
|
|
150
|
+
# Obtain all definitions made in all the files to properly link definitions with fields using them. This to properly
|
|
151
|
+
# create template parameters.
|
|
152
|
+
all_types_definitions = {"enums": [], "messages": []}
|
|
153
|
+
for fd in file_definitions:
|
|
154
|
+
nt = fd.get_all_nested_types()
|
|
155
|
+
all_types_definitions["enums"].extend(nt["enums"])
|
|
156
|
+
all_types_definitions["messages"].extend(nt["messages"])
|
|
157
|
+
|
|
158
|
+
# Match all fields with their respective type definition.
|
|
159
|
+
for fd in file_definitions:
|
|
160
|
+
fd.match_fields_with_definitions(all_types_definitions)
|
|
161
|
+
|
|
162
|
+
# Add template parameters to the fields that need them. A message can only be resolved after the messages it uses
|
|
163
|
+
# are resolved. As the files are not sorted by their mutual dependencies this may take several passes.
|
|
164
|
+
unresolved_files = []
|
|
165
|
+
for _ in range(3):
|
|
166
|
+
unresolved_files = [fd for fd in file_definitions if not fd.register_template_parameters()]
|
|
167
|
+
if not unresolved_files:
|
|
168
|
+
break
|
|
169
|
+
|
|
170
|
+
if unresolved_files:
|
|
171
|
+
raise Exception("Messages with repeated, string or byte fields use template parameters to define their length. "
|
|
172
|
+
"For some reason it was not possible to add all required template parameters in the following "
|
|
173
|
+
"file(s): " + ", ".join(fd.descriptor.name for fd in unresolved_files) + ".")
|
|
174
|
+
|
|
175
|
+
# Load version information
|
|
176
|
+
version_info = load_version_info()
|
|
177
|
+
|
|
178
|
+
# Resolve the optional custom header once per generation run (best-effort,
|
|
179
|
+
# never blocks the build).
|
|
180
|
+
plugin_version = "{major}.{minor}.{patch}".format(**version_info)
|
|
181
|
+
custom_header_text = custom_header.resolve_custom_header(
|
|
182
|
+
custom_header.resolve_token(), plugin_version)
|
|
183
|
+
|
|
184
|
+
with resource_path("EmbeddedProto", "templates") as filepath:
|
|
185
|
+
template_loader = jinja2.FileSystemLoader(searchpath=filepath)
|
|
186
|
+
template_env = jinja2.Environment(loader=template_loader, trim_blocks=True, lstrip_blocks=True)
|
|
187
|
+
# Add date, time and version information:
|
|
188
|
+
template_env.globals['current_date_and_time'] = get_current_date_and_time()
|
|
189
|
+
template_env.globals['version_major'] = version_info['major']
|
|
190
|
+
template_env.globals['version_minor'] = version_info['minor']
|
|
191
|
+
template_env.globals['version_patch'] = version_info['patch']
|
|
192
|
+
template_env.globals['custom_header'] = custom_header_text
|
|
193
|
+
|
|
194
|
+
for fd in file_definitions:
|
|
195
|
+
file_str = fd.render(template_env)
|
|
196
|
+
if file_str:
|
|
197
|
+
f = respones.file.add()
|
|
198
|
+
f.name = fd.filename_with_folder + options_file.header_extension()
|
|
199
|
+
f.content = file_str
|
|
200
|
+
else:
|
|
201
|
+
break
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_current_date_and_time():
|
|
205
|
+
locale.setlocale(locale.LC_TIME, '')
|
|
206
|
+
return datetime.now().strftime('%c')
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# -----------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
def configure_response_features(response):
|
|
212
|
+
response.supported_features = (
|
|
213
|
+
plugin.CodeGeneratorResponse.FEATURE_PROTO3_OPTIONAL |
|
|
214
|
+
plugin.CodeGeneratorResponse.FEATURE_SUPPORTS_EDITIONS
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if hasattr(descriptor_pb2, "Edition"):
|
|
218
|
+
response.minimum_edition = descriptor_pb2.Edition.Value("EDITION_2023")
|
|
219
|
+
response.maximum_edition = descriptor_pb2.Edition.Value("EDITION_2024")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def main_plugin():
|
|
223
|
+
# The main function when running the scrip as a protoc plugin. It will read in the protoc data from the stdin and
|
|
224
|
+
# write back the output to stdout.
|
|
225
|
+
|
|
226
|
+
# Create the response object
|
|
227
|
+
response = plugin.CodeGeneratorResponse()
|
|
228
|
+
configure_response_features(response)
|
|
229
|
+
|
|
230
|
+
# Read request message from stdin
|
|
231
|
+
data = io.open(sys.stdin.fileno(), "rb").read()
|
|
232
|
+
request = plugin.CodeGeneratorRequest.FromString(data)
|
|
233
|
+
|
|
234
|
+
# If desired output debug data.
|
|
235
|
+
if '--debug' in sys.argv:
|
|
236
|
+
# Write the requests to a file for easy debugging.
|
|
237
|
+
with open("./debug_embedded_proto.bin", 'wb') as file:
|
|
238
|
+
file.write(request.SerializeToString())
|
|
239
|
+
|
|
240
|
+
from google.protobuf.json_format import MessageToJson
|
|
241
|
+
|
|
242
|
+
with open("./debug_embedded_proto.json", 'w') as file:
|
|
243
|
+
file.write(MessageToJson(request))
|
|
244
|
+
|
|
245
|
+
# Generate code
|
|
246
|
+
try:
|
|
247
|
+
generate_code(request, response)
|
|
248
|
+
except jinja2.UndefinedError as e:
|
|
249
|
+
response.error = "Embedded Proto ERROR - Template Undefined Error exception: " + str(e)
|
|
250
|
+
except jinja2.TemplateRuntimeError as e:
|
|
251
|
+
response.error = "Embedded Proto ERROR - Template Runtime Error exception: " + str(e)
|
|
252
|
+
except jinja2.TemplateAssertionError as e:
|
|
253
|
+
response.error = "Embedded Proto ERROR - TemplateAssertionError exception: " + str(e)
|
|
254
|
+
except jinja2.TemplateSyntaxError as e:
|
|
255
|
+
response.error = "Embedded Proto ERROR - TemplateSyntaxError exception: " + str(e)
|
|
256
|
+
except jinja2.TemplateError as e:
|
|
257
|
+
response.error = "Embedded Proto ERROR - TemplateError exception: " + str(e)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
response.error = "Embedded Proto ERROR - " + describe_exception(e)
|
|
260
|
+
|
|
261
|
+
# Serialize response message
|
|
262
|
+
output = response.SerializeToString()
|
|
263
|
+
|
|
264
|
+
# Write to stdout
|
|
265
|
+
io.open(sys.stdout.fileno(), "wb").write(output)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# -----------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
def main_cli():
|
|
271
|
+
# The main function when running from the command line and debugging. Instead of receiving data from protoc this
|
|
272
|
+
# will read in a binary file stored the previous time main_plugin() is ran.
|
|
273
|
+
|
|
274
|
+
with open("debug_embedded_proto.bin", 'rb') as file:
|
|
275
|
+
# Create the response object
|
|
276
|
+
response = plugin.CodeGeneratorResponse()
|
|
277
|
+
configure_response_features(response)
|
|
278
|
+
|
|
279
|
+
data = file.read()
|
|
280
|
+
request = plugin.CodeGeneratorRequest.FromString(data)
|
|
281
|
+
|
|
282
|
+
# Generate code
|
|
283
|
+
try:
|
|
284
|
+
generate_code(request, response)
|
|
285
|
+
except jinja2.UndefinedError as e:
|
|
286
|
+
response.error = "Embedded Proto ERROR - Template Undefined Error exception: " + str(e)
|
|
287
|
+
except jinja2.TemplateRuntimeError as e:
|
|
288
|
+
response.error = "Embedded Proto ERROR - Template Runtime Error exception: " + str(e)
|
|
289
|
+
except jinja2.TemplateAssertionError as e:
|
|
290
|
+
response.error = "Embedded Proto ERROR - TemplateAssertionError exception: " + str(e)
|
|
291
|
+
except jinja2.TemplateSyntaxError as e:
|
|
292
|
+
response.error = "Embedded Proto ERROR - TemplateSyntaxError exception: " + str(e)
|
|
293
|
+
except jinja2.TemplateError as e:
|
|
294
|
+
response.error = "Embedded Proto ERROR - TemplateError exception: " + str(e)
|
|
295
|
+
except Exception as e:
|
|
296
|
+
response.error = "Embedded Proto ERROR - " + describe_exception(e)
|
|
297
|
+
|
|
298
|
+
# For debugging purposes print the result to the console.
|
|
299
|
+
for response_file in response.file:
|
|
300
|
+
print(response_file.name)
|
|
301
|
+
print(response_file.content)
|
|
302
|
+
|
|
303
|
+
if response.error:
|
|
304
|
+
print(response.error)
|
|
305
|
+
|
|
306
|
+
# -----------------------------------------------------------------------------
|
|
307
|
+
def main():
|
|
308
|
+
# Check if we are running as a plugin under protoc
|
|
309
|
+
if '--protoc-plugin' in sys.argv:
|
|
310
|
+
main_plugin()
|
|
311
|
+
else:
|
|
312
|
+
main_cli()
|
|
313
|
+
|
|
314
|
+
# -----------------------------------------------------------------------------
|
|
315
|
+
if __name__ == "__main__":
|
|
316
|
+
main()
|