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.
Files changed (63) hide show
  1. EmbeddedProto/EmbeddedProto.py +122 -0
  2. EmbeddedProto/Features.py +255 -0
  3. EmbeddedProto/Field.py +1295 -0
  4. EmbeddedProto/Oneof.py +74 -0
  5. EmbeddedProto/ProtoFile.py +210 -0
  6. EmbeddedProto/TypeDefinitions.py +367 -0
  7. EmbeddedProto/__init__.py +0 -0
  8. EmbeddedProto/__main__.py +28 -0
  9. EmbeddedProto/config.py +173 -0
  10. EmbeddedProto/custom_header.py +513 -0
  11. EmbeddedProto/embedded_proto_options.proto +62 -0
  12. EmbeddedProto/embedded_proto_options_pb2.py +37 -0
  13. EmbeddedProto/field_options.py +344 -0
  14. EmbeddedProto/main.py +316 -0
  15. EmbeddedProto/src/EmbeddedProto/BytesStringCallback.h +476 -0
  16. EmbeddedProto/src/EmbeddedProto/Defines.h +118 -0
  17. EmbeddedProto/src/EmbeddedProto/EmptyArray.h +81 -0
  18. EmbeddedProto/src/EmbeddedProto/Errors.h +53 -0
  19. EmbeddedProto/src/EmbeddedProto/FieldStringBytes.h +793 -0
  20. EmbeddedProto/src/EmbeddedProto/Fields.h +832 -0
  21. EmbeddedProto/src/EmbeddedProto/Functional.h +412 -0
  22. EmbeddedProto/src/EmbeddedProto/MessageCallback.h +688 -0
  23. EmbeddedProto/src/EmbeddedProto/MessageInterface.h +792 -0
  24. EmbeddedProto/src/EmbeddedProto/MessageSizeCalculator.h +109 -0
  25. EmbeddedProto/src/EmbeddedProto/MessageState.h +207 -0
  26. EmbeddedProto/src/EmbeddedProto/ReadBufferFixedSize.h +204 -0
  27. EmbeddedProto/src/EmbeddedProto/ReadBufferInterface.h +181 -0
  28. EmbeddedProto/src/EmbeddedProto/ReadBufferSection.h +232 -0
  29. EmbeddedProto/src/EmbeddedProto/RepeatedField.h +1053 -0
  30. EmbeddedProto/src/EmbeddedProto/RepeatedFieldCallback.h +432 -0
  31. EmbeddedProto/src/EmbeddedProto/RepeatedFieldFixedSize.h +367 -0
  32. EmbeddedProto/src/EmbeddedProto/Version.h +39 -0
  33. EmbeddedProto/src/EmbeddedProto/WireFormatter.h +852 -0
  34. EmbeddedProto/src/EmbeddedProto/WriteBufferFixedSize.h +114 -0
  35. EmbeddedProto/src/EmbeddedProto/WriteBufferInterface.h +134 -0
  36. EmbeddedProto/src/EmbeddedProto.h +55 -0
  37. EmbeddedProto/templates/FieldBasic_Deserialize.h.jinja2 +26 -0
  38. EmbeddedProto/templates/FieldBasic_GetSet.h.jinja2 +89 -0
  39. EmbeddedProto/templates/FieldBytes_GetSet.h.jinja2 +86 -0
  40. EmbeddedProto/templates/FieldEnum_Deserialize.h.jinja2 +50 -0
  41. EmbeddedProto/templates/FieldEnum_GetSet.h.jinja2 +93 -0
  42. EmbeddedProto/templates/FieldErrorRecursive_GetSet.h.jinja2 +29 -0
  43. EmbeddedProto/templates/FieldMap_GetSet.h.jinja2 +203 -0
  44. EmbeddedProto/templates/FieldMsg_Deserialize.h.jinja2 +44 -0
  45. EmbeddedProto/templates/FieldMsg_GetSet.h.jinja2 +93 -0
  46. EmbeddedProto/templates/FieldRepeated_GetSet.h.jinja2 +94 -0
  47. EmbeddedProto/templates/FieldString_GetSet.h.jinja2 +86 -0
  48. EmbeddedProto/templates/Field_DeserializePartial.h.jinja2 +33 -0
  49. EmbeddedProto/templates/Field_Serialize.h.jinja2 +112 -0
  50. EmbeddedProto/templates/Field_SerializePartial.h.jinja2 +119 -0
  51. EmbeddedProto/templates/Header.h.jinja2 +108 -0
  52. EmbeddedProto/templates/TypeDefEnum.h.jinja2 +45 -0
  53. EmbeddedProto/templates/TypeDefMsg.h.jinja2 +791 -0
  54. EmbeddedProto/templates/TypeOneof.h.jinja2 +178 -0
  55. EmbeddedProto/version.json +3 -0
  56. embeddedproto-4.0.0b1.dist-info/METADATA +141 -0
  57. embeddedproto-4.0.0b1.dist-info/RECORD +63 -0
  58. embeddedproto-4.0.0b1.dist-info/WHEEL +5 -0
  59. embeddedproto-4.0.0b1.dist-info/entry_points.txt +3 -0
  60. embeddedproto-4.0.0b1.dist-info/licenses/LICENSE +30 -0
  61. embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/GPL-3.0-only.txt +674 -0
  62. embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/LicenseRef-EmbeddedProto-Commercial.txt +12 -0
  63. 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()