barectf: fix Flake8 errors
[deliverable/barectf.git] / barectf / config_parse_v2.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2015-2020 Philippe Proulx <pproulx@efficios.com>
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 from barectf.config_parse_common import _ConfigurationParseError
25 from barectf.config_parse_common import _append_error_ctx
26 import barectf.config_parse_common as config_parse_common
27 from barectf.config_parse_common import _MapNode
28 import collections
29 import copy
30 from barectf.typing import VersionNumber, _OptStr
31 from typing import Optional, List, Dict, TextIO, Union, Callable
32 import typing
33
34
35 def _del_prop_if_exists(node: _MapNode, prop_name: str):
36 if prop_name in node:
37 del node[prop_name]
38
39
40 def _rename_prop(node: _MapNode, old_prop_name: str, new_prop_name: str):
41 if old_prop_name in node:
42 node[new_prop_name] = node[old_prop_name]
43 del node[old_prop_name]
44
45
46 def _copy_prop_if_exists(dst_node: _MapNode, src_node: _MapNode, src_prop_name: str,
47 dst_prop_name: _OptStr = None):
48 if dst_prop_name is None:
49 dst_prop_name = src_prop_name
50
51 if src_prop_name in src_node:
52 dst_node[dst_prop_name] = copy.deepcopy(src_node[src_prop_name])
53
54
55 # A barectf 2 YAML configuration parser.
56 #
57 # The only purpose of such a parser is to transform the passed root
58 # configuration node so that it's a valid barectf 3 configuration node.
59 #
60 # The parser's `config_node` property is the equivalent barectf 3
61 # configuration node.
62 #
63 # See the comments of _parse() for more implementation details about the
64 # parsing stages and general strategy.
65 class _Parser(config_parse_common._Parser):
66 # Builds a barectf 2 YAML configuration parser and parses the root
67 # configuration node `node` (already loaded from the file-like
68 # object `root_file`).
69 def __init__(self, root_file: TextIO, node: _MapNode, with_pkg_include_dir: bool,
70 include_dirs: Optional[List[str]], ignore_include_not_found: bool):
71 super().__init__(root_file, node, with_pkg_include_dir, include_dirs,
72 ignore_include_not_found, VersionNumber(2))
73 self._ft_cls_name_to_conv_method: Dict[str, Callable[[_MapNode], _MapNode]] = {
74 'int': self._conv_int_ft_node,
75 'integer': self._conv_int_ft_node,
76 'enum': self._conv_enum_ft_node,
77 'enumeration': self._conv_enum_ft_node,
78 'flt': self._conv_real_ft_node,
79 'float': self._conv_real_ft_node,
80 'floating-point': self._conv_real_ft_node,
81 'str': self._conv_string_ft_node,
82 'string': self._conv_string_ft_node,
83 'array': self._conv_static_array_ft_node,
84 'struct': self._conv_struct_ft_node,
85 'structure': self._conv_struct_ft_node,
86 }
87 self._parse()
88
89 # Converts a v2 field type node to a v3 field type node and returns
90 # it.
91 def _conv_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
92 assert 'class' in v2_ft_node
93 cls = v2_ft_node['class']
94 assert cls in self._ft_cls_name_to_conv_method
95 return self._ft_cls_name_to_conv_method[cls](v2_ft_node)
96
97 def _conv_ft_node_if_exists(self, v2_parent_node: Optional[_MapNode], key: str) -> Optional[_MapNode]:
98 if v2_parent_node is None:
99 return None
100
101 if key not in v2_parent_node:
102 return None
103
104 return self._conv_ft_node(v2_parent_node[key])
105
106 # Converts a v2 integer field type node to a v3 integer field type
107 # node and returns it.
108 def _conv_int_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
109 # copy v2 integer field type node
110 v3_ft_node = copy.deepcopy(v2_ft_node)
111
112 # signedness depends on the class, not a property
113 cls_name = 'uint'
114 prop_name = 'signed'
115 is_signed_node = v3_ft_node.get(prop_name)
116
117 if is_signed_node is True:
118 cls_name = 'sint'
119
120 v3_ft_node['class'] = cls_name
121 _del_prop_if_exists(v3_ft_node, prop_name)
122
123 # rename `align` property to `alignment`
124 _rename_prop(v3_ft_node, 'align', 'alignment')
125
126 # rename `base` property to `preferred-display-base`
127 _rename_prop(v3_ft_node, 'base', 'preferred-display-base')
128
129 # remove `encoding` property
130 _del_prop_if_exists(v3_ft_node, 'encoding')
131
132 # remove `property-mappings` property
133 _del_prop_if_exists(v3_ft_node, 'property-mappings')
134
135 return v3_ft_node
136
137 # Converts a v2 enumeration field type node to a v3 enumeration
138 # field type node and returns it.
139 def _conv_enum_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
140 # An enumeration field type _is_ an integer field type, so use a
141 # copy of the converted v2 value field type node.
142 v3_ft_node = copy.deepcopy(self._conv_ft_node(v2_ft_node['value-type']))
143
144 # transform class name accordingly
145 prop_name = 'class'
146 cls_name = 'uenum'
147
148 if v3_ft_node[prop_name] == 'sint':
149 cls_name = 'senum'
150
151 v3_ft_node[prop_name] = cls_name
152
153 # convert members to mappings
154 prop_name = 'members'
155 members_node = v2_ft_node.get(prop_name)
156
157 if members_node is not None:
158 mappings_node: _MapNode = collections.OrderedDict()
159 cur = 0
160
161 for member_node in members_node:
162 v3_value_node: Union[int, List[int]]
163
164 if type(member_node) is str:
165 label = member_node
166 v3_value_node = cur
167 cur += 1
168 else:
169 assert type(member_node) is collections.OrderedDict
170 label = member_node['label']
171 v2_value_node = member_node['value']
172
173 if type(v2_value_node) is int:
174 cur = v2_value_node + 1
175 v3_value_node = v2_value_node
176 else:
177 assert type(v2_value_node) is list
178 assert len(v2_value_node) == 2
179 v3_value_node = list(v2_value_node)
180 cur = v2_value_node[1] + 1
181
182 if label not in mappings_node:
183 mappings_node[label] = []
184
185 mappings_node[label].append(v3_value_node)
186
187 v3_ft_node['mappings'] = mappings_node
188
189 return v3_ft_node
190
191 # Converts a v2 real field type node to a v3 real field type node
192 # and returns it.
193 def _conv_real_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
194 # copy v2 real field type node
195 v3_ft_node = copy.deepcopy(v2_ft_node)
196
197 # set class to `real`
198 v3_ft_node['class'] = 'real'
199
200 # rename `align` property to `alignment`
201 _rename_prop(v3_ft_node, 'align', 'alignment')
202
203 # set `size` property to a single integer (total size, in bits)
204 prop_name = 'size'
205 v3_ft_node[prop_name] = v3_ft_node[prop_name]['exp'] + v3_ft_node[prop_name]['mant']
206
207 return v3_ft_node
208
209 # Converts a v2 string field type node to a v3 string field type
210 # node and returns it.
211 def _conv_string_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
212 # copy v2 string field type node
213 v3_ft_node = copy.deepcopy(v2_ft_node)
214
215 # remove `encoding` property
216 _del_prop_if_exists(v3_ft_node, 'encoding')
217
218 return v3_ft_node
219
220 # Converts a v2 array field type node to a v3 (static) array field
221 # type node and returns it.
222 def _conv_static_array_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
223 # class renamed to `static-array`
224 v3_ft_node: _MapNode = collections.OrderedDict({'class': 'static-array'})
225
226 # copy `length` property
227 _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'length')
228
229 # convert element field type
230 v3_ft_node['element-field-type'] = self._conv_ft_node(v2_ft_node['element-type'])
231
232 return v3_ft_node
233
234 # Converts a v2 structure field type node to a v3 structure field
235 # type node and returns it.
236 def _conv_struct_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
237 # Create fresh v3 structure field type node, reusing the class
238 # of `v2_ft_node`.
239 v3_ft_node = collections.OrderedDict({'class': v2_ft_node['class']})
240
241 # rename `min-align` property to `minimum-alignment`
242 _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'min-align', 'minimum-alignment')
243
244 # convert fields to members
245 prop_name = 'fields'
246
247 if prop_name in v2_ft_node:
248 members_node = []
249
250 for member_name, v2_member_ft_node in v2_ft_node[prop_name].items():
251 members_node.append(collections.OrderedDict({
252 member_name: collections.OrderedDict({
253 'field-type': self._conv_ft_node(v2_member_ft_node)
254 })
255 }))
256
257 v3_ft_node['members'] = members_node
258
259 return v3_ft_node
260
261 # Converts a v2 clock type node to a v3 clock type node and returns
262 # it.
263 def _conv_clk_type_node(self, v2_clk_type_node: _MapNode) -> _MapNode:
264 # copy v2 clock type node
265 v3_clk_type_node = copy.deepcopy(v2_clk_type_node)
266
267 # rename `freq` property to `frequency`
268 _rename_prop(v3_clk_type_node, 'freq', 'frequency')
269
270 # rename `error-cycles` property to `precision`
271 _rename_prop(v3_clk_type_node, 'error-cycles', 'precision')
272
273 # rename `absolute` property to `origin-is-unix-epoch`
274 _rename_prop(v3_clk_type_node, 'absolute', 'origin-is-unix-epoch')
275
276 # rename `$return-ctype`/`return-ctype` property to `$c-type`
277 new_prop_name = '$c-type'
278 _rename_prop(v3_clk_type_node, 'return-ctype', new_prop_name)
279 _rename_prop(v3_clk_type_node, '$return-ctype', new_prop_name)
280
281 return v3_clk_type_node
282
283 # Converts a v2 event type node to a v3 event type node and returns
284 # it.
285 def _conv_ev_type_node(self, v2_ev_type_node: _MapNode) -> _MapNode:
286 # create empty v3 event type node
287 v3_ev_type_node: _MapNode = collections.OrderedDict()
288
289 # copy `log-level` property
290 _copy_prop_if_exists(v3_ev_type_node, v2_ev_type_node, 'log-level')
291
292 # convert specific context field type node
293 v2_ft_node = v2_ev_type_node.get('context-type')
294
295 if v2_ft_node is not None:
296 v3_ev_type_node['specific-context-field-type'] = self._conv_ft_node(v2_ft_node)
297
298 # convert payload field type node
299 v2_ft_node = v2_ev_type_node.get('payload-type')
300
301 if v2_ft_node is not None:
302 v3_ev_type_node['payload-field-type'] = self._conv_ft_node(v2_ft_node)
303
304 return v3_ev_type_node
305
306 @staticmethod
307 def _set_v3_feature_ft_if_exists(v3_features_node: _MapNode, key: str,
308 node: Union[Optional[_MapNode], bool]):
309 val = node
310
311 if val is None:
312 val = False
313
314 v3_features_node[key] = val
315
316 # Converts a v2 stream type node to a v3 stream type node and
317 # returns it.
318 def _conv_stream_type_node(self, v2_stream_type_node: _MapNode) -> _MapNode:
319 # This function creates a v3 stream type features node from the
320 # packet context and event header field type nodes of a
321 # v2 stream type node.
322 def v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node: _MapNode,
323 v2_ev_header_ft_fields_node: Optional[_MapNode]) -> _MapNode:
324 if v2_ev_header_ft_fields_node is None:
325 v2_ev_header_ft_fields_node = collections.OrderedDict()
326
327 v3_pkt_total_size_ft_node = self._conv_ft_node(v2_pkt_ctx_ft_fields_node['packet_size'])
328 v3_pkt_content_size_ft_node = self._conv_ft_node(v2_pkt_ctx_ft_fields_node['content_size'])
329 v3_pkt_beg_time_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
330 'timestamp_begin')
331 v3_pkt_end_time_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
332 'timestamp_end')
333 v3_pkt_disc_ev_counter_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
334 'events_discarded')
335 v3_ev_type_id_ft_node = self._conv_ft_node_if_exists(v2_ev_header_ft_fields_node, 'id')
336 v3_ev_time_ft_node = self._conv_ft_node_if_exists(v2_ev_header_ft_fields_node,
337 'timestamp')
338 v3_features_node: _MapNode = collections.OrderedDict()
339 v3_pkt_node: _MapNode = collections.OrderedDict()
340 v3_ev_node: _MapNode = collections.OrderedDict()
341 v3_pkt_node['total-size-field-type'] = v3_pkt_total_size_ft_node
342 v3_pkt_node['content-size-field-type'] = v3_pkt_content_size_ft_node
343 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'beginning-time-field-type',
344 v3_pkt_beg_time_ft_node)
345 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'end-time-field-type',
346 v3_pkt_end_time_ft_node)
347 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'discarded-events-counter-field-type',
348 v3_pkt_disc_ev_counter_ft_node)
349 self._set_v3_feature_ft_if_exists(v3_ev_node, 'type-id-field-type',
350 v3_ev_type_id_ft_node)
351 self._set_v3_feature_ft_if_exists(v3_ev_node, 'time-field-type', v3_ev_time_ft_node)
352 v3_features_node['packet'] = v3_pkt_node
353 v3_features_node['event'] = v3_ev_node
354 return v3_features_node
355
356 def clk_type_name_from_v2_int_ft_node(v2_int_ft_node: Optional[_MapNode]) -> _OptStr:
357 if v2_int_ft_node is None:
358 return None
359
360 assert v2_int_ft_node['class'] in ('int', 'integer')
361 prop_mappings_node = v2_int_ft_node.get('property-mappings')
362
363 if prop_mappings_node is not None and len(prop_mappings_node) > 0:
364 return prop_mappings_node[0]['name']
365
366 return None
367
368 # create empty v3 stream type node
369 v3_stream_type_node: _MapNode = collections.OrderedDict()
370
371 # rename `$default` property to `$is-default`
372 _copy_prop_if_exists(v3_stream_type_node, v2_stream_type_node, '$default', '$is-default')
373
374 # set default clock type node
375 pct_prop_name = 'packet-context-type'
376 v2_pkt_ctx_ft_fields_node = v2_stream_type_node[pct_prop_name]['fields']
377 eht_prop_name = 'event-header-type'
378 v2_ev_header_ft_fields_node = None
379 v2_ev_header_ft_node = v2_stream_type_node.get(eht_prop_name)
380
381 if v2_ev_header_ft_node is not None:
382 v2_ev_header_ft_fields_node = v2_ev_header_ft_node['fields']
383
384 def_clk_type_name = None
385
386 try:
387 ts_begin_prop_name = 'timestamp_begin'
388 ts_begin_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node.get(ts_begin_prop_name))
389 ts_end_prop_name = 'timestamp_end'
390 ts_end_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node.get(ts_end_prop_name))
391
392 if ts_begin_clk_type_name is not None and ts_end_clk_type_name is not None:
393 if ts_begin_clk_type_name != ts_end_clk_type_name:
394 raise _ConfigurationParseError(f'`{ts_begin_prop_name}`/`{ts_end_prop_name}` properties',
395 'Field types are not mapped to the same clock type')
396 except _ConfigurationParseError as exc:
397 _append_error_ctx(exc, f'`{pct_prop_name}` property')
398
399 try:
400 if def_clk_type_name is None and v2_ev_header_ft_fields_node is not None:
401 def_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_ev_header_ft_fields_node.get('timestamp'))
402
403 if def_clk_type_name is None and ts_begin_clk_type_name is not None:
404 def_clk_type_name = ts_begin_clk_type_name
405
406 if def_clk_type_name is None and ts_end_clk_type_name is not None:
407 def_clk_type_name = ts_end_clk_type_name
408 except _ConfigurationParseError as exc:
409 _append_error_ctx(exc, f'`{eht_prop_name}` property')
410
411 if def_clk_type_name is not None:
412 v3_stream_type_node['$default-clock-type-name'] = def_clk_type_name
413
414 # set features node
415 v3_stream_type_node['$features'] = v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node,
416 v2_ev_header_ft_fields_node)
417
418 # set extra packet context field type members node
419 pkt_ctx_ft_extra_members = []
420 ctf_member_names = [
421 'packet_size',
422 'content_size',
423 'timestamp_begin',
424 'timestamp_end',
425 'events_discarded',
426 'packet_seq_num',
427 ]
428
429 for member_name, v2_ft_node in v2_pkt_ctx_ft_fields_node.items():
430 if member_name in ctf_member_names:
431 continue
432
433 pkt_ctx_ft_extra_members.append(collections.OrderedDict({
434 member_name: collections.OrderedDict({
435 'field-type': self._conv_ft_node(v2_ft_node)
436 })
437 }))
438
439 if len(pkt_ctx_ft_extra_members) > 0:
440 v3_stream_type_node['packet-context-field-type-extra-members'] = pkt_ctx_ft_extra_members
441
442 # convert event common context field type node
443 v2_ft_node = v2_stream_type_node.get('event-context-type')
444
445 if v2_ft_node is not None:
446 v3_stream_type_node['event-common-context-field-type'] = self._conv_ft_node(v2_ft_node)
447
448 # convert event type nodes
449 v3_event_types_node = collections.OrderedDict()
450
451 for ev_type_name, v2_ev_type_node in v2_stream_type_node['events'].items():
452 try:
453 v3_event_types_node[ev_type_name] = self._conv_ev_type_node(v2_ev_type_node)
454 except _ConfigurationParseError as exc:
455 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
456
457 v3_stream_type_node['event-types'] = v3_event_types_node
458
459 return v3_stream_type_node
460
461 # Converts a v2 metadata node to a v3 trace node and returns it.
462 def _conv_meta_node(self, v2_meta_node: _MapNode) -> _MapNode:
463 def v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node: Optional[_MapNode]) -> _MapNode:
464 def set_if_exists(key, node):
465 return self._set_v3_feature_ft_if_exists(v3_features_node, key, node)
466
467 v2_pkt_header_ft_fields_node = collections.OrderedDict()
468
469 if v2_pkt_header_ft_node is not None:
470 v2_pkt_header_ft_fields_node = v2_pkt_header_ft_node['fields']
471
472 v3_magic_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node, 'magic')
473 v3_uuid_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node, 'uuid')
474 v3_stream_type_id_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node,
475 'stream_id')
476 v3_features_node: _MapNode = collections.OrderedDict()
477 set_if_exists('magic-field-type', v3_magic_ft_node)
478 set_if_exists('uuid-field-type', v3_uuid_ft_node)
479 set_if_exists('stream-type-id-field-type', v3_stream_type_id_ft_node)
480 return v3_features_node
481
482 v3_trace_node: _MapNode = collections.OrderedDict()
483 v3_trace_type_node: _MapNode = collections.OrderedDict()
484 v2_trace_node = v2_meta_node['trace']
485
486 # rename `byte-order` property to `$default-byte-order`
487 _copy_prop_if_exists(v3_trace_type_node, v2_trace_node, 'byte-order', '$default-byte-order')
488
489 # copy `uuid` property
490 _copy_prop_if_exists(v3_trace_type_node, v2_trace_node, 'uuid')
491
492 # copy `$log-levels`/`log-levels` property
493 new_prop_name = '$log-level-aliases'
494 _copy_prop_if_exists(v3_trace_type_node, v2_meta_node, 'log-levels', new_prop_name)
495 _copy_prop_if_exists(v3_trace_type_node, v2_meta_node, '$log-levels', new_prop_name)
496
497 # copy `clocks` property, converting clock type nodes
498 v2_clk_types_node = v2_meta_node.get('clocks')
499
500 if v2_clk_types_node is not None:
501 v3_clk_types_node = collections.OrderedDict()
502
503 for name, v2_clk_type_node in v2_clk_types_node.items():
504 v3_clk_types_node[name] = self._conv_clk_type_node(v2_clk_type_node)
505
506 v3_trace_type_node['clock-types'] = v3_clk_types_node
507
508 # set features node
509 v2_pkt_header_ft_node = v2_trace_node.get('packet-header-type')
510 v3_trace_type_node['$features'] = v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node)
511
512 # convert stream type nodes
513 v3_stream_types_node = collections.OrderedDict()
514
515 for stream_type_name, v2_stream_type_node in v2_meta_node['streams'].items():
516 try:
517 v3_stream_types_node[stream_type_name] = self._conv_stream_type_node(v2_stream_type_node)
518 except _ConfigurationParseError as exc:
519 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
520
521 v3_trace_type_node['stream-types'] = v3_stream_types_node
522
523 # If `v2_meta_node` has a `$default-stream` property, find the
524 # corresponding v3 stream type node and set its `$is-default`
525 # property to `True`.
526 prop_name = '$default-stream'
527 v2_def_stream_type_node = v2_meta_node.get(prop_name)
528
529 if v2_def_stream_type_node is not None:
530 found = False
531
532 for stream_type_name, v3_stream_type_node in v3_stream_types_node.items():
533 if stream_type_name == v2_def_stream_type_node:
534 v3_stream_type_node['$is-default'] = True
535 found = True
536 break
537
538 if not found:
539 raise _ConfigurationParseError(f'`{prop_name}` property',
540 f'Stream type `{v2_def_stream_type_node}` does not exist')
541
542 # set environment node
543 v2_env_node = v2_meta_node.get('env')
544
545 if v2_env_node is not None:
546 v3_trace_node['environment'] = copy.deepcopy(v2_env_node)
547
548 # set v3 trace node's type node
549 v3_trace_node['type'] = v3_trace_type_node
550
551 return v3_trace_node
552
553 # Transforms the root configuration node into a valid v3
554 # configuration node.
555 def _transform_config_node(self):
556 # remove the `version` property
557 del self._root_node['version']
558
559 # relocate prefix and option nodes
560 prefix_prop_name = 'prefix'
561 v2_prefix_node = self._root_node.get(prefix_prop_name, 'barectf_')
562 _del_prop_if_exists(self._root_node, prefix_prop_name)
563 opt_prop_name = 'options'
564 v2_options_node = self._root_node.get(opt_prop_name)
565 _del_prop_if_exists(self._root_node, opt_prop_name)
566 code_gen_node = collections.OrderedDict()
567 v3_prefixes = config_parse_common._v3_prefixes_from_v2_prefix(v2_prefix_node)
568 v3_prefix_node = collections.OrderedDict([
569 ('identifier', v3_prefixes.identifier),
570 ('file-name', v3_prefixes.file_name),
571 ])
572 code_gen_node[prefix_prop_name] = v3_prefix_node
573
574 if v2_options_node is not None:
575 header_node = collections.OrderedDict()
576 _copy_prop_if_exists(header_node, v2_options_node, 'gen-prefix-def',
577 'identifier-prefix-definition')
578 _copy_prop_if_exists(header_node, v2_options_node, 'gen-default-stream-def',
579 'default-stream-type-name-definition')
580 code_gen_node['header'] = header_node
581
582 self._root_node[opt_prop_name] = collections.OrderedDict({
583 'code-generation': code_gen_node,
584 })
585
586 # convert the v2 metadata node into a v3 trace node
587 try:
588 self._root_node['trace'] = self._conv_meta_node(self._root_node['metadata'])
589 except _ConfigurationParseError as exc:
590 _append_error_ctx(exc, 'Metadata object')
591
592 del self._root_node['metadata']
593
594 # Expands the field type aliases found in the metadata node.
595 #
596 # This method modifies the metadata node.
597 #
598 # When this method returns:
599 #
600 # * Any field type alias is replaced with its full field type node
601 # equivalent.
602 #
603 # * The `type-aliases` property of metadata node is removed.
604 def _expand_ft_aliases(self):
605 meta_node = self._root_node['metadata']
606 ft_aliases_node = meta_node['type-aliases']
607
608 # Expand field type aliases within trace, stream, and event
609 # types now.
610 try:
611 self._resolve_ft_alias_from(ft_aliases_node, meta_node['trace'], 'packet-header-type')
612 except _ConfigurationParseError as exc:
613 _append_error_ctx(exc, 'Trace type')
614
615 for stream_type_name, stream_type_node in meta_node['streams'].items():
616 try:
617 self._resolve_ft_alias_from(ft_aliases_node, stream_type_node,
618 'packet-context-type')
619 self._resolve_ft_alias_from(ft_aliases_node, stream_type_node, 'event-header-type')
620 self._resolve_ft_alias_from(ft_aliases_node, stream_type_node,
621 'event-context-type')
622
623 for ev_type_name, ev_type_node in stream_type_node['events'].items():
624 try:
625 self._resolve_ft_alias_from(ft_aliases_node, ev_type_node, 'context-type')
626 self._resolve_ft_alias_from(ft_aliases_node, ev_type_node, 'payload-type')
627 except _ConfigurationParseError as exc:
628 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
629 except _ConfigurationParseError as exc:
630 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
631
632 # remove the (now unneeded) `type-aliases` node
633 del meta_node['type-aliases']
634
635 # Applies field type inheritance to all field type nodes found in
636 # the metadata node.
637 #
638 # This method modifies the metadata node.
639 #
640 # When this method returns, no field type node has an `$inherit` or
641 # `inherit` property.
642 def _apply_fts_inheritance(self):
643 meta_node = self._root_node['metadata']
644 self._apply_ft_inheritance(meta_node['trace'], 'packet-header-type')
645
646 for stream_type_node in meta_node['streams'].values():
647 self._apply_ft_inheritance(stream_type_node, 'packet-context-type')
648 self._apply_ft_inheritance(stream_type_node, 'event-header-type')
649 self._apply_ft_inheritance(stream_type_node, 'event-context-type')
650
651 for ev_type_node in stream_type_node['events'].values():
652 self._apply_ft_inheritance(ev_type_node, 'context-type')
653 self._apply_ft_inheritance(ev_type_node, 'payload-type')
654
655 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
656 # metadata node has a `type-aliases` property.
657 def _expand_fts(self):
658 # Make sure that the current configuration node is valid
659 # considering field types are not expanded yet.
660 self._schema_validator.validate(self._root_node,
661 'config/2/config-pre-field-type-expansion')
662
663 meta_node = self._root_node['metadata']
664 ft_aliases_node = meta_node.get('type-aliases')
665
666 if ft_aliases_node is None:
667 # If there's no `type-aliases` node, then there's no field
668 # type aliases and therefore no possible inheritance.
669 return
670
671 # first, expand field type aliases
672 self._expand_ft_aliases()
673
674 # next, apply inheritance to create effective field types
675 self._apply_fts_inheritance()
676
677 # Processes the inclusions of the event type node `ev_type_node`,
678 # returning the effective node.
679 def _process_ev_type_node_include(self, ev_type_node: _MapNode) -> _MapNode:
680 # Make sure the event type node is valid for the inclusion
681 # processing stage.
682 self._schema_validator.validate(ev_type_node, 'config/2/event-type-pre-include')
683
684 # process inclusions
685 return self._process_node_include(ev_type_node, self._process_ev_type_node_include)
686
687 # Processes the inclusions of the stream type node
688 # `stream_type_node`, returning the effective node.
689 def _process_stream_type_node_include(self, stream_type_node: _MapNode) -> _MapNode:
690 def process_children_include(stream_type_node):
691 prop_name = 'events'
692
693 if prop_name in stream_type_node:
694 ev_types_node = stream_type_node[prop_name]
695
696 for key in list(ev_types_node):
697 ev_types_node[key] = self._process_ev_type_node_include(ev_types_node[key])
698
699 # Make sure the stream type node is valid for the inclusion
700 # processing stage.
701 self._schema_validator.validate(stream_type_node, 'config/2/stream-type-pre-include')
702
703 # process inclusions
704 return self._process_node_include(stream_type_node, self._process_stream_type_node_include,
705 process_children_include)
706
707 # Processes the inclusions of the trace type node `trace_type_node`,
708 # returning the effective node.
709 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
710 # Make sure the trace type node is valid for the inclusion
711 # processing stage.
712 self._schema_validator.validate(trace_type_node, 'config/2/trace-type-pre-include')
713
714 # process inclusions
715 return self._process_node_include(trace_type_node, self._process_trace_type_node_include)
716
717 # Processes the inclusions of the clock type node `clk_type_node`,
718 # returning the effective node.
719 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
720 # Make sure the clock type node is valid for the inclusion
721 # processing stage.
722 self._schema_validator.validate(clk_type_node, 'config/2/clock-type-pre-include')
723
724 # process inclusions
725 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
726
727 # Processes the inclusions of the metadata node `meta_node`,
728 # returning the effective node.
729 def _process_meta_node_include(self, meta_node: _MapNode) -> _MapNode:
730 def process_children_include(meta_node: _MapNode):
731 prop_name = 'trace'
732
733 if prop_name in meta_node:
734 meta_node[prop_name] = self._process_trace_type_node_include(meta_node[prop_name])
735
736 prop_name = 'clocks'
737
738 if prop_name in meta_node:
739 clk_types_node = meta_node[prop_name]
740
741 for key in list(clk_types_node):
742 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
743
744 prop_name = 'streams'
745
746 if prop_name in meta_node:
747 stream_types_node = meta_node[prop_name]
748
749 for key in list(stream_types_node):
750 stream_types_node[key] = self._process_stream_type_node_include(stream_types_node[key])
751
752 # Make sure the metadata node is valid for the inclusion
753 # processing stage.
754 self._schema_validator.validate(meta_node, 'config/2/metadata-pre-include')
755
756 # process inclusions
757 return self._process_node_include(meta_node, self._process_meta_node_include,
758 process_children_include)
759
760 # Processes the inclusions of the configuration node, modifying it
761 # during the process.
762 def _process_config_includes(self):
763 # Process inclusions in this order:
764 #
765 # 1. Clock type node, event type nodes, and trace type nodes
766 # (the order between those is not important).
767 #
768 # 2. Stream type nodes.
769 #
770 # 3. Metadata node.
771 #
772 # This is because:
773 #
774 # * A metadata node can include clock type nodes, a trace type
775 # node, stream type nodes, and event type nodes (indirectly).
776 #
777 # * A stream type node can include event type nodes.
778 #
779 # First, make sure the configuration node itself is valid for
780 # the inclusion processing stage.
781 self._schema_validator.validate(self._root_node,
782 'config/2/config-pre-include')
783
784 # Process metadata node inclusions.
785 #
786 # self._process_meta_node_include() returns a new (or the same)
787 # metadata node without any `$include` property in it,
788 # recursively.
789 prop_name = 'metadata'
790 self._root_node[prop_name] = self._process_meta_node_include(self._root_node[prop_name])
791
792 def _parse(self):
793 # Make sure the configuration node is minimally valid, that is,
794 # it contains a valid `version` property.
795 #
796 # This step does not validate the whole configuration node yet
797 # because we don't have an effective configuration node; we
798 # still need to:
799 #
800 # * Process inclusions.
801 # * Expand field types (aliases and inheritance).
802 self._schema_validator.validate(self._root_node, 'config/2/config-min')
803
804 # process configuration node inclusions
805 self._process_config_includes()
806
807 # Expand field type nodes.
808 #
809 # This process:
810 #
811 # 1. Replaces field type aliases with "effective" field type
812 # nodes, recursively.
813 #
814 # After this step, the `type-aliases` property of the
815 # metadata node is gone.
816 #
817 # 2. Applies inheritance, following the `$inherit`/`inherit`
818 # properties.
819 #
820 # After this step, field type nodes do not contain `$inherit`
821 # or `inherit` properties.
822 #
823 # This is done blindly, in that the process _doesn't_ validate
824 # field type nodes at this point.
825 #
826 # The reason we must do this here for a barectf 2 configuration,
827 # considering that barectf 3 also supports field type node
828 # aliases and inheritance, is that we need to find specific
829 # packet header and packet context field type member nodes (for
830 # example, `stream_id`, `packet_size`, or `timestamp_end`) to
831 # set the `$features` properties of barectf 3 trace type and
832 # stream type nodes. Those field type nodes can be aliases,
833 # contain aliases, or inherit from other nodes.
834 self._expand_fts()
835
836 # Validate the whole, (almost) effective configuration node.
837 #
838 # It's almost effective because the `log-level` property of
839 # event type nodes can be log level aliases. Log level aliases
840 # are also a feature of a barectf 3 configuration node,
841 # therefore this is compatible.
842 self._schema_validator.validate(self._root_node, 'config/2/config')
843
844 # Transform the current configuration node into a valid v3
845 # configuration node.
846 self._transform_config_node()
847
848 @property
849 def config_node(self) -> config_parse_common._ConfigNodeV3:
850 return config_parse_common._ConfigNodeV3(typing.cast(_MapNode, self._root_node))
This page took 0.083574 seconds and 4 git commands to generate.