docs: cleanup: Rephrase and correct typos
[barectf.git] / barectf / config_parse_v2.py
CommitLineData
4810b707
PP
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
24from barectf.config_parse_common import _ConfigurationParseError
25from barectf.config_parse_common import _append_error_ctx
26import barectf.config_parse_common as config_parse_common
2d55dc7d 27from barectf.config_parse_common import _MapNode
4810b707
PP
28import collections
29import copy
2d55dc7d
PP
30from barectf.typing import VersionNumber, _OptStr
31from typing import Optional, List, Dict, TextIO, Union, Callable
32import typing
4810b707
PP
33
34
2d55dc7d 35def _del_prop_if_exists(node: _MapNode, prop_name: str):
4810b707
PP
36 if prop_name in node:
37 del node[prop_name]
38
39
2d55dc7d 40def _rename_prop(node: _MapNode, old_prop_name: str, new_prop_name: str):
4810b707
PP
41 if old_prop_name in node:
42 node[new_prop_name] = node[old_prop_name]
43 del node[old_prop_name]
44
45
2d55dc7d
PP
46def _copy_prop_if_exists(dst_node: _MapNode, src_node: _MapNode, src_prop_name: str,
47 dst_prop_name: _OptStr = None):
4810b707
PP
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.
65class _Parser(config_parse_common._Parser):
66 # Builds a barectf 2 YAML configuration parser and parses the root
2d55dc7d
PP
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]] = {
4810b707
PP
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,
be9f12dc 83 'array': self._conv_array_ft_node,
4810b707
PP
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.
2d55dc7d 91 def _conv_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
4810b707
PP
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
2d55dc7d 97 def _conv_ft_node_if_exists(self, v2_parent_node: Optional[_MapNode], key: str) -> Optional[_MapNode]:
4810b707 98 if v2_parent_node is None:
2d55dc7d 99 return None
4810b707
PP
100
101 if key not in v2_parent_node:
2d55dc7d 102 return None
4810b707
PP
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.
2d55dc7d 108 def _conv_int_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
4810b707
PP
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
a209cf4d
EB
132 # remove `byte-order` property (the equivalent barectf 3
133 # configuration property is named `trace-byte-order`)
4c91e769
PP
134 _del_prop_if_exists(v3_ft_node, 'byte-order')
135
4810b707
PP
136 # remove `property-mappings` property
137 _del_prop_if_exists(v3_ft_node, 'property-mappings')
138
139 return v3_ft_node
140
141 # Converts a v2 enumeration field type node to a v3 enumeration
142 # field type node and returns it.
2d55dc7d 143 def _conv_enum_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
4810b707
PP
144 # An enumeration field type _is_ an integer field type, so use a
145 # copy of the converted v2 value field type node.
146 v3_ft_node = copy.deepcopy(self._conv_ft_node(v2_ft_node['value-type']))
147
148 # transform class name accordingly
149 prop_name = 'class'
150 cls_name = 'uenum'
151
152 if v3_ft_node[prop_name] == 'sint':
153 cls_name = 'senum'
154
155 v3_ft_node[prop_name] = cls_name
156
157 # convert members to mappings
158 prop_name = 'members'
159 members_node = v2_ft_node.get(prop_name)
160
161 if members_node is not None:
2d55dc7d 162 mappings_node: _MapNode = collections.OrderedDict()
4810b707
PP
163 cur = 0
164
165 for member_node in members_node:
2d55dc7d
PP
166 v3_value_node: Union[int, List[int]]
167
4810b707
PP
168 if type(member_node) is str:
169 label = member_node
170 v3_value_node = cur
171 cur += 1
172 else:
173 assert type(member_node) is collections.OrderedDict
174 label = member_node['label']
175 v2_value_node = member_node['value']
176
177 if type(v2_value_node) is int:
178 cur = v2_value_node + 1
179 v3_value_node = v2_value_node
180 else:
181 assert type(v2_value_node) is list
182 assert len(v2_value_node) == 2
183 v3_value_node = list(v2_value_node)
184 cur = v2_value_node[1] + 1
185
186 if label not in mappings_node:
187 mappings_node[label] = []
188
189 mappings_node[label].append(v3_value_node)
190
191 v3_ft_node['mappings'] = mappings_node
192
193 return v3_ft_node
194
195 # Converts a v2 real field type node to a v3 real field type node
196 # and returns it.
2d55dc7d 197 def _conv_real_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
4810b707
PP
198 # copy v2 real field type node
199 v3_ft_node = copy.deepcopy(v2_ft_node)
200
201 # set class to `real`
202 v3_ft_node['class'] = 'real'
203
204 # rename `align` property to `alignment`
205 _rename_prop(v3_ft_node, 'align', 'alignment')
206
207 # set `size` property to a single integer (total size, in bits)
208 prop_name = 'size'
209 v3_ft_node[prop_name] = v3_ft_node[prop_name]['exp'] + v3_ft_node[prop_name]['mant']
210
211 return v3_ft_node
212
213 # Converts a v2 string field type node to a v3 string field type
214 # node and returns it.
2d55dc7d 215 def _conv_string_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
4810b707
PP
216 # copy v2 string field type node
217 v3_ft_node = copy.deepcopy(v2_ft_node)
218
219 # remove `encoding` property
220 _del_prop_if_exists(v3_ft_node, 'encoding')
221
222 return v3_ft_node
223
224 # Converts a v2 array field type node to a v3 (static) array field
225 # type node and returns it.
be9f12dc
PP
226 def _conv_array_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
227 # class renamed to `static-array` or `dynamic-array`
228 is_dynamic = v2_ft_node['length'] == 'dynamic'
229 array_type = 'dynamic' if is_dynamic else 'static'
230 v3_ft_node: _MapNode = collections.OrderedDict({'class': f'{array_type}-array'})
231
232 # copy `length` property if it's a static array field type
233 if not is_dynamic:
234 _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'length')
4810b707
PP
235
236 # convert element field type
237 v3_ft_node['element-field-type'] = self._conv_ft_node(v2_ft_node['element-type'])
238
239 return v3_ft_node
240
241 # Converts a v2 structure field type node to a v3 structure field
242 # type node and returns it.
2d55dc7d 243 def _conv_struct_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
4810b707
PP
244 # Create fresh v3 structure field type node, reusing the class
245 # of `v2_ft_node`.
246 v3_ft_node = collections.OrderedDict({'class': v2_ft_node['class']})
247
248 # rename `min-align` property to `minimum-alignment`
249 _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'min-align', 'minimum-alignment')
250
251 # convert fields to members
252 prop_name = 'fields'
253
254 if prop_name in v2_ft_node:
255 members_node = []
256
257 for member_name, v2_member_ft_node in v2_ft_node[prop_name].items():
258 members_node.append(collections.OrderedDict({
259 member_name: collections.OrderedDict({
260 'field-type': self._conv_ft_node(v2_member_ft_node)
261 })
262 }))
263
264 v3_ft_node['members'] = members_node
265
266 return v3_ft_node
267
268 # Converts a v2 clock type node to a v3 clock type node and returns
269 # it.
2d55dc7d 270 def _conv_clk_type_node(self, v2_clk_type_node: _MapNode) -> _MapNode:
4810b707
PP
271 # copy v2 clock type node
272 v3_clk_type_node = copy.deepcopy(v2_clk_type_node)
273
274 # rename `freq` property to `frequency`
275 _rename_prop(v3_clk_type_node, 'freq', 'frequency')
276
277 # rename `error-cycles` property to `precision`
278 _rename_prop(v3_clk_type_node, 'error-cycles', 'precision')
279
280 # rename `absolute` property to `origin-is-unix-epoch`
281 _rename_prop(v3_clk_type_node, 'absolute', 'origin-is-unix-epoch')
282
283 # rename `$return-ctype`/`return-ctype` property to `$c-type`
284 new_prop_name = '$c-type'
285 _rename_prop(v3_clk_type_node, 'return-ctype', new_prop_name)
286 _rename_prop(v3_clk_type_node, '$return-ctype', new_prop_name)
287
288 return v3_clk_type_node
289
e8f0d548
PP
290 # Converts a v2 event record type node to a v3 event record type
291 # node and returns it.
292 def _conv_ert_node(self, v2_ert_node: _MapNode) -> _MapNode:
293 # create empty v3 event record type node
294 v3_ert_node: _MapNode = collections.OrderedDict()
4810b707
PP
295
296 # copy `log-level` property
e8f0d548 297 _copy_prop_if_exists(v3_ert_node, v2_ert_node, 'log-level')
4810b707
PP
298
299 # convert specific context field type node
e8f0d548 300 v2_ft_node = v2_ert_node.get('context-type')
4810b707
PP
301
302 if v2_ft_node is not None:
e8f0d548 303 v3_ert_node['specific-context-field-type'] = self._conv_ft_node(v2_ft_node)
4810b707
PP
304
305 # convert payload field type node
e8f0d548 306 v2_ft_node = v2_ert_node.get('payload-type')
4810b707
PP
307
308 if v2_ft_node is not None:
e8f0d548 309 v3_ert_node['payload-field-type'] = self._conv_ft_node(v2_ft_node)
4810b707 310
e8f0d548 311 return v3_ert_node
4810b707
PP
312
313 @staticmethod
2d55dc7d
PP
314 def _set_v3_feature_ft_if_exists(v3_features_node: _MapNode, key: str,
315 node: Union[Optional[_MapNode], bool]):
4810b707
PP
316 val = node
317
318 if val is None:
319 val = False
320
321 v3_features_node[key] = val
322
e8f0d548
PP
323 # Converts a v2 data stream type node to a v3 data stream type node
324 # and returns it.
325 def _conv_dst_node(self, v2_dst_node: _MapNode) -> _MapNode:
326 # This function creates a v3 data stream type features node from
327 # the packet context and event record header field type nodes of
328 # a v2 data stream type node.
2d55dc7d 329 def v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node: _MapNode,
e8f0d548
PP
330 v2_er_header_ft_fields_node: Optional[_MapNode]) -> _MapNode:
331 if v2_er_header_ft_fields_node is None:
332 v2_er_header_ft_fields_node = collections.OrderedDict()
4810b707
PP
333
334 v3_pkt_total_size_ft_node = self._conv_ft_node(v2_pkt_ctx_ft_fields_node['packet_size'])
335 v3_pkt_content_size_ft_node = self._conv_ft_node(v2_pkt_ctx_ft_fields_node['content_size'])
462e49b3
PP
336 v3_pkt_beg_ts_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
337 'timestamp_begin')
338 v3_pkt_end_ts_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
339 'timestamp_end')
e8f0d548
PP
340 v3_pkt_disc_er_counter_snap_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
341 'events_discarded')
342 v3_ert_id_ft_node = self._conv_ft_node_if_exists(v2_er_header_ft_fields_node, 'id')
462e49b3
PP
343 v3_er_ts_ft_node = self._conv_ft_node_if_exists(v2_er_header_ft_fields_node,
344 'timestamp')
2d55dc7d
PP
345 v3_features_node: _MapNode = collections.OrderedDict()
346 v3_pkt_node: _MapNode = collections.OrderedDict()
e8f0d548 347 v3_er_node: _MapNode = collections.OrderedDict()
4810b707
PP
348 v3_pkt_node['total-size-field-type'] = v3_pkt_total_size_ft_node
349 v3_pkt_node['content-size-field-type'] = v3_pkt_content_size_ft_node
462e49b3
PP
350 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'beginning-timestamp-field-type',
351 v3_pkt_beg_ts_ft_node)
352 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'end-timestamp-field-type',
353 v3_pkt_end_ts_ft_node)
e8f0d548
PP
354 self._set_v3_feature_ft_if_exists(v3_pkt_node,
355 'discarded-event-records-counter-snapshot-field-type',
356 v3_pkt_disc_er_counter_snap_ft_node)
357 self._set_v3_feature_ft_if_exists(v3_er_node, 'type-id-field-type', v3_ert_id_ft_node)
462e49b3 358 self._set_v3_feature_ft_if_exists(v3_er_node, 'timestamp-field-type', v3_er_ts_ft_node)
4810b707 359 v3_features_node['packet'] = v3_pkt_node
e8f0d548 360 v3_features_node['event-record'] = v3_er_node
4810b707
PP
361 return v3_features_node
362
2d55dc7d 363 def clk_type_name_from_v2_int_ft_node(v2_int_ft_node: Optional[_MapNode]) -> _OptStr:
4810b707 364 if v2_int_ft_node is None:
2d55dc7d 365 return None
4810b707
PP
366
367 assert v2_int_ft_node['class'] in ('int', 'integer')
368 prop_mappings_node = v2_int_ft_node.get('property-mappings')
369
370 if prop_mappings_node is not None and len(prop_mappings_node) > 0:
371 return prop_mappings_node[0]['name']
372
2d55dc7d
PP
373 return None
374
e8f0d548
PP
375 # create empty v3 data stream type node
376 v3_dst_node: _MapNode = collections.OrderedDict()
4810b707
PP
377
378 # rename `$default` property to `$is-default`
e8f0d548 379 _copy_prop_if_exists(v3_dst_node, v2_dst_node, '$default', '$is-default')
4810b707
PP
380
381 # set default clock type node
382 pct_prop_name = 'packet-context-type'
e8f0d548 383 v2_pkt_ctx_ft_fields_node = v2_dst_node[pct_prop_name]['fields']
4810b707 384 eht_prop_name = 'event-header-type'
e8f0d548
PP
385 v2_er_header_ft_fields_node = None
386 v2_er_header_ft_node = v2_dst_node.get(eht_prop_name)
4810b707 387
e8f0d548
PP
388 if v2_er_header_ft_node is not None:
389 v2_er_header_ft_fields_node = v2_er_header_ft_node['fields']
4810b707
PP
390
391 def_clk_type_name = None
392
393 try:
394 ts_begin_prop_name = 'timestamp_begin'
395 ts_begin_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node.get(ts_begin_prop_name))
396 ts_end_prop_name = 'timestamp_end'
397 ts_end_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node.get(ts_end_prop_name))
398
399 if ts_begin_clk_type_name is not None and ts_end_clk_type_name is not None:
400 if ts_begin_clk_type_name != ts_end_clk_type_name:
401 raise _ConfigurationParseError(f'`{ts_begin_prop_name}`/`{ts_end_prop_name}` properties',
402 'Field types are not mapped to the same clock type')
403 except _ConfigurationParseError as exc:
404 _append_error_ctx(exc, f'`{pct_prop_name}` property')
405
406 try:
e8f0d548
PP
407 if def_clk_type_name is None and v2_er_header_ft_fields_node is not None:
408 def_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_er_header_ft_fields_node.get('timestamp'))
4810b707
PP
409
410 if def_clk_type_name is None and ts_begin_clk_type_name is not None:
411 def_clk_type_name = ts_begin_clk_type_name
412
413 if def_clk_type_name is None and ts_end_clk_type_name is not None:
414 def_clk_type_name = ts_end_clk_type_name
415 except _ConfigurationParseError as exc:
416 _append_error_ctx(exc, f'`{eht_prop_name}` property')
417
418 if def_clk_type_name is not None:
e8f0d548 419 v3_dst_node['$default-clock-type-name'] = def_clk_type_name
4810b707
PP
420
421 # set features node
e8f0d548
PP
422 v3_dst_node['$features'] = v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node,
423 v2_er_header_ft_fields_node)
4810b707
PP
424
425 # set extra packet context field type members node
426 pkt_ctx_ft_extra_members = []
427 ctf_member_names = [
428 'packet_size',
429 'content_size',
430 'timestamp_begin',
431 'timestamp_end',
432 'events_discarded',
433 'packet_seq_num',
434 ]
435
436 for member_name, v2_ft_node in v2_pkt_ctx_ft_fields_node.items():
437 if member_name in ctf_member_names:
438 continue
439
440 pkt_ctx_ft_extra_members.append(collections.OrderedDict({
441 member_name: collections.OrderedDict({
442 'field-type': self._conv_ft_node(v2_ft_node)
443 })
444 }))
445
446 if len(pkt_ctx_ft_extra_members) > 0:
e8f0d548 447 v3_dst_node['packet-context-field-type-extra-members'] = pkt_ctx_ft_extra_members
4810b707 448
e8f0d548
PP
449 # convert event record common context field type node
450 v2_ft_node = v2_dst_node.get('event-context-type')
4810b707
PP
451
452 if v2_ft_node is not None:
e8f0d548 453 v3_dst_node['event-record-common-context-field-type'] = self._conv_ft_node(v2_ft_node)
4810b707 454
e8f0d548
PP
455 # convert event record type nodes
456 v3_erts_node = collections.OrderedDict()
4810b707 457
e8f0d548 458 for ert_name, v2_ert_node in v2_dst_node['events'].items():
4810b707 459 try:
e8f0d548 460 v3_erts_node[ert_name] = self._conv_ert_node(v2_ert_node)
4810b707 461 except _ConfigurationParseError as exc:
e8f0d548 462 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 463
e8f0d548 464 v3_dst_node['event-record-types'] = v3_erts_node
4810b707 465
e8f0d548 466 return v3_dst_node
4810b707
PP
467
468 # Converts a v2 metadata node to a v3 trace node and returns it.
2d55dc7d
PP
469 def _conv_meta_node(self, v2_meta_node: _MapNode) -> _MapNode:
470 def v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node: Optional[_MapNode]) -> _MapNode:
4810b707
PP
471 def set_if_exists(key, node):
472 return self._set_v3_feature_ft_if_exists(v3_features_node, key, node)
473
474 v2_pkt_header_ft_fields_node = collections.OrderedDict()
475
476 if v2_pkt_header_ft_node is not None:
477 v2_pkt_header_ft_fields_node = v2_pkt_header_ft_node['fields']
478
479 v3_magic_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node, 'magic')
480 v3_uuid_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node, 'uuid')
e8f0d548
PP
481 v3_dst_id_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node,
482 'stream_id')
2d55dc7d 483 v3_features_node: _MapNode = collections.OrderedDict()
4810b707
PP
484 set_if_exists('magic-field-type', v3_magic_ft_node)
485 set_if_exists('uuid-field-type', v3_uuid_ft_node)
e8f0d548 486 set_if_exists('data-stream-type-id-field-type', v3_dst_id_ft_node)
4810b707
PP
487 return v3_features_node
488
2d55dc7d
PP
489 v3_trace_node: _MapNode = collections.OrderedDict()
490 v3_trace_type_node: _MapNode = collections.OrderedDict()
4810b707
PP
491 v2_trace_node = v2_meta_node['trace']
492
a209cf4d
EB
493 # copy `byte-order` property as `trace-byte-order` property
494 _copy_prop_if_exists(v3_trace_type_node, v2_trace_node, 'byte-order', 'trace-byte-order')
4810b707
PP
495
496 # copy `uuid` property
497 _copy_prop_if_exists(v3_trace_type_node, v2_trace_node, 'uuid')
498
499 # copy `$log-levels`/`log-levels` property
500 new_prop_name = '$log-level-aliases'
501 _copy_prop_if_exists(v3_trace_type_node, v2_meta_node, 'log-levels', new_prop_name)
502 _copy_prop_if_exists(v3_trace_type_node, v2_meta_node, '$log-levels', new_prop_name)
503
504 # copy `clocks` property, converting clock type nodes
505 v2_clk_types_node = v2_meta_node.get('clocks')
506
507 if v2_clk_types_node is not None:
508 v3_clk_types_node = collections.OrderedDict()
509
510 for name, v2_clk_type_node in v2_clk_types_node.items():
511 v3_clk_types_node[name] = self._conv_clk_type_node(v2_clk_type_node)
512
513 v3_trace_type_node['clock-types'] = v3_clk_types_node
514
515 # set features node
516 v2_pkt_header_ft_node = v2_trace_node.get('packet-header-type')
517 v3_trace_type_node['$features'] = v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node)
518
e8f0d548
PP
519 # convert data stream type nodes
520 v3_dsts_node = collections.OrderedDict()
4810b707 521
e8f0d548 522 for dst_name, v2_dst_node in v2_meta_node['streams'].items():
4810b707 523 try:
e8f0d548 524 v3_dsts_node[dst_name] = self._conv_dst_node(v2_dst_node)
4810b707 525 except _ConfigurationParseError as exc:
e8f0d548 526 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707 527
e8f0d548 528 v3_trace_type_node['data-stream-types'] = v3_dsts_node
4810b707
PP
529
530 # If `v2_meta_node` has a `$default-stream` property, find the
e8f0d548
PP
531 # corresponding v3 data stream type node and set its
532 # `$is-default` property to `True`.
4810b707 533 prop_name = '$default-stream'
e8f0d548 534 v2_def_dst_node = v2_meta_node.get(prop_name)
4810b707 535
e8f0d548 536 if v2_def_dst_node is not None:
4810b707
PP
537 found = False
538
e8f0d548
PP
539 for dst_name, v3_dst_node in v3_dsts_node.items():
540 if dst_name == v2_def_dst_node:
541 v3_dst_node['$is-default'] = True
4810b707
PP
542 found = True
543 break
544
545 if not found:
546 raise _ConfigurationParseError(f'`{prop_name}` property',
e8f0d548 547 f'Data stream type `{v2_def_dst_node}` does not exist')
4810b707
PP
548
549 # set environment node
550 v2_env_node = v2_meta_node.get('env')
551
552 if v2_env_node is not None:
553 v3_trace_node['environment'] = copy.deepcopy(v2_env_node)
554
555 # set v3 trace node's type node
556 v3_trace_node['type'] = v3_trace_type_node
557
558 return v3_trace_node
559
560 # Transforms the root configuration node into a valid v3
561 # configuration node.
562 def _transform_config_node(self):
563 # remove the `version` property
564 del self._root_node['version']
565
566 # relocate prefix and option nodes
567 prefix_prop_name = 'prefix'
568 v2_prefix_node = self._root_node.get(prefix_prop_name, 'barectf_')
569 _del_prop_if_exists(self._root_node, prefix_prop_name)
570 opt_prop_name = 'options'
571 v2_options_node = self._root_node.get(opt_prop_name)
572 _del_prop_if_exists(self._root_node, opt_prop_name)
573 code_gen_node = collections.OrderedDict()
574 v3_prefixes = config_parse_common._v3_prefixes_from_v2_prefix(v2_prefix_node)
575 v3_prefix_node = collections.OrderedDict([
576 ('identifier', v3_prefixes.identifier),
577 ('file-name', v3_prefixes.file_name),
578 ])
579 code_gen_node[prefix_prop_name] = v3_prefix_node
580
581 if v2_options_node is not None:
582 header_node = collections.OrderedDict()
583 _copy_prop_if_exists(header_node, v2_options_node, 'gen-prefix-def',
584 'identifier-prefix-definition')
585 _copy_prop_if_exists(header_node, v2_options_node, 'gen-default-stream-def',
e8f0d548 586 'default-data-stream-type-name-definition')
4810b707
PP
587 code_gen_node['header'] = header_node
588
589 self._root_node[opt_prop_name] = collections.OrderedDict({
590 'code-generation': code_gen_node,
591 })
592
593 # convert the v2 metadata node into a v3 trace node
594 try:
595 self._root_node['trace'] = self._conv_meta_node(self._root_node['metadata'])
596 except _ConfigurationParseError as exc:
597 _append_error_ctx(exc, 'Metadata object')
598
599 del self._root_node['metadata']
600
601 # Expands the field type aliases found in the metadata node.
602 #
603 # This method modifies the metadata node.
604 #
605 # When this method returns:
606 #
607 # * Any field type alias is replaced with its full field type node
608 # equivalent.
609 #
610 # * The `type-aliases` property of metadata node is removed.
611 def _expand_ft_aliases(self):
612 meta_node = self._root_node['metadata']
613 ft_aliases_node = meta_node['type-aliases']
614
e8f0d548
PP
615 # Expand field type aliases within trace, data stream, and event
616 # record types now.
4810b707
PP
617 try:
618 self._resolve_ft_alias_from(ft_aliases_node, meta_node['trace'], 'packet-header-type')
619 except _ConfigurationParseError as exc:
620 _append_error_ctx(exc, 'Trace type')
621
e8f0d548 622 for dst_name, dst_node in meta_node['streams'].items():
4810b707 623 try:
e8f0d548
PP
624 self._resolve_ft_alias_from(ft_aliases_node, dst_node, 'packet-context-type')
625 self._resolve_ft_alias_from(ft_aliases_node, dst_node, 'event-header-type')
626 self._resolve_ft_alias_from(ft_aliases_node, dst_node, 'event-context-type')
4810b707 627
e8f0d548 628 for ert_name, ert_node in dst_node['events'].items():
4810b707 629 try:
e8f0d548
PP
630 self._resolve_ft_alias_from(ft_aliases_node, ert_node, 'context-type')
631 self._resolve_ft_alias_from(ft_aliases_node, ert_node, 'payload-type')
4810b707 632 except _ConfigurationParseError as exc:
e8f0d548 633 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 634 except _ConfigurationParseError as exc:
e8f0d548 635 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
636
637 # remove the (now unneeded) `type-aliases` node
638 del meta_node['type-aliases']
639
640 # Applies field type inheritance to all field type nodes found in
641 # the metadata node.
642 #
643 # This method modifies the metadata node.
644 #
645 # When this method returns, no field type node has an `$inherit` or
646 # `inherit` property.
647 def _apply_fts_inheritance(self):
648 meta_node = self._root_node['metadata']
649 self._apply_ft_inheritance(meta_node['trace'], 'packet-header-type')
650
e8f0d548
PP
651 for dst_node in meta_node['streams'].values():
652 self._apply_ft_inheritance(dst_node, 'packet-context-type')
653 self._apply_ft_inheritance(dst_node, 'event-header-type')
654 self._apply_ft_inheritance(dst_node, 'event-context-type')
4810b707 655
e8f0d548
PP
656 for ert_node in dst_node['events'].values():
657 self._apply_ft_inheritance(ert_node, 'context-type')
658 self._apply_ft_inheritance(ert_node, 'payload-type')
4810b707
PP
659
660 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
661 # metadata node has a `type-aliases` property.
662 def _expand_fts(self):
663 # Make sure that the current configuration node is valid
664 # considering field types are not expanded yet.
665 self._schema_validator.validate(self._root_node,
c3fa1a14 666 'config/2/config-pre-field-type-expansion')
4810b707
PP
667
668 meta_node = self._root_node['metadata']
669 ft_aliases_node = meta_node.get('type-aliases')
670
671 if ft_aliases_node is None:
672 # If there's no `type-aliases` node, then there's no field
673 # type aliases and therefore no possible inheritance.
674 return
675
676 # first, expand field type aliases
677 self._expand_ft_aliases()
678
679 # next, apply inheritance to create effective field types
680 self._apply_fts_inheritance()
681
e8f0d548 682 # Processes the inclusions of the event record type node `ert_node`,
4810b707 683 # returning the effective node.
e8f0d548
PP
684 def _process_ert_node_include(self, ert_node: _MapNode) -> _MapNode:
685 # Make sure the event record type node is valid for the
686 # inclusion processing stage.
687 self._schema_validator.validate(ert_node, 'config/2/ert-pre-include')
4810b707
PP
688
689 # process inclusions
e8f0d548 690 return self._process_node_include(ert_node, self._process_ert_node_include)
4810b707 691
e8f0d548
PP
692 # Processes the inclusions of the data stream type node `dst_node`,
693 # returning the effective node.
694 def _process_dst_node_include(self, dst_node: _MapNode) -> _MapNode:
695 def process_children_include(dst_node):
4810b707
PP
696 prop_name = 'events'
697
e8f0d548
PP
698 if prop_name in dst_node:
699 erts_node = dst_node[prop_name]
4810b707 700
e8f0d548
PP
701 for key in list(erts_node):
702 erts_node[key] = self._process_ert_node_include(erts_node[key])
4810b707 703
e8f0d548 704 # Make sure the data stream type node is valid for the inclusion
4810b707 705 # processing stage.
e8f0d548 706 self._schema_validator.validate(dst_node, 'config/2/dst-pre-include')
4810b707
PP
707
708 # process inclusions
e8f0d548 709 return self._process_node_include(dst_node, self._process_dst_node_include,
4810b707
PP
710 process_children_include)
711
712 # Processes the inclusions of the trace type node `trace_type_node`,
713 # returning the effective node.
2d55dc7d 714 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
4810b707
PP
715 # Make sure the trace type node is valid for the inclusion
716 # processing stage.
c3fa1a14 717 self._schema_validator.validate(trace_type_node, 'config/2/trace-type-pre-include')
4810b707
PP
718
719 # process inclusions
720 return self._process_node_include(trace_type_node, self._process_trace_type_node_include)
721
722 # Processes the inclusions of the clock type node `clk_type_node`,
723 # returning the effective node.
2d55dc7d 724 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
4810b707
PP
725 # Make sure the clock type node is valid for the inclusion
726 # processing stage.
c3fa1a14 727 self._schema_validator.validate(clk_type_node, 'config/2/clock-type-pre-include')
4810b707
PP
728
729 # process inclusions
730 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
731
732 # Processes the inclusions of the metadata node `meta_node`,
733 # returning the effective node.
2d55dc7d
PP
734 def _process_meta_node_include(self, meta_node: _MapNode) -> _MapNode:
735 def process_children_include(meta_node: _MapNode):
4810b707
PP
736 prop_name = 'trace'
737
738 if prop_name in meta_node:
739 meta_node[prop_name] = self._process_trace_type_node_include(meta_node[prop_name])
740
741 prop_name = 'clocks'
742
743 if prop_name in meta_node:
744 clk_types_node = meta_node[prop_name]
745
746 for key in list(clk_types_node):
747 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
748
749 prop_name = 'streams'
750
751 if prop_name in meta_node:
e8f0d548 752 dsts_node = meta_node[prop_name]
4810b707 753
e8f0d548
PP
754 for key in list(dsts_node):
755 dsts_node[key] = self._process_dst_node_include(dsts_node[key])
4810b707
PP
756
757 # Make sure the metadata node is valid for the inclusion
758 # processing stage.
c3fa1a14 759 self._schema_validator.validate(meta_node, 'config/2/metadata-pre-include')
4810b707
PP
760
761 # process inclusions
762 return self._process_node_include(meta_node, self._process_meta_node_include,
763 process_children_include)
764
765 # Processes the inclusions of the configuration node, modifying it
766 # during the process.
767 def _process_config_includes(self):
768 # Process inclusions in this order:
769 #
e8f0d548
PP
770 # 1. Clock type node, event record type nodes, and trace type
771 # nodes (the order between those is not important).
4810b707 772 #
e8f0d548 773 # 2. Data stream type nodes.
4810b707
PP
774 #
775 # 3. Metadata node.
776 #
777 # This is because:
778 #
779 # * A metadata node can include clock type nodes, a trace type
e8f0d548
PP
780 # node, data stream type nodes, and event record type nodes
781 # (indirectly).
4810b707 782 #
e8f0d548 783 # * A data stream type node can include event record type nodes.
4810b707
PP
784 #
785 # First, make sure the configuration node itself is valid for
786 # the inclusion processing stage.
787 self._schema_validator.validate(self._root_node,
c3fa1a14 788 'config/2/config-pre-include')
4810b707
PP
789
790 # Process metadata node inclusions.
791 #
792 # self._process_meta_node_include() returns a new (or the same)
793 # metadata node without any `$include` property in it,
794 # recursively.
795 prop_name = 'metadata'
796 self._root_node[prop_name] = self._process_meta_node_include(self._root_node[prop_name])
797
798 def _parse(self):
799 # Make sure the configuration node is minimally valid, that is,
800 # it contains a valid `version` property.
801 #
802 # This step does not validate the whole configuration node yet
803 # because we don't have an effective configuration node; we
804 # still need to:
805 #
806 # * Process inclusions.
807 # * Expand field types (aliases and inheritance).
c3fa1a14 808 self._schema_validator.validate(self._root_node, 'config/2/config-min')
4810b707
PP
809
810 # process configuration node inclusions
811 self._process_config_includes()
812
813 # Expand field type nodes.
814 #
815 # This process:
816 #
817 # 1. Replaces field type aliases with "effective" field type
818 # nodes, recursively.
819 #
820 # After this step, the `type-aliases` property of the
821 # metadata node is gone.
822 #
823 # 2. Applies inheritance, following the `$inherit`/`inherit`
824 # properties.
825 #
826 # After this step, field type nodes do not contain `$inherit`
827 # or `inherit` properties.
828 #
829 # This is done blindly, in that the process _doesn't_ validate
830 # field type nodes at this point.
831 #
832 # The reason we must do this here for a barectf 2 configuration,
833 # considering that barectf 3 also supports field type node
834 # aliases and inheritance, is that we need to find specific
835 # packet header and packet context field type member nodes (for
836 # example, `stream_id`, `packet_size`, or `timestamp_end`) to
837 # set the `$features` properties of barectf 3 trace type and
e8f0d548 838 # data stream type nodes. Those field type nodes can be aliases,
4810b707
PP
839 # contain aliases, or inherit from other nodes.
840 self._expand_fts()
841
842 # Validate the whole, (almost) effective configuration node.
843 #
844 # It's almost effective because the `log-level` property of
e8f0d548
PP
845 # event record type nodes can be log level aliases. Log level
846 # aliases are also a feature of a barectf 3 configuration node,
4810b707 847 # therefore this is compatible.
c3fa1a14 848 self._schema_validator.validate(self._root_node, 'config/2/config')
4810b707
PP
849
850 # Transform the current configuration node into a valid v3
851 # configuration node.
852 self._transform_config_node()
853
854 @property
2d55dc7d
PP
855 def config_node(self) -> config_parse_common._ConfigNodeV3:
856 return config_parse_common._ConfigNodeV3(typing.cast(_MapNode, self._root_node))
This page took 0.10442 seconds and 4 git commands to generate.