plexus-msgs-python 1.0.4__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 (45) hide show
  1. plexus/msgs/__init__.py +6 -0
  2. plexus/msgs/__main__.py +40 -0
  3. plexus/msgs/control/plexus_msgs_control_control_command_pb2.py +39 -0
  4. plexus/msgs/control/plexus_msgs_control_control_command_pb2.pyi +33 -0
  5. plexus/msgs/control/plexus_msgs_control_dbw_command_pb2.py +41 -0
  6. plexus/msgs/control/plexus_msgs_control_dbw_command_pb2.pyi +42 -0
  7. plexus/msgs/descriptor.pb +0 -0
  8. plexus/msgs/localization/plexus_msgs_localization_odometry_pb2.py +38 -0
  9. plexus/msgs/localization/plexus_msgs_localization_odometry_pb2.pyi +15 -0
  10. plexus/msgs/localization/plexus_msgs_localization_positioning_pb2.py +39 -0
  11. plexus/msgs/localization/plexus_msgs_localization_positioning_pb2.pyi +18 -0
  12. plexus/msgs/mapping/plexus_msgs_mapping_map_region_pb2.py +54 -0
  13. plexus/msgs/mapping/plexus_msgs_mapping_map_region_pb2.pyi +104 -0
  14. plexus/msgs/perception/plexus_msgs_perception_lane_detection_pb2.py +53 -0
  15. plexus/msgs/perception/plexus_msgs_perception_lane_detection_pb2.pyi +97 -0
  16. plexus/msgs/perception/plexus_msgs_perception_obstacle_detection_pb2.py +51 -0
  17. plexus/msgs/perception/plexus_msgs_perception_obstacle_detection_pb2.pyi +82 -0
  18. plexus/msgs/planning/plexus_msgs_planning_driving_trajectory_pb2.py +40 -0
  19. plexus/msgs/planning/plexus_msgs_planning_driving_trajectory_pb2.pyi +18 -0
  20. plexus/msgs/planning/plexus_msgs_planning_obstacle_threat_pb2.py +41 -0
  21. plexus/msgs/planning/plexus_msgs_planning_obstacle_threat_pb2.pyi +61 -0
  22. plexus/msgs/plexus_common_carto_pb2.py +40 -0
  23. plexus/msgs/plexus_common_carto_pb2.pyi +26 -0
  24. plexus/msgs/plexus_common_entity_pb2.py +47 -0
  25. plexus/msgs/plexus_common_entity_pb2.pyi +96 -0
  26. plexus/msgs/plexus_common_geom_pb2.py +64 -0
  27. plexus/msgs/plexus_common_geom_pb2.pyi +120 -0
  28. plexus/msgs/plexus_common_message_pb2.py +40 -0
  29. plexus/msgs/plexus_common_message_pb2.pyi +29 -0
  30. plexus/msgs/plexus_common_pb2.py +63 -0
  31. plexus/msgs/plexus_common_pb2.pyi +171 -0
  32. plexus/msgs/plexus_pb2.py +38 -0
  33. plexus/msgs/plexus_pb2.pyi +27 -0
  34. plexus/msgs/routing/plexus_msgs_routing_navigation_info_pb2.py +52 -0
  35. plexus/msgs/routing/plexus_msgs_routing_navigation_info_pb2.pyi +117 -0
  36. plexus/msgs/testing/plexus_msgs_testing_dummy_pb2.py +141 -0
  37. plexus/msgs/testing/plexus_msgs_testing_dummy_pb2.pyi +276 -0
  38. plexus/msgs/utils.py +530 -0
  39. plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages_pb2.py +39 -0
  40. plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages_pb2.pyi +20 -0
  41. plexus_msgs_python-1.0.4.dist-info/METADATA +20 -0
  42. plexus_msgs_python-1.0.4.dist-info/RECORD +45 -0
  43. plexus_msgs_python-1.0.4.dist-info/WHEEL +5 -0
  44. plexus_msgs_python-1.0.4.dist-info/entry_points.txt +2 -0
  45. plexus_msgs_python-1.0.4.dist-info/top_level.txt +1 -0
plexus/msgs/utils.py ADDED
@@ -0,0 +1,530 @@
1
+ import base64
2
+ import copy
3
+ import importlib
4
+ import importlib.resources
5
+ import pkgutil
6
+ import textwrap
7
+ from collections.abc import Callable
8
+ from types import ModuleType
9
+ from typing import Any, TypeVar
10
+
11
+ import jinja2
12
+ from google.protobuf import descriptor_pool, message_factory
13
+ from google.protobuf.descriptor_pb2 import (
14
+ DescriptorProto as MessageDesc,
15
+ FieldDescriptorProto as FieldDesc,
16
+ FileDescriptorProto as FileDesc,
17
+ FileDescriptorSet as FileDescSet,
18
+ )
19
+ from google.protobuf.message import Message
20
+ from iker.common.utils.funcutils import memorized, singleton
21
+
22
+ from plexus.msgs.plexus_common_message_pb2 import (
23
+ Message as PlexusMessageProto,
24
+ MessageHeader as PlexusMessageHeaderProto,
25
+ )
26
+
27
+ __all__ = [
28
+ "import_plexus_msgs",
29
+ "get_protobuf_class",
30
+ "wrap_plexus_message",
31
+ "unwrap_plexus_message",
32
+ "import_submodules",
33
+ "traverse_descriptor_file",
34
+ "plexus_msgs_omitted_fields",
35
+ "plexus_msgs_composite_fields",
36
+ "generate_plexus_msg_tailor_python_code",
37
+ "generate_plexus_msg_tailor_java_code",
38
+ "generate_plexus_msg_tailor_cpp_code",
39
+ "tailor_plexus_msg",
40
+ "tailor_plexus_msg_2",
41
+ "copy_plexus_message",
42
+ "copy_plexus_message_2",
43
+ ]
44
+
45
+ MessageT = TypeVar("MessageT", bound=Message)
46
+
47
+
48
+ @singleton
49
+ def import_plexus_msgs():
50
+ """Imports all modules in the ``plexus.msgs`` package"""
51
+ import plexus.msgs
52
+ return import_submodules(plexus.msgs)
53
+
54
+
55
+ @singleton
56
+ def plexus_msgs_descriptor_path() -> str:
57
+ """Returns the path to the plexus messages descriptor file"""
58
+ import plexus.msgs
59
+ return str(importlib.resources.files(plexus.msgs).joinpath("descriptor.pb"))
60
+
61
+
62
+ @memorized
63
+ def get_protobuf_class(name: str) -> Callable[..., MessageT]:
64
+ """Returns the protobuf class for a given name"""
65
+ import_plexus_msgs()
66
+
67
+ pool = descriptor_pool.Default()
68
+ desc = pool.FindMessageTypeByName(name)
69
+ return message_factory.GetMessageClass(desc)
70
+
71
+
72
+ def wrap_plexus_message(header: PlexusMessageHeaderProto, payload: MessageT) -> bytes:
73
+ """Wraps a plexus message into a protobuf encoded string"""
74
+ if header.payload_type != payload.DESCRIPTOR.full_name:
75
+ raise ValueError(
76
+ f"header payload type '{header.payload_type}' does not match payload type '{payload.DESCRIPTOR.full_name}'"
77
+ )
78
+
79
+ plexus_message_pb = PlexusMessageProto()
80
+ plexus_message_pb.header.CopyFrom(header)
81
+ plexus_message_pb.payload = base64.b64encode(payload.SerializeToString())
82
+
83
+ return plexus_message_pb.SerializeToString()
84
+
85
+
86
+ def unwrap_plexus_message(data: bytes) -> tuple[PlexusMessageHeaderProto, MessageT]:
87
+ """Unwraps a plexus message from a protobuf encoded string"""
88
+ plexus_message_pb = PlexusMessageProto()
89
+ plexus_message_pb.ParseFromString(data)
90
+
91
+ payload_class = get_protobuf_class(plexus_message_pb.header.payload_type)
92
+ payload_pb = payload_class()
93
+ payload_pb.ParseFromString(base64.b64decode(plexus_message_pb.payload))
94
+
95
+ return plexus_message_pb.header, payload_pb
96
+
97
+
98
+ def import_submodules(package: str | ModuleType) -> dict[str, ModuleType]:
99
+ """Imports all submodules of a package, recursively"""
100
+ if isinstance(package, str):
101
+ return import_submodules(importlib.import_module(package))
102
+ results = {}
103
+ for loader, name, is_pkg in pkgutil.walk_packages(package.__path__, package.__name__ + "."):
104
+ results[name] = importlib.import_module(name)
105
+ return results
106
+
107
+
108
+ def traverse_descriptor_file(
109
+ descriptor_path: str,
110
+ message_ops: Callable[[str, FileDesc, MessageDesc], ...],
111
+ field_ops: Callable[[str, FileDesc, MessageDesc, FieldDesc], ...],
112
+ ):
113
+ """
114
+ Traverses a protobuf descriptor file and applies operations on messages and fields.
115
+
116
+ :param descriptor_path: Path to the protobuf descriptor file.
117
+ :param message_ops: Callable that takes a qualified name, a file descriptor, and a message descriptor.
118
+ :param field_ops: Callable that takes a qualified name, a file descriptor, a message descriptor, and a field
119
+ descriptor.
120
+ """
121
+
122
+ def traverse(file_desc: FileDesc, message_desc: MessageDesc, parent_qualified_name: str):
123
+ qualified_name = parent_qualified_name + "." + message_desc.name if parent_qualified_name else message_desc.name
124
+
125
+ for field_desc in message_desc.field:
126
+ field_ops(qualified_name, file_desc, message_desc, field_desc)
127
+ message_ops(qualified_name, file_desc, message_desc)
128
+
129
+ for nested_message_desc in message_desc.nested_type:
130
+ traverse(file_desc, nested_message_desc, qualified_name)
131
+
132
+ with open(descriptor_path, "rb") as fh:
133
+ file_desc_set = FileDescSet()
134
+ file_desc_set.ParseFromString(fh.read())
135
+
136
+ for file_desc in file_desc_set.file:
137
+ for message_desc in file_desc.message_type:
138
+ traverse(file_desc, message_desc, "." + file_desc.package)
139
+
140
+
141
+ def plexus_msgs_omitted_fields(
142
+ descriptor_path: str = plexus_msgs_descriptor_path(),
143
+ ) -> dict[str, tuple[FileDesc, MessageDesc, dict[str, FieldDesc]]]:
144
+ class OptionCollector(object):
145
+ def __init__(self, option_name: str, option_value_check: Callable[[Any], bool]):
146
+ self.option_name = option_name
147
+ self.option_value_check = option_value_check
148
+ self.collected_fields: list[FieldDesc] = []
149
+ self.collected_messages: list[tuple[str, FileDesc, MessageDesc, list[FieldDesc]]] = []
150
+
151
+ def message_ops(self, qualified_name: str, file_desc: FileDesc, message_desc: MessageDesc):
152
+ if self.collected_fields:
153
+ self.collected_messages.append((qualified_name, file_desc, message_desc, self.collected_fields))
154
+ self.collected_fields = []
155
+
156
+ def field_ops(
157
+ self,
158
+ qualified_name: str,
159
+ file_desc: FileDesc,
160
+ message_desc: MessageDesc,
161
+ field_desc: FieldDesc,
162
+ ):
163
+ for option, option_value in field_desc.options.ListFields():
164
+ if getattr(option, "name", None) == self.option_name and self.option_value_check(option_value):
165
+ self.collected_fields.append(field_desc)
166
+ return
167
+
168
+ collector = OptionCollector("omitted", lambda x: x is True)
169
+
170
+ traverse_descriptor_file(descriptor_path, collector.message_ops, collector.field_ops)
171
+
172
+ return {qualified_name: (file_desc, message_desc, {field_desc.name: field_desc for field_desc in field_desc_list})
173
+ for qualified_name, file_desc, message_desc, field_desc_list in collector.collected_messages}
174
+
175
+
176
+ def plexus_msgs_composite_fields(
177
+ descriptor_path: str = plexus_msgs_descriptor_path(),
178
+ ) -> dict[str, tuple[FileDesc, MessageDesc, dict[str, FieldDesc]]]:
179
+ class MessageTypeTreeCollector(object):
180
+ def __init__(self):
181
+ self.collected_fields: list[FieldDesc] = []
182
+ self.collected_messages: list[tuple[str, FileDesc, MessageDesc, list[FieldDesc]]] = []
183
+
184
+ def message_ops(self, qualified_name: str, file_desc: FileDesc, message_desc: MessageDesc):
185
+ if self.collected_fields:
186
+ self.collected_messages.append((qualified_name, file_desc, message_desc, self.collected_fields))
187
+ self.collected_fields = []
188
+
189
+ def field_ops(
190
+ self,
191
+ qualified_name: str,
192
+ file_desc: FileDesc,
193
+ message_desc: MessageDesc,
194
+ field_desc: FieldDesc,
195
+ ):
196
+ if field_desc.type != FieldDesc.TYPE_MESSAGE:
197
+ return
198
+ self.collected_fields.append(field_desc)
199
+
200
+ collector = MessageTypeTreeCollector()
201
+
202
+ traverse_descriptor_file(descriptor_path, collector.message_ops, collector.field_ops)
203
+
204
+ return {qualified_name: (file_desc, message_desc, {field_desc.name: field_desc for field_desc in field_desc_list})
205
+ for qualified_name, file_desc, message_desc, field_desc_list in collector.collected_messages}
206
+
207
+
208
+ def collect_plexus_msg_tailor_specs(
209
+ descriptor_path: str = plexus_msgs_descriptor_path(),
210
+ ) -> list[tuple[str, FileDesc, MessageDesc, list[FieldDesc], list[FieldDesc]]]:
211
+ omitted_fields = plexus_msgs_omitted_fields(descriptor_path)
212
+ composite_fields = plexus_msgs_composite_fields(descriptor_path)
213
+
214
+ def detect_omission(qualified_name: str, visited: set[str] = None) -> bool:
215
+ visited = visited or set()
216
+ if qualified_name in visited:
217
+ return False
218
+ if qualified_name in omitted_fields:
219
+ return True
220
+
221
+ if qualified_name in composite_fields:
222
+ _, _, field_desc_map = composite_fields[qualified_name]
223
+ return any(detect_omission(field_desc.type_name, visited | {qualified_name})
224
+ for field_desc in field_desc_map.values())
225
+ return False
226
+
227
+ specs = []
228
+ for qualified_name, (file_desc, message_desc, field_desc_map) in composite_fields.items():
229
+ if not detect_omission(qualified_name):
230
+ continue
231
+ _, _, omitted_field_desc_map = omitted_fields.get(qualified_name, (None, None, {}))
232
+ composite_field_desc_list = []
233
+ for field_desc in field_desc_map.values():
234
+ if not detect_omission(field_desc.type_name) or field_desc.name in omitted_field_desc_map:
235
+ continue
236
+ composite_field_desc_list.append(field_desc)
237
+ specs.append(
238
+ (
239
+ qualified_name,
240
+ file_desc,
241
+ message_desc,
242
+ list(omitted_field_desc_map.values()),
243
+ composite_field_desc_list,
244
+ )
245
+ )
246
+
247
+ return specs
248
+
249
+
250
+ def generate_plexus_msg_tailor_python_code(
251
+ descriptor_path: str = plexus_msgs_descriptor_path(),
252
+ *,
253
+ tailor_function_name: str = "tailor_plexus_msg"
254
+ ) -> str:
255
+ tailors = [
256
+ {
257
+ "qualified_name": qualified_name,
258
+ "tailor_function_name": "tailor" + "_".join(qualified_name.split(".")),
259
+ "omitted_fields": [field_desc.name for field_desc in omitted_field_desc_list],
260
+ "composite_fields": [
261
+ {
262
+ "name": field_desc.name,
263
+ "repeated": field_desc.label == FieldDesc.LABEL_REPEATED,
264
+ }
265
+ for field_desc in composite_field_desc_list],
266
+ }
267
+ for qualified_name, _, _, omitted_field_desc_list, composite_field_desc_list
268
+ in collect_plexus_msg_tailor_specs(descriptor_path)
269
+ ]
270
+
271
+ template_str = textwrap.dedent(
272
+ """
273
+ # AUTO-GENERATED FILE. DO NOT EDIT.
274
+ import copy
275
+
276
+ from google.protobuf.message import Message
277
+
278
+ {% for tailor in tailors %}
279
+
280
+ def {{ tailor.tailor_function_name }}(message: Message):
281
+ {% for field_name in tailor.omitted_fields %}
282
+ message.ClearField("{{ field_name }}")
283
+ {% endfor %}
284
+ {% for field in tailor.composite_fields %}
285
+ {% if field.repeated %}
286
+ for item in message.{{ field.name }}:
287
+ {{ settings.tailor_function_name }}(item)
288
+ {% else %}
289
+ if message.HasField("{{ field.name }}"):
290
+ {{ settings.tailor_function_name }}(message.{{ field.name }})
291
+ {% endif %}
292
+ {% endfor %}
293
+ return message
294
+
295
+ {% endfor %}
296
+
297
+ PLEXUS_MSG_TAILORS_REGISTRY = {
298
+ {% for tailor in tailors %}
299
+ "{{ tailor.qualified_name }}": {{ tailor.tailor_function_name }},
300
+ {% endfor %}
301
+ }
302
+
303
+
304
+ def {{ settings.tailor_function_name }}(message: Message):
305
+ qualified_name = "." + message.DESCRIPTOR.full_name
306
+ if qualified_name in PLEXUS_MSG_TAILORS_REGISTRY:
307
+ return PLEXUS_MSG_TAILORS_REGISTRY[qualified_name](message)
308
+ return message
309
+ """
310
+ )
311
+
312
+ env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
313
+
314
+ template = env.from_string(template_str)
315
+ rendered = template.render(tailors=tailors,
316
+ settings=dict(tailor_function_name=tailor_function_name))
317
+
318
+ return rendered
319
+
320
+
321
+ def generate_plexus_msg_tailor_java_code(
322
+ descriptor_path: str = plexus_msgs_descriptor_path(),
323
+ *,
324
+ tailor_package_name: str = "plexus.msgs",
325
+ tailor_class_name: str = "PlexusMsgTailor",
326
+ tailer_method_name: str = "tailor",
327
+ ) -> str:
328
+ def snake_case_to_camel_case(s: str) -> str:
329
+ return "".join(word.capitalize() for word in s.split("_"))
330
+
331
+ tailors = [
332
+ {
333
+ "qualified_name": qualified_name,
334
+ "message_class_name": qualified_name[1:],
335
+ "omitted_fields": [field_desc.name for field_desc in omitted_field_desc_list],
336
+ "composite_fields": [
337
+ {
338
+ "name": field_desc.name,
339
+ "repeated": field_desc.label == FieldDesc.LABEL_REPEATED,
340
+ }
341
+ for field_desc in composite_field_desc_list],
342
+ }
343
+ for qualified_name, file_desc, _, omitted_field_desc_list, composite_field_desc_list
344
+ in collect_plexus_msg_tailor_specs(descriptor_path)
345
+ ]
346
+
347
+ template_str = textwrap.dedent(
348
+ """
349
+ // AUTO-GENERATED FILE. DO NOT EDIT.
350
+ package {{ settings.tailor_package_name }};
351
+
352
+ public class {{ settings.tailor_class_name }} {
353
+ {% for tailor in tailors %}
354
+
355
+ public static {{ tailor.message_class_name }} tailorMessage({{ tailor.message_class_name }} message) {
356
+ var builder = message.toBuilder();
357
+ {% for field_name in tailor.omitted_fields %}
358
+ builder.clear{{ field_name|snake_case_to_camel_case }}();
359
+ {% endfor %}
360
+ {% for field in tailor.composite_fields %}
361
+ {% if field.repeated %}
362
+ for (int i = 0; i < builder.get{{ field.name|snake_case_to_camel_case }}Count(); i++) {
363
+ builder.set{{ field.name|snake_case_to_camel_case }}(
364
+ i, tailorMessage(builder.get{{ field.name|snake_case_to_camel_case }}(i)));
365
+ }
366
+ {% else %}
367
+ if (builder.has{{ field.name|snake_case_to_camel_case }}()) {
368
+ builder.set{{ field.name|snake_case_to_camel_case }}(
369
+ tailorMessage(message.get{{ field.name|snake_case_to_camel_case }}()));
370
+ }
371
+ {% endif %}
372
+ {% endfor %}
373
+ return builder.build();
374
+ }
375
+
376
+ {% endfor %}
377
+
378
+ public static <T extends com.google.protobuf.Message> T {{ settings.tailer_method_name}}(T message) {
379
+ {% for tailor in tailors %}
380
+ if (message instanceof {{ tailor.message_class_name }})
381
+ return (T) tailorMessage(({{ tailor.message_class_name }}) message);
382
+ {% endfor %}
383
+ return message;
384
+ }
385
+ }
386
+ """
387
+ )
388
+
389
+ env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
390
+ env.filters["snake_case_to_camel_case"] = snake_case_to_camel_case
391
+
392
+ template = env.from_string(template_str)
393
+ rendered = template.render(tailors=tailors,
394
+ settings=dict(tailor_package_name=tailor_package_name,
395
+ tailor_class_name=tailor_class_name,
396
+ tailer_method_name=tailer_method_name))
397
+
398
+ return rendered
399
+
400
+
401
+ def generate_plexus_msg_tailor_cpp_code(
402
+ descriptor_path: str = plexus_msgs_descriptor_path(),
403
+ *,
404
+ tailor_namespace: str = "plexus::msgs",
405
+ tailor_class_name: str = "PlexusMsgTailor",
406
+ tailor_method_name: str = "Tailor",
407
+ ) -> str:
408
+ def snake_case_to_pascal_case(s: str) -> str:
409
+ return "".join(word.capitalize() for word in s.split("_"))
410
+
411
+ tailors = [
412
+ {
413
+ "qualified_name": qualified_name,
414
+ "message_class_name": qualified_name.replace(".", "::"),
415
+ "omitted_fields": [field_desc.name for field_desc in omitted_field_desc_list],
416
+ "composite_fields": [
417
+ {
418
+ "name": field_desc.name,
419
+ "repeated": field_desc.label == FieldDesc.LABEL_REPEATED,
420
+ }
421
+ for field_desc in composite_field_desc_list],
422
+ }
423
+ for qualified_name, file_desc, _, omitted_field_desc_list, composite_field_desc_list
424
+ in collect_plexus_msg_tailor_specs(descriptor_path)
425
+ ]
426
+
427
+ template_str = textwrap.dedent(
428
+ """
429
+ // AUTO-GENERATED FILE. DO NOT EDIT.
430
+ #pragma once
431
+
432
+
433
+ namespace {{ settings.tailor_namespace }} {
434
+
435
+
436
+ class {{ settings.tailor_class_name }} {
437
+ public:
438
+ {% for tailor in tailors %}
439
+
440
+ static void TailorMessage({{ tailor.message_class_name }} &message) {
441
+ {% for field_name in tailor.omitted_fields %}
442
+ message.clear_{{ field_name }}();
443
+ {% endfor %}
444
+ {% for field in tailor.composite_fields %}
445
+ {% if field.repeated %}
446
+ for (int i = 0; i < message.mutable_{{ field.name }}()->size(); ++i) {
447
+ {{ settings.tailor_class_name }}::TailorMessage(message.mutable_{{ field.name }}()->at(i));
448
+ }
449
+ {% else %}
450
+ if (message.has_{{ field.name }}()) {
451
+ {{ settings.tailor_class_name }}::TailorMessage(*message.mutable_{{ field.name }}());
452
+ }
453
+ {% endif %}
454
+ {% endfor %}
455
+ }
456
+
457
+ {% endfor %}
458
+
459
+ template <typename T>
460
+ static auto {{ settings.tailor_method_name }}(const T &message) {
461
+ return message;
462
+ }
463
+ };
464
+
465
+ {% for tailor in tailors %}
466
+
467
+ template <>
468
+ auto {{ settings.tailor_class_name }}::{{ settings.tailor_method_name }}(const {{ tailor.message_class_name }} &message) {
469
+ auto copy = message;
470
+ {{ settings.tailor_class_name }}::TailorMessage(copy);
471
+ return copy;
472
+ }
473
+
474
+ {% endfor %}
475
+
476
+ } // namespace {{ settings.tailor_namespace }}
477
+ """
478
+ )
479
+
480
+ env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
481
+ env.filters["snake_case_to_pascal_case"] = snake_case_to_pascal_case
482
+
483
+ template = env.from_string(template_str)
484
+ rendered = template.render(tailors=tailors,
485
+ settings=dict(tailor_namespace=tailor_namespace,
486
+ tailor_class_name=tailor_class_name,
487
+ tailor_method_name=tailor_method_name))
488
+
489
+ return rendered
490
+
491
+
492
+ def make_tailor_plexus_msg():
493
+ import_plexus_msgs()
494
+
495
+ codegen_module = ModuleType("codegen")
496
+ exec(generate_plexus_msg_tailor_python_code(), codegen_module.__dict__)
497
+
498
+ return codegen_module.tailor_plexus_msg
499
+
500
+
501
+ tailor_plexus_msg = make_tailor_plexus_msg()
502
+
503
+
504
+ def tailor_plexus_msg_2(message: MessageT):
505
+ qualified_name = "." + message.DESCRIPTOR.full_name
506
+ _, _, omitted_field_desc_map = plexus_msgs_omitted_fields().get(qualified_name, (None, None, {}))
507
+ if not omitted_field_desc_map:
508
+ return message
509
+
510
+ for field_desc in message.DESCRIPTOR.fields:
511
+ if field_desc.name in omitted_field_desc_map:
512
+ message.ClearField(field_desc.name)
513
+ elif field_desc.type == FieldDesc.TYPE_MESSAGE:
514
+ value = getattr(message, field_desc.name)
515
+ if field_desc.label == FieldDesc.LABEL_REPEATED:
516
+ for item in value:
517
+ tailor_plexus_msg(item)
518
+ elif value is not None and value.ByteSize():
519
+ tailor_plexus_msg(value)
520
+ return message
521
+
522
+
523
+ def copy_plexus_message(message: MessageT, *, tailored: bool = False):
524
+ message_copy = copy.deepcopy(message)
525
+ return tailor_plexus_msg(message_copy) if tailored else message_copy
526
+
527
+
528
+ def copy_plexus_message_2(message: MessageT, *, tailored: bool = False) -> MessageT:
529
+ message_copy = copy.deepcopy(message)
530
+ return tailor_plexus_msg_2(message_copy) if tailored else message_copy
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages.proto\x12\x13plexus.msgs.vehicle\"z\n\x0b\x43\x61nMessages\x12:\n\x08messages\x18\x01 \x03(\x0b\x32(.plexus.msgs.vehicle.CanMessages.Message\x1a/\n\x07Message\x12\x0f\n\x02id\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\rB\x05\n\x03_idB\x02P\x01\x62\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'plexus.msgs.vehicle.plexus_msgs_vehicle_can_messages_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ _globals['DESCRIPTOR']._loaded_options = None
34
+ _globals['DESCRIPTOR']._serialized_options = b'P\001'
35
+ _globals['_CANMESSAGES']._serialized_start=83
36
+ _globals['_CANMESSAGES']._serialized_end=205
37
+ _globals['_CANMESSAGES_MESSAGE']._serialized_start=158
38
+ _globals['_CANMESSAGES_MESSAGE']._serialized_end=205
39
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,20 @@
1
+ from google.protobuf.internal import containers as _containers
2
+ from google.protobuf import descriptor as _descriptor
3
+ from google.protobuf import message as _message
4
+ from collections.abc import Iterable as _Iterable, Mapping as _Mapping
5
+ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
6
+
7
+ DESCRIPTOR: _descriptor.FileDescriptor
8
+
9
+ class CanMessages(_message.Message):
10
+ __slots__ = ("messages",)
11
+ class Message(_message.Message):
12
+ __slots__ = ("id", "data")
13
+ ID_FIELD_NUMBER: _ClassVar[int]
14
+ DATA_FIELD_NUMBER: _ClassVar[int]
15
+ id: int
16
+ data: _containers.RepeatedScalarFieldContainer[int]
17
+ def __init__(self, id: _Optional[int] = ..., data: _Optional[_Iterable[int]] = ...) -> None: ...
18
+ MESSAGES_FIELD_NUMBER: _ClassVar[int]
19
+ messages: _containers.RepeatedCompositeFieldContainer[CanMessages.Message]
20
+ def __init__(self, messages: _Optional[_Iterable[_Union[CanMessages.Message, _Mapping]]] = ...) -> None: ...
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: plexus-msgs-python
3
+ Version: 1.0.4
4
+ Summary: Python APIs and utilities for Plexus ProtoBuf messages
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: Python :: 3.11
7
+ Classifier: Programming Language :: Python :: 3.12
8
+ Classifier: Programming Language :: Python :: 3.13
9
+ Requires-Python: <3.14,>=3.11
10
+ Requires-Dist: jinja2>=3.0
11
+ Requires-Dist: protobuf>=5.0
12
+ Requires-Dist: iker-python-common>=1.0
13
+ Provides-Extra: all
14
+ Requires-Dist: plexus-msgs-python; extra == "all"
15
+ Provides-Extra: test
16
+ Requires-Dist: ddt>=1.7; extra == "test"
17
+ Requires-Dist: pytest-cov>=5.0; extra == "test"
18
+ Requires-Dist: pytest-order>=1.3; extra == "test"
19
+ Requires-Dist: pytest>=8.3; extra == "test"
20
+ Requires-Dist: iker-python-common>=1.0; extra == "test"
@@ -0,0 +1,45 @@
1
+ plexus/msgs/__init__.py,sha256=KSeCSv7WNBxHUsiW-n53CW1ZV7Xy5zomUECCs8m34gY,175
2
+ plexus/msgs/__main__.py,sha256=fyaPPvfB8Oi-Ih0MfX3xt6Tvc3CmNYi7lqy0Xxip01M,1520
3
+ plexus/msgs/descriptor.pb,sha256=0o7Ukil4ir1-KhsEA00UM3c9vEZkm0dJELY9mMB9Iws,144504
4
+ plexus/msgs/plexus_common_carto_pb2.py,sha256=XqqXceWk83171vr4uATmJam2gnwr05VNHhrHSwTSZy8,1994
5
+ plexus/msgs/plexus_common_carto_pb2.pyi,sha256=CKmVNQZuEbdoOFrh58P_BnjMr-Z3T2BKzvSOOlsx-AA,1260
6
+ plexus/msgs/plexus_common_entity_pb2.py,sha256=n2pN2tsDA7rTFB_f7FjED_K1Wk0VN1b2vGzis9-Pqfw,4020
7
+ plexus/msgs/plexus_common_entity_pb2.pyi,sha256=xOOf7WeEbbOTa-xHsEuR-h8mUTWrSaMXkzCAjz-0kME,5513
8
+ plexus/msgs/plexus_common_geom_pb2.py,sha256=HfXZ0_35udZtGXrPzVBk9O8vtTgtN9lomOmWy7sUuQo,5630
9
+ plexus/msgs/plexus_common_geom_pb2.pyi,sha256=tqtV83UHJAu5tzHMF9V7bkh-dpxz8iPeJK0ty1pPB-0,5957
10
+ plexus/msgs/plexus_common_message_pb2.py,sha256=iLbMEUh7QOOOW6XWY3XcjKcxEgZp-Kj7HwavVcSMz9E,2248
11
+ plexus/msgs/plexus_common_message_pb2.pyi,sha256=KrzJ7Mhst37q3lHlWc1Y09APWyTLPixeKRHzoTE8vnM,1377
12
+ plexus/msgs/plexus_common_pb2.py,sha256=yjj8iRNW9VRyRYshcQE2oZzc6YqPrHnkF-Cz0QhxH3o,6397
13
+ plexus/msgs/plexus_common_pb2.pyi,sha256=CW_kDDLX-ZJvvm2GXwNbQtwozhyHh2XWaBMz0c8VN_U,7570
14
+ plexus/msgs/plexus_pb2.py,sha256=V7IiyXRr3V-hx-QXcJHuib9qCCq3Tek24EyawkH4mU8,1967
15
+ plexus/msgs/plexus_pb2.pyi,sha256=xjEufjB6d1xzxkh8BTQXS7zrI047bwdVqqq_jzZMaU0,1098
16
+ plexus/msgs/utils.py,sha256=ZZFuwOIs92TiaeX83nmtjxZZ5lZDCUCvyiPvUfHMInk,20045
17
+ plexus/msgs/control/plexus_msgs_control_control_command_pb2.py,sha256=-_osho0zib5RjkT0j7BdKWMI91fhpHo_odXAiZXLeww,2733
18
+ plexus/msgs/control/plexus_msgs_control_control_command_pb2.pyi,sha256=YkJpNxRQszGzln56Zi0b0IGMMnWJQ6Ce8XH-Yqe_-TA,1993
19
+ plexus/msgs/control/plexus_msgs_control_dbw_command_pb2.py,sha256=u_qxJX3-TDETn5WjBOBC6zofIxcpGfNJm6LGjoAuuro,2861
20
+ plexus/msgs/control/plexus_msgs_control_dbw_command_pb2.pyi,sha256=JBEHEVGYwAPTs99BoErwxQOdOaFItYEe_7q1CchOqww,2306
21
+ plexus/msgs/localization/plexus_msgs_localization_odometry_pb2.py,sha256=Y10YlLeao_r0h3PrSNZ6uriC9FXZ8ROic5QY-pUMVeA,1846
22
+ plexus/msgs/localization/plexus_msgs_localization_odometry_pb2.pyi,sha256=lH_adAUMwoiMLSvWpevs_peU_kqxIuF849uJIakGRhg,751
23
+ plexus/msgs/localization/plexus_msgs_localization_positioning_pb2.py,sha256=M5JAIhGI1Tz3mv5Mg9n6w_-e2LoUkdZ-CHxv7DxKm_I,2130
24
+ plexus/msgs/localization/plexus_msgs_localization_positioning_pb2.pyi,sha256=_gYhMZ20ACOtAQ2CMrjK27mLFa-J9mlj1r3BmnFvxrk,1048
25
+ plexus/msgs/mapping/plexus_msgs_mapping_map_region_pb2.py,sha256=q3O3r5zzCeFStE57GYdjpNf7yXTNCFvCJPeh_ZhjnX8,6309
26
+ plexus/msgs/mapping/plexus_msgs_mapping_map_region_pb2.pyi,sha256=baADpOiWNjDGNL1JVOStXRXWJU3wickfq7O11i11Ct8,7007
27
+ plexus/msgs/perception/plexus_msgs_perception_lane_detection_pb2.py,sha256=0rgyKblnYukkTKzEUKkM8t1Dr1QJzOXYJueZPbwkYDc,6684
28
+ plexus/msgs/perception/plexus_msgs_perception_lane_detection_pb2.pyi,sha256=oX68nke32CKLqnv9peCgfCUAINmfSJDpYfhJJo_nuUw,8486
29
+ plexus/msgs/perception/plexus_msgs_perception_obstacle_detection_pb2.py,sha256=UnZvszkDqW1IGkhYiqo4zlMJRhT4j7poJYVFclIgd4A,6044
30
+ plexus/msgs/perception/plexus_msgs_perception_obstacle_detection_pb2.pyi,sha256=mH-nvuGDHNtPgUXG1LqQxKYgybMPs-gVJp2_6p1Jb6k,7072
31
+ plexus/msgs/planning/plexus_msgs_planning_driving_trajectory_pb2.py,sha256=UlAptS5auZHs1qDz7WbBb7B2s3q0UmmdiCM5EiK0hAg,2101
32
+ plexus/msgs/planning/plexus_msgs_planning_driving_trajectory_pb2.pyi,sha256=Fs5Mz1XAPBgNTZQm3pKfagnAD-jgEm8euR70N0RNRTE,937
33
+ plexus/msgs/planning/plexus_msgs_planning_obstacle_threat_pb2.py,sha256=jR2aJfb2kt0Puw3a0FFm5PhgM0DrfQsWDUodXsoimHg,3237
34
+ plexus/msgs/planning/plexus_msgs_planning_obstacle_threat_pb2.pyi,sha256=UL3RKWbtJFzMEx0Qohmzk6K9lpIPDa-XeR4CiJSHXtI,3844
35
+ plexus/msgs/routing/plexus_msgs_routing_navigation_info_pb2.py,sha256=3lATV0oqWS_vYatBjrgoWDoqFDMcwYPurPiXdZWJaQI,6177
36
+ plexus/msgs/routing/plexus_msgs_routing_navigation_info_pb2.pyi,sha256=z4aOMYCzFUD15_KGxgB5A6nZjggz18G_pyZmHtnzB1c,8892
37
+ plexus/msgs/testing/plexus_msgs_testing_dummy_pb2.py,sha256=x121-jzpntmeaBcHT_p_eOpH2Gin9iu_Gkg2svKXWOI,24230
38
+ plexus/msgs/testing/plexus_msgs_testing_dummy_pb2.pyi,sha256=1k8ygoalUTdRVaazdUB6NvQFrqR94n8FCUHhLsL47Mk,23796
39
+ plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages_pb2.py,sha256=qTRo-uO9pSxOfuVKz8yzfXM5RMSGI5Q7X1d-OO4ezjo,1823
40
+ plexus/msgs/vehicle/plexus_msgs_vehicle_can_messages_pb2.pyi,sha256=syQUL-FMatbe9l6IGDw4cTJ7H3T_e8NJoZiwqSZzjpY,994
41
+ plexus_msgs_python-1.0.4.dist-info/METADATA,sha256=Fr4uTE8mtZse4j3op0F1dVmnAS8ZodlV6hmIV5sfqy0,782
42
+ plexus_msgs_python-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
+ plexus_msgs_python-1.0.4.dist-info/entry_points.txt,sha256=FHsZqentRWi8W_GpY6vJnSN-9Rmriolti9nkVNnXW_Q,65
44
+ plexus_msgs_python-1.0.4.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
45
+ plexus_msgs_python-1.0.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ plexus-msgs-python = plexus.msgs.__main__:main
@@ -0,0 +1 @@
1
+ plexus