1 # The MIT License (MIT)
3 # Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 from barectf
import metadata
24 from barectf
import config
36 class _ConfigParseErrorCtx
:
37 def __init__(self
, name
, msg
=None):
50 class ConfigParseError(RuntimeError):
51 def __init__(self
, init_ctx_name
, init_ctx_msg
=None):
53 self
.append_ctx(init_ctx_name
, init_ctx_msg
)
59 def append_ctx(self
, name
, msg
=None):
60 self
._ctx
.append(_ConfigParseErrorCtx(name
, msg
))
63 def _opt_to_public(obj
):
67 return obj
.to_public()
75 if self
._public
is None:
76 self
._public
= self
._to
_public
()
81 raise NotImplementedError
84 class _PropertyMapping(_PseudoObj
):
91 return metadata
.PropertyMapping(self
.object.to_public(), self
.prop
)
94 class _Integer(_PseudoObj
):
98 self
.byte_order
= None
102 self
.reset_encoding()
103 self
.reset_property_mappings()
105 def reset_align(self
):
108 def reset_signed(self
):
111 def reset_base(self
):
114 def reset_encoding(self
):
115 self
.encoding
= metadata
.Encoding
.NONE
117 def reset_property_mappings(self
):
118 self
.property_mappings
= []
121 def real_align(self
):
122 if self
.align
is None:
123 if self
.size
% 8 == 0:
130 def _to_public(self
):
131 prop_mappings
= [pm
.to_public() for pm
in self
.property_mappings
]
132 return metadata
.Integer(self
.size
, self
.byte_order
, self
.align
,
133 self
.signed
, self
.base
, self
.encoding
,
137 class _FloatingPoint(_PseudoObj
):
141 self
.mant_size
= None
142 self
.byte_order
= None
145 def reset_align(self
):
149 def real_align(self
):
152 def _to_public(self
):
153 return metadata
.FloatingPoint(self
.exp_size
, self
.mant_size
,
154 self
.byte_order
, self
.align
)
157 class _Enum(_PseudoObj
):
160 self
.value_type
= None
161 self
.members
= collections
.OrderedDict()
164 def last_value(self
):
165 if len(self
.members
) == 0:
168 return list(self
.members
.values())[-1][1]
171 def real_align(self
):
172 return self
.value_type
.real_align
174 def _to_public(self
):
175 return metadata
.Enum(self
.value_type
.to_public(), self
.members
)
178 class _String(_PseudoObj
):
181 self
.reset_encoding()
183 def reset_encoding(self
):
184 self
.encoding
= metadata
.Encoding
.UTF8
188 def _to_public(self
):
189 return metadata
.String(self
.encoding
)
192 class _Array(_PseudoObj
):
195 self
.element_type
= None
199 def real_align(self
):
200 return self
.element_type
.real_align
202 def _to_public(self
):
203 return metadata
.Array(self
.element_type
.to_public(), self
.length
)
206 class _Struct(_PseudoObj
):
209 self
.reset_min_align()
212 def reset_min_align(self
):
215 def reset_fields(self
):
216 self
.fields
= collections
.OrderedDict()
219 def real_align(self
):
220 align
= self
.min_align
222 for pseudo_field
in self
.fields
.values():
223 if pseudo_field
.real_align
> align
:
224 align
= pseudo_field
.real_align
228 def _to_public(self
):
231 for name
, pseudo_field
in self
.fields
.items():
232 fields
.append((name
, pseudo_field
.to_public()))
234 return metadata
.Struct(self
.min_align
, collections
.OrderedDict(fields
))
237 class _Trace(_PseudoObj
):
240 self
.byte_order
= None
242 self
.packet_header_type
= None
244 def _to_public(self
):
245 return metadata
.Trace(self
.byte_order
, self
.uuid
,
246 _opt_to_public(self
.packet_header_type
))
249 class _Clock(_PseudoObj
):
254 self
.reset_description()
256 self
.reset_error_cycles()
257 self
.reset_offset_seconds()
258 self
.reset_offset_cycles()
259 self
.reset_absolute()
260 self
.reset_return_ctype()
262 def reset_name(self
):
265 def reset_uuid(self
):
268 def reset_description(self
):
269 self
.description
= None
271 def reset_freq(self
):
274 def reset_error_cycles(self
):
275 self
.error_cycles
= 0
277 def reset_offset_seconds(self
):
278 self
.offset_seconds
= 0
280 def reset_offset_cycles(self
):
281 self
.offset_cycles
= 0
283 def reset_absolute(self
):
284 self
.absolute
= False
286 def reset_return_ctype(self
):
287 self
.return_ctype
= 'uint32_t'
289 def _to_public(self
):
290 return metadata
.Clock(self
.name
, self
.uuid
, self
.description
, self
.freq
,
291 self
.error_cycles
, self
.offset_seconds
,
292 self
.offset_cycles
, self
.absolute
,
296 class _Event(_PseudoObj
):
301 self
.log_level
= None
302 self
.payload_type
= None
303 self
.context_type
= None
305 def _to_public(self
):
306 return metadata
.Event(self
.id, self
.name
, self
.log_level
,
307 _opt_to_public(self
.payload_type
),
308 _opt_to_public(self
.context_type
))
311 class _Stream(_PseudoObj
):
316 self
.packet_context_type
= None
317 self
.event_header_type
= None
318 self
.event_context_type
= None
319 self
.events
= collections
.OrderedDict()
321 def is_event_empty(self
, event
):
324 if self
.event_header_type
is not None:
325 total_fields
+= len(self
.event_header_type
.fields
)
327 if self
.event_context_type
is not None:
328 total_fields
+= len(self
.event_context_type
.fields
)
330 if event
.context_type
is not None:
331 total_fields
+= len(event
.context_type
.fields
)
333 if event
.payload_type
is not None:
334 total_fields
+= len(event
.payload_type
.fields
)
336 return total_fields
== 0
338 def _to_public(self
):
341 for name
, pseudo_ev
in self
.events
.items():
342 events
.append((name
, pseudo_ev
.to_public()))
344 return metadata
.Stream(self
.id, self
.name
,
345 _opt_to_public(self
.packet_context_type
),
346 _opt_to_public(self
.event_header_type
),
347 _opt_to_public(self
.event_context_type
),
348 collections
.OrderedDict(events
))
351 class _Metadata(_PseudoObj
):
358 self
.default_stream_name
= None
360 def _to_public(self
):
363 for name
, pseudo_clock
in self
.clocks
.items():
364 clocks
.append((name
, pseudo_clock
.to_public()))
368 for name
, pseudo_stream
in self
.streams
.items():
369 streams
.append((name
, pseudo_stream
.to_public()))
371 return metadata
.Metadata(self
.trace
.to_public(), self
.env
,
372 collections
.OrderedDict(clocks
),
373 collections
.OrderedDict(streams
),
374 self
.default_stream_name
)
377 def _is_assoc_array_prop(node
):
378 return isinstance(node
, dict)
381 def _is_array_prop(node
):
382 return isinstance(node
, list)
385 def _is_int_prop(node
):
386 return type(node
) is int
389 def _is_str_prop(node
):
390 return type(node
) is str
393 def _is_bool_prop(node
):
394 return type(node
) is bool
397 def _is_valid_alignment(align
):
398 return ((align
& (align
- 1)) == 0) and align
> 0
401 def _byte_order_str_to_bo(bo_str
):
402 bo_str
= bo_str
.lower()
405 return metadata
.ByteOrder
.LE
407 return metadata
.ByteOrder
.BE
410 def _encoding_str_to_encoding(encoding_str
):
411 encoding_str
= encoding_str
.lower()
413 if encoding_str
== 'utf-8' or encoding_str
== 'utf8':
414 return metadata
.Encoding
.UTF8
415 elif encoding_str
== 'ascii':
416 return metadata
.Encoding
.ASCII
417 elif encoding_str
== 'none':
418 return metadata
.Encoding
.NONE
421 _re_iden
= re
.compile(r
'^[a-zA-Z][a-zA-Z0-9_]*$')
422 _ctf_keywords
= set([
441 def _is_valid_identifier(iden
):
442 if not _re_iden
.match(iden
):
445 if _re_iden
in _ctf_keywords
:
451 def _get_first_unknown_prop(node
, known_props
):
452 for prop_name
in node
:
453 if prop_name
in known_props
:
459 # This validator validates the configured metadata for barectf specific
464 # * all header/contexts are at least byte-aligned
465 # * all integer and floating point number sizes to be <= 64
466 # * no inner structures or arrays
467 class _BarectfMetadataValidator
:
469 self
._type
_to
_validate
_type
_func
= {
470 _Integer
: self
._validate
_int
_type
,
471 _FloatingPoint
: self
._validate
_float
_type
,
472 _Enum
: self
._validate
_enum
_type
,
473 _String
: self
._validate
_string
_type
,
474 _Struct
: self
._validate
_struct
_type
,
475 _Array
: self
._validate
_array
_type
,
478 def _validate_int_type(self
, t
, entity_root
):
480 raise ConfigParseError('Integer type', 'Size must be lesser than or equal to 64 bits')
482 def _validate_float_type(self
, t
, entity_root
):
483 if t
.exp_size
+ t
.mant_size
> 64:
484 raise ConfigParseError('Floating point number type', 'Size must be lesser than or equal to 64 bits')
486 def _validate_enum_type(self
, t
, entity_root
):
487 if t
.value_type
.size
> 64:
488 raise ConfigParseError('Enumeration type', 'Integer type\'s size must be lesser than or equal to 64 bits')
490 def _validate_string_type(self
, t
, entity_root
):
493 def _validate_struct_type(self
, t
, entity_root
):
495 raise ConfigParseError('Structure type', 'Inner structure types are not supported as of this version')
497 for field_name
, field_type
in t
.fields
.items():
498 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
499 if field_name
== 'uuid':
504 self
._validate
_type
(field_type
, False)
505 except ConfigParseError
as exc
:
506 exc
.append_ctx('Structure type\' field "{}"'.format(field_name
))
509 def _validate_array_type(self
, t
, entity_root
):
510 raise ConfigParseError('Array type', 'Not supported as of this version')
512 def _validate_type(self
, t
, entity_root
):
513 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
515 def _validate_entity(self
, t
):
519 # make sure entity is byte-aligned
521 raise ConfigParseError('Root type', 'Alignment must be at least byte-aligned')
523 # make sure entity is a structure
524 if type(t
) is not _Struct
:
525 raise ConfigParseError('Root type', 'Expecting a structure type')
528 self
._validate
_type
(t
, True)
530 def _validate_entities_and_names(self
, meta
):
531 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
534 self
._validate
_entity
(meta
.trace
.packet_header_type
)
535 except ConfigParseError
as exc
:
536 exc
.append_ctx('Trace', 'Invalid packet header type')
539 for stream_name
, stream
in meta
.streams
.items():
540 if not _is_valid_identifier(stream_name
):
541 raise ConfigParseError('Trace', 'Stream name "{}" is not a valid C identifier'.format(stream_name
))
543 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
546 self
._validate
_entity
(stream
.packet_context_type
)
547 except ConfigParseError
as exc
:
548 exc
.append_ctx('Stream "{}"'.format(stream_name
),
549 'Invalid packet context type')
552 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
555 self
._validate
_entity
(stream
.event_header_type
)
556 except ConfigParseError
as exc
:
557 exc
.append_ctx('Stream "{}"'.format(stream_name
),
558 'Invalid event header type')
561 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
564 self
._validate
_entity
(stream
.event_context_type
)
565 except ConfigParseError
as exc
:
566 exc
.append_ctx('Stream "{}"'.format(stream_name
),
567 'Invalid event context type'.format(stream_name
))
571 for ev_name
, ev
in stream
.events
.items():
572 if not _is_valid_identifier(ev_name
):
573 raise ConfigParseError('Stream "{}"'.format(stream_name
),
574 'Event name "{}" is not a valid C identifier'.format(ev_name
))
576 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
579 self
._validate
_entity
(ev
.context_type
)
580 except ConfigParseError
as exc
:
581 exc
.append_ctx('Event "{}"'.format(ev_name
),
582 'Invalid context type')
585 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
588 self
._validate
_entity
(ev
.payload_type
)
589 except ConfigParseError
as exc
:
590 exc
.append_ctx('Event "{}"'.format(ev_name
),
591 'Invalid payload type')
594 if stream
.is_event_empty(ev
):
595 raise ConfigParseError('Event "{}"'.format(ev_name
), 'Empty')
596 except ConfigParseError
as exc
:
597 exc
.append_ctx('Stream "{}"'.format(stream_name
))
600 def _validate_default_stream(self
, meta
):
601 if meta
.default_stream_name
:
602 if meta
.default_stream_name
not in meta
.streams
.keys():
603 raise ConfigParseError('barectf metadata', 'Default stream name ("{}") does not exist'.format(meta
.default_stream_name
))
605 def validate(self
, meta
):
606 self
._validate
_entities
_and
_names
(meta
)
607 self
._validate
_default
_stream
(meta
)
610 # This validator validates special fields of trace, stream, and event
611 # types. For example, if checks that the "stream_id" field exists in the
612 # trace packet header if there's more than one stream, and much more.
613 class _MetadataSpecialFieldsValidator
:
614 def _validate_trace_packet_header_type(self
, t
):
615 # needs "stream_id" field?
616 if len(self
._meta
.streams
) > 1:
619 raise ConfigParseError('"packet-header-type" property',
620 'Need "stream_id" field (more than one stream), but trace packet header type is missing')
622 if type(t
) is not _Struct
:
623 raise ConfigParseError('"packet-header-type" property',
624 'Need "stream_id" field (more than one stream), but trace packet header type is not a structure type')
626 if 'stream_id' not in t
.fields
:
627 raise ConfigParseError('"packet-header-type" property',
628 'Need "stream_id" field (more than one stream)')
630 # validate "magic" and "stream_id" types
631 if type(t
) is not _Struct
:
634 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
635 if field_name
== 'magic':
636 if type(field_type
) is not _Integer
:
637 raise ConfigParseError('"packet-header-type" property',
638 '"magic" field must be an integer type')
640 if field_type
.signed
or field_type
.size
!= 32:
641 raise ConfigParseError('"packet-header-type" property',
642 '"magic" field must be a 32-bit unsigned integer type')
645 raise ConfigParseError('"packet-header-type" property',
646 '"magic" field must be the first trace packet header type\'s field')
647 elif field_name
== 'stream_id':
648 if type(field_type
) is not _Integer
:
649 raise ConfigParseError('"packet-header-type" property',
650 '"stream_id" field must be an integer type')
652 if field_type
.signed
:
653 raise ConfigParseError('"packet-header-type" property',
654 '"stream_id" field must be an unsigned integer type')
656 # "id" size can fit all event IDs
657 if len(self
._meta
.streams
) > (1 << field_type
.size
):
658 raise ConfigParseError('"packet-header-type" property',
659 '"stream_id" field\' size is too small for the number of trace streams')
660 elif field_name
== 'uuid':
661 if self
._meta
.trace
.uuid
is None:
662 raise ConfigParseError('"packet-header-type" property',
663 '"uuid" field specified, but no trace UUID provided')
665 if type(field_type
) is not _Array
:
666 raise ConfigParseError('"packet-header-type" property',
667 '"uuid" field must be an array')
669 if field_type
.length
!= 16:
670 raise ConfigParseError('"packet-header-type" property',
671 '"uuid" field must be an array of 16 bytes')
673 element_type
= field_type
.element_type
675 if type(element_type
) is not _Integer
:
676 raise ConfigParseError('"packet-header-type" property',
677 '"uuid" field must be an array of 16 unsigned bytes')
679 if element_type
.size
!= 8:
680 raise ConfigParseError('"packet-header-type" property',
681 '"uuid" field must be an array of 16 unsigned bytes')
683 if element_type
.signed
:
684 raise ConfigParseError('"packet-header-type" property',
685 '"uuid" field must be an array of 16 unsigned bytes')
687 if element_type
.real_align
!= 8:
688 raise ConfigParseError('"packet-header-type" property',
689 '"uuid" field must be an array of 16 unsigned, byte-aligned bytes')
691 def _validate_trace(self
, meta
):
692 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
694 def _validate_stream_packet_context(self
, stream
):
695 t
= stream
.packet_context_type
698 raise ConfigParseError('Stream',
699 'Missing "packet-context-type" property')
701 if type(t
) is not _Struct
:
702 raise ConfigParseError('"packet-context-type" property',
703 'Expecting a structure type')
705 # "timestamp_begin", if exists, is an unsigned integer type,
709 if 'timestamp_begin' in t
.fields
:
710 ts_begin
= t
.fields
['timestamp_begin']
712 if type(ts_begin
) is not _Integer
:
713 raise ConfigParseError('"packet-context-type" property',
714 '"timestamp_begin" field must be an integer type')
717 raise ConfigParseError('"packet-context-type" property',
718 '"timestamp_begin" field must be an unsigned integer type')
720 if not ts_begin
.property_mappings
:
721 raise ConfigParseError('"packet-context-type" property',
722 '"timestamp_begin" field must be mapped to a clock')
724 # "timestamp_end", if exists, is an unsigned integer type,
728 if 'timestamp_end' in t
.fields
:
729 ts_end
= t
.fields
['timestamp_end']
731 if type(ts_end
) is not _Integer
:
732 raise ConfigParseError('"packet-context-type" property',
733 '"timestamp_end" field must be an integer type')
736 raise ConfigParseError('"packet-context-type" property',
737 '"timestamp_end" field must be an unsigned integer type')
739 if not ts_end
.property_mappings
:
740 raise ConfigParseError('"packet-context-type" property',
741 '"timestamp_end" field must be mapped to a clock')
743 # "timestamp_begin" and "timestamp_end" exist together
744 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
745 raise ConfigParseError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
747 # "timestamp_begin" and "timestamp_end" are mapped to the same clock
748 if ts_begin
is not None and ts_end
is not None:
749 if ts_begin
.property_mappings
[0].object.name
!= ts_end
.property_mappings
[0].object.name
:
750 raise ConfigParseError('"timestamp_begin" and "timestamp_end" fields must be mapped to the same clock object in stream packet context type')
752 # "events_discarded", if exists, is an unsigned integer type
753 if 'events_discarded' in t
.fields
:
754 events_discarded
= t
.fields
['events_discarded']
756 if type(events_discarded
) is not _Integer
:
757 raise ConfigParseError('"packet-context-type" property',
758 '"events_discarded" field must be an integer type')
760 if events_discarded
.signed
:
761 raise ConfigParseError('"packet-context-type" property',
762 '"events_discarded" field must be an unsigned integer type')
764 # "packet_size" and "content_size" must exist
765 if 'packet_size' not in t
.fields
:
766 raise ConfigParseError('"packet-context-type" property',
767 'Missing "packet_size" field in stream packet context type')
769 packet_size
= t
.fields
['packet_size']
771 # "content_size" and "content_size" must exist
772 if 'content_size' not in t
.fields
:
773 raise ConfigParseError('"packet-context-type" property',
774 'Missing "content_size" field in stream packet context type')
776 content_size
= t
.fields
['content_size']
778 # "packet_size" is an unsigned integer type
779 if type(packet_size
) is not _Integer
:
780 raise ConfigParseError('"packet-context-type" property',
781 '"packet_size" field in stream packet context type must be an integer type')
783 if packet_size
.signed
:
784 raise ConfigParseError('"packet-context-type" property',
785 '"packet_size" field in stream packet context type must be an unsigned integer type')
787 # "content_size" is an unsigned integer type
788 if type(content_size
) is not _Integer
:
789 raise ConfigParseError('"packet-context-type" property',
790 '"content_size" field in stream packet context type must be an integer type')
792 if content_size
.signed
:
793 raise ConfigParseError('"packet-context-type" property',
794 '"content_size" field in stream packet context type must be an unsigned integer type')
796 # "packet_size" size should be greater than or equal to "content_size" size
797 if content_size
.size
> packet_size
.size
:
798 raise ConfigParseError('"packet-context-type" property',
799 '"content_size" field size must be lesser than or equal to "packet_size" field size')
801 def _validate_stream_event_header(self
, stream
):
802 t
= stream
.event_header_type
805 if len(stream
.events
) > 1:
808 raise ConfigParseError('"event-header-type" property',
809 'Need "id" field (more than one event), but stream event header type is missing')
811 if type(t
) is not _Struct
:
812 raise ConfigParseError('"event-header-type" property',
813 'Need "id" field (more than one event), but stream event header type is not a structure type')
815 if 'id' not in t
.fields
:
816 raise ConfigParseError('"event-header-type" property',
817 'Need "id" field (more than one event)')
819 # validate "id" and "timestamp" types
820 if type(t
) is not _Struct
:
823 # "timestamp", if exists, is an unsigned integer type,
825 if 'timestamp' in t
.fields
:
826 ts
= t
.fields
['timestamp']
828 if type(ts
) is not _Integer
:
829 raise ConfigParseError('"event-header-type" property',
830 '"timestamp" field must be an integer type')
833 raise ConfigParseError('"event-header-type" property',
834 '"timestamp" field must be an unsigned integer type')
836 if not ts
.property_mappings
:
837 raise ConfigParseError('"event-header-type" property',
838 '"timestamp" field must be mapped to a clock')
843 # "id" is an unsigned integer type
844 if type(eid
) is not _Integer
:
845 raise ConfigParseError('"event-header-type" property',
846 '"id" field must be an integer type')
849 raise ConfigParseError('"event-header-type" property',
850 '"id" field must be an unsigned integer type')
852 # "id" size can fit all event IDs
853 if len(stream
.events
) > (1 << eid
.size
):
854 raise ConfigParseError('"event-header-type" property',
855 '"id" field\' size is too small for the number of stream events')
857 def _validate_stream(self
, stream
):
858 self
._validate
_stream
_packet
_context
(stream
)
859 self
._validate
_stream
_event
_header
(stream
)
861 def validate(self
, meta
):
863 self
._validate
_trace
(meta
)
865 for stream
in meta
.streams
.values():
867 self
._validate
_stream
(stream
)
868 except ConfigParseError
as exc
:
869 exc
.append_ctx('Stream "{}"'.format(stream
.name
), 'Invalid')
873 # Entities. Order of values is important here.
875 class _Entity(enum
.IntEnum
):
876 TRACE_PACKET_HEADER
= 0
877 STREAM_PACKET_CONTEXT
= 1
878 STREAM_EVENT_HEADER
= 2
879 STREAM_EVENT_CONTEXT
= 3
884 # Since type inheritance allows types to be only partially defined at
885 # any place in the configuration, this validator validates that actual
886 # trace, stream, and event types are all complete and valid. Therefore
887 # an invalid, but unusued type alias is accepted.
888 class _MetadataTypesHistologyValidator
:
890 self
._type
_to
_validate
_type
_histology
_func
= {
891 _Integer
: self
._validate
_integer
_histology
,
892 _FloatingPoint
: self
._validate
_float
_histology
,
893 _Enum
: self
._validate
_enum
_histology
,
894 _String
: self
._validate
_string
_histology
,
895 _Struct
: self
._validate
_struct
_histology
,
896 _Array
: self
._validate
_array
_histology
,
899 def _validate_integer_histology(self
, t
):
902 raise ConfigParseError('Integer type', 'Missing size')
904 def _validate_float_histology(self
, t
):
905 # exponent digits is set
906 if t
.exp_size
is None:
907 raise ConfigParseError('Floating point number type',
908 'Missing exponent size')
910 # mantissa digits is set
911 if t
.mant_size
is None:
912 raise ConfigParseError('Floating point number type',
913 'Missing mantissa size')
915 # exponent and mantissa sum is a multiple of 8
916 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
917 raise ConfigParseError('Floating point number type',
918 'Mantissa and exponent sizes sum must be a multiple of 8')
920 def _validate_enum_histology(self
, t
):
921 # integer type is set
922 if t
.value_type
is None:
923 raise ConfigParseError('Enumeration type', 'Missing value type')
925 # there's at least one member
927 raise ConfigParseError('Enumeration type', 'At least one member required')
929 # no overlapping values and all values are valid considering
933 if t
.value_type
.signed
:
934 value_min
= -(1 << t
.value_type
.size
- 1)
935 value_max
= (1 << (t
.value_type
.size
- 1)) - 1
938 value_max
= (1 << t
.value_type
.size
) - 1
940 for label
, value
in t
.members
.items():
942 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
943 raise ConfigParseError('Enumeration type\'s member "{}"',
944 'Overlaps another member'.format(label
))
946 name_fmt
= 'Enumeration type\'s member "{}"'
947 msg_fmt
= 'Value {} is outside the value type range [{}, {}]'
949 if value
[0] < value_min
or value
[0] > value_max
:
950 raise ConfigParseError(name_fmt
.format(label
),
951 msg_fmt
.format(value
[0], value_min
, value_max
))
953 if value
[1] < value_min
or value
[1] > value_max
:
954 raise ConfigParseError(name_fmt
.format(label
),
955 msg_fmt
.format(value
[0], value_min
, value_max
))
959 def _validate_string_histology(self
, t
):
963 def _validate_struct_histology(self
, t
):
964 # all fields are valid
965 for field_name
, field_type
in t
.fields
.items():
967 self
._validate
_type
_histology
(field_type
)
968 except ConfigParseError
as exc
:
969 exc
.append_ctx('Structure type\'s field "{}"'.format(field_name
))
972 def _validate_array_histology(self
, t
):
975 raise ConfigParseError('Array type', 'Missing length')
977 # element type is set
978 if t
.element_type
is None:
979 raise ConfigParseError('Array type', 'Missing element type')
981 # element type is valid
983 self
._validate
_type
_histology
(t
.element_type
)
984 except ConfigParseError
as exc
:
985 exc
.append_ctx('Array type', 'Invalid element type')
988 def _validate_type_histology(self
, t
):
992 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
994 def _validate_entity_type_histology(self
, t
):
998 if type(t
) is not _Struct
:
999 raise ConfigParseError('Root type', 'Expecting a structure type')
1001 self
._validate
_type
_histology
(t
)
1003 def _validate_event_types_histology(self
, ev
):
1006 # validate event context type
1008 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1009 except ConfigParseError
as exc
:
1010 exc
.append_ctx('Event "{}"'.format(ev
.name
),
1011 'Invalid context type')
1014 # validate event payload type
1016 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1017 except ConfigParseError
as exc
:
1018 exc
.append_ctx('Event "{}"'.format(ev
.name
),
1019 'Invalid payload type')
1022 def _validate_stream_types_histology(self
, stream
):
1023 stream_name
= stream
.name
1025 # validate stream packet context type
1027 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1028 except ConfigParseError
as exc
:
1029 exc
.append_ctx('Stream "{}"'.format(stream_name
),
1030 'Invalid packet context type')
1033 # validate stream event header type
1035 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1036 except ConfigParseError
as exc
:
1037 exc
.append_ctx('Stream "{}"'.format(stream_name
),
1038 'Invalid event header type')
1041 # validate stream event context type
1043 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1044 except ConfigParseError
as exc
:
1045 exc
.append_ctx('Stream "{}"'.format(stream_name
),
1046 'Invalid event context type')
1050 for ev
in stream
.events
.values():
1052 self
._validate
_event
_types
_histology
(ev
)
1053 except ConfigParseError
as exc
:
1054 exc
.append_ctx('Stream "{}"'.format(stream_name
),
1058 def validate(self
, meta
):
1059 # validate trace packet header type
1061 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1062 except ConfigParseError
as exc
:
1063 exc
.append_ctx('Metadata\'s trace', 'Invalid packet header type')
1067 for stream
in meta
.streams
.values():
1068 self
._validate
_stream
_types
_histology
(stream
)
1071 class _YamlConfigParser
:
1072 def __init__(self
, include_dirs
, ignore_include_not_found
, dump_config
):
1073 self
._class
_name
_to
_create
_type
_func
= {
1074 'int': self
._create
_integer
,
1075 'integer': self
._create
_integer
,
1076 'flt': self
._create
_float
,
1077 'float': self
._create
_float
,
1078 'floating-point': self
._create
_float
,
1079 'enum': self
._create
_enum
,
1080 'enumeration': self
._create
_enum
,
1081 'str': self
._create
_string
,
1082 'string': self
._create
_string
,
1083 'struct': self
._create
_struct
,
1084 'structure': self
._create
_struct
,
1085 'array': self
._create
_array
,
1087 self
._type
_to
_create
_type
_func
= {
1088 _Integer
: self
._create
_integer
,
1089 _FloatingPoint
: self
._create
_float
,
1090 _Enum
: self
._create
_enum
,
1091 _String
: self
._create
_string
,
1092 _Struct
: self
._create
_struct
,
1093 _Array
: self
._create
_array
,
1095 self
._include
_dirs
= include_dirs
1096 self
._ignore
_include
_not
_found
= ignore_include_not_found
1097 self
._dump
_config
= dump_config
1099 def _set_byte_order(self
, metadata_node
):
1100 if 'trace' not in metadata_node
:
1101 raise ConfigParseError('Metadata', 'Missing "trace" property')
1103 trace_node
= metadata_node
['trace']
1105 if not _is_assoc_array_prop(trace_node
):
1106 raise ConfigParseError('Metadata\'s "trace" property',
1107 'Must be an associative array')
1109 if 'byte-order' not in trace_node
:
1110 raise ConfigParseError('Metadata\'s "trace" property',
1111 'Missing "byte-order" property')
1113 bo_node
= trace_node
['byte-order']
1115 if not _is_str_prop(bo_node
):
1116 raise ConfigParseError('Metadata\'s "trace" property',
1117 '"byte-order" property must be a string ("le" or "be")')
1119 self
._bo
= _byte_order_str_to_bo(bo_node
)
1121 if self
._bo
is None:
1122 raise ConfigParseError('Metadata\'s "trace" property',
1123 'Invalid "byte-order" property: must be "le" or "be"')
1125 def _lookup_type_alias(self
, name
):
1126 if name
in self
._tas
:
1127 return copy
.deepcopy(self
._tas
[name
])
1129 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1130 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1133 raise ConfigParseError('Integer type\'s clock property mapping',
1134 'Unknown property: "{}"'.format(unk_prop
))
1136 if 'name' not in prop_mapping_node
:
1137 raise ConfigParseError('Integer type\'s clock property mapping',
1138 'Missing "name" property')
1140 if 'property' not in prop_mapping_node
:
1141 raise ConfigParseError('Integer type\'s clock property mapping',
1142 'Missing "property" property')
1144 clock_name
= prop_mapping_node
['name']
1145 prop
= prop_mapping_node
['property']
1147 if not _is_str_prop(clock_name
):
1148 raise ConfigParseError('Integer type\'s clock property mapping',
1149 '"name" property must be a string')
1151 if not _is_str_prop(prop
):
1152 raise ConfigParseError('Integer type\'s clock property mapping',
1153 '"property" property must be a string')
1155 if clock_name
not in self
._clocks
:
1156 raise ConfigParseError('Integer type\'s clock property mapping',
1157 'Invalid clock name "{}"'.format(clock_name
))
1160 raise ConfigParseError('Integer type\'s clock property mapping',
1161 'Invalid "property" property: "{}"'.format(prop
))
1163 mapped_clock
= self
._clocks
[clock_name
]
1164 prop_mapping
= _PropertyMapping()
1165 prop_mapping
.object = mapped_clock
1166 prop_mapping
.prop
= prop
1167 int_obj
.property_mappings
.append(prop_mapping
)
1169 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
1170 kp
= known_props
+ ['inherit', 'class']
1172 if self
._version
>= 201:
1173 kp
.append('$inherit')
1175 return _get_first_unknown_prop(type_node
, kp
)
1177 def _create_integer(self
, obj
, node
):
1179 # create integer object
1182 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1189 'property-mappings',
1193 raise ConfigParseError('Integer type',
1194 'Unknown property: "{}"'.format(unk_prop
))
1200 if not _is_int_prop(size
):
1201 raise ConfigParseError('Integer type',
1202 '"size" property of integer type object must be an integer')
1205 raise ConfigParseError('Integer type',
1206 'Invalid integer size: {}'.format(size
))
1212 align
= node
['align']
1217 if not _is_int_prop(align
):
1218 raise ConfigParseError('Integer type',
1219 '"align" property of integer type object must be an integer')
1221 if not _is_valid_alignment(align
):
1222 raise ConfigParseError('Integer type',
1223 'Invalid alignment: {}'.format(align
))
1228 if 'signed' in node
:
1229 signed
= node
['signed']
1234 if not _is_bool_prop(signed
):
1235 raise ConfigParseError('Integer type',
1236 '"signed" property of integer type object must be a boolean')
1241 if 'byte-order' in node
:
1242 byte_order
= node
['byte-order']
1244 if byte_order
is None:
1245 obj
.byte_order
= self
._bo
1247 if not _is_str_prop(byte_order
):
1248 raise ConfigParseError('Integer type',
1249 '"byte-order" property of integer type object must be a string ("le" or "be")')
1251 byte_order
= _byte_order_str_to_bo(byte_order
)
1253 if byte_order
is None:
1254 raise ConfigParseError('Integer type',
1255 'Invalid "byte-order" property in integer type object')
1257 obj
.byte_order
= byte_order
1259 obj
.byte_order
= self
._bo
1268 if not _is_str_prop(base
):
1269 raise ConfigParseError('Integer type',
1270 '"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1281 raise ConfigParseError('Integer type',
1282 'Unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base
))
1287 if 'encoding' in node
:
1288 encoding
= node
['encoding']
1290 if encoding
is None:
1291 obj
.reset_encoding()
1293 if not _is_str_prop(encoding
):
1294 raise ConfigParseError('Integer type',
1295 '"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1297 encoding
= _encoding_str_to_encoding(encoding
)
1299 if encoding
is None:
1300 raise ConfigParseError('Integer type',
1301 'Invalid "encoding" property in integer type object')
1303 obj
.encoding
= encoding
1306 if 'property-mappings' in node
:
1307 prop_mappings
= node
['property-mappings']
1309 if prop_mappings
is None:
1310 obj
.reset_property_mappings()
1312 if not _is_array_prop(prop_mappings
):
1313 raise ConfigParseError('Integer type',
1314 '"property-mappings" property of integer type object must be an array')
1316 if len(prop_mappings
) > 1:
1317 raise ConfigParseError('Integer type',
1318 'Length of "property-mappings" array in integer type object must be 1')
1320 for index
, prop_mapping
in enumerate(prop_mappings
):
1321 if not _is_assoc_array_prop(prop_mapping
):
1322 raise ConfigParseError('Integer type',
1323 'Elements of "property-mappings" property of integer type object must be associative arrays')
1325 if 'type' not in prop_mapping
:
1326 raise ConfigParseError('Integer type',
1327 'Missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1329 prop_type
= prop_mapping
['type']
1331 if not _is_str_prop(prop_type
):
1332 raise ConfigParseError('Integer type',
1333 '"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1335 if prop_type
== 'clock':
1336 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1338 raise ConfigParseError('Integer type',
1339 'Unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1343 def _create_float(self
, obj
, node
):
1345 # create floating point number object
1346 obj
= _FloatingPoint()
1348 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1355 raise ConfigParseError('Floating point number type',
1356 'Unknown property: "{}"'.format(unk_prop
))
1362 if not _is_assoc_array_prop(size
):
1363 raise ConfigParseError('Floating point number type',
1364 '"size" property must be an associative array')
1366 unk_prop
= _get_first_unknown_prop(size
, ['exp', 'mant'])
1369 raise ConfigParseError('Floating point number type\'s "size" property',
1370 'Unknown property: "{}"'.format(unk_prop
))
1375 if not _is_int_prop(exp
):
1376 raise ConfigParseError('Floating point number type\'s "size" property',
1377 '"exp" property must be an integer')
1380 raise ConfigParseError('Floating point number type\'s "size" property',
1381 'Invalid exponent size: {}')
1388 if not _is_int_prop(mant
):
1389 raise ConfigParseError('Floating point number type\'s "size" property',
1390 '"mant" property must be an integer')
1393 raise ConfigParseError('Floating point number type\'s "size" property',
1394 'Invalid mantissa size: {}')
1396 obj
.mant_size
= mant
1400 align
= node
['align']
1405 if not _is_int_prop(align
):
1406 raise ConfigParseError('Floating point number type',
1407 '"align" property must be an integer')
1409 if not _is_valid_alignment(align
):
1410 raise ConfigParseError('Floating point number type',
1411 'Invalid alignment: {}'.format(align
))
1416 if 'byte-order' in node
:
1417 byte_order
= node
['byte-order']
1419 if byte_order
is None:
1420 obj
.byte_order
= self
._bo
1422 if not _is_str_prop(byte_order
):
1423 raise ConfigParseError('Floating point number type',
1424 '"byte-order" property must be a string ("le" or "be")')
1426 byte_order
= _byte_order_str_to_bo(byte_order
)
1428 if byte_order
is None:
1429 raise ConfigParseError('Floating point number type',
1430 'Invalid "byte-order" property')
1432 obj
.byte_order
= self
._bo
1436 def _create_enum(self
, obj
, node
):
1438 # create enumeration object
1441 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1447 raise ConfigParseError('Enumeration type',
1448 'Unknown property: "{}"'.format(unk_prop
))
1451 if 'value-type' in node
:
1452 value_type_node
= node
['value-type']
1455 obj
.value_type
= self
._create
_type
(value_type_node
)
1456 except ConfigParseError
as exc
:
1457 exc
.append_ctx('Enumeration type', 'Cannot create integer type')
1461 if 'members' in node
:
1462 members_node
= node
['members']
1464 if not _is_array_prop(members_node
):
1465 raise ConfigParseError('Enumeration type',
1466 '"members" property must be an array')
1469 last_value
= obj
.last_value
1471 if last_value
is None:
1474 cur
= last_value
+ 1
1476 for index
, m_node
in enumerate(members_node
):
1477 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1478 raise ConfigParseError('Enumeration type',
1479 'Invalid member #{}: expecting a string or an associative array'.format(index
))
1481 if _is_str_prop(m_node
):
1486 unk_prop
= _get_first_unknown_prop(m_node
, [
1492 raise ConfigParseError('Enumeration type',
1493 'Unknown member object property: "{}"'.format(unk_prop
))
1495 if 'label' not in m_node
:
1496 raise ConfigParseError('Enumeration type',
1497 'Missing "label" property in member #{}'.format(index
))
1499 label
= m_node
['label']
1501 if not _is_str_prop(label
):
1502 raise ConfigParseError('Enumeration type',
1503 '"label" property of member #{} must be a string'.format(index
))
1505 if 'value' not in m_node
:
1506 raise ConfigParseError('Enumeration type',
1507 'Missing "value" property in member ("{}")'.format(label
))
1509 value
= m_node
['value']
1511 if not _is_int_prop(value
) and not _is_array_prop(value
):
1512 raise ConfigParseError('Enumeration type',
1513 'Invalid member ("{}"): expecting an integer or an array'.format(label
))
1515 if _is_int_prop(value
):
1517 value
= (value
, value
)
1520 raise ConfigParseError('Enumeration type',
1521 'Invalid member ("{}"): range must have exactly two items'.format(label
))
1527 raise ConfigParseError('Enumeration type',
1528 'Invalid member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1533 obj
.members
[label
] = value
1537 def _create_string(self
, obj
, node
):
1539 # create string object
1542 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1547 raise ConfigParseError('String type',
1548 'Unknown object property: "{}"'.format(unk_prop
))
1551 if 'encoding' in node
:
1552 encoding
= node
['encoding']
1554 if encoding
is None:
1555 obj
.reset_encoding()
1557 if not _is_str_prop(encoding
):
1558 raise ConfigParseError('String type',
1559 '"encoding" property of must be a string ("none", "ascii", or "utf-8")')
1561 encoding
= _encoding_str_to_encoding(encoding
)
1563 if encoding
is None:
1564 raise ConfigParseError('String type',
1565 'Invalid "encoding" property')
1567 obj
.encoding
= encoding
1571 def _create_struct(self
, obj
, node
):
1573 # create structure object
1576 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1582 raise ConfigParseError('Structure type',
1583 'Unknown object property: "{}"'.format(unk_prop
))
1586 if 'min-align' in node
:
1587 min_align
= node
['min-align']
1589 if min_align
is None:
1590 obj
.reset_min_align()
1592 if not _is_int_prop(min_align
):
1593 raise ConfigParseError('Structure type',
1594 '"min-align" property must be an integer')
1596 if not _is_valid_alignment(min_align
):
1597 raise ConfigParseError('Structure type',
1598 'Invalid minimum alignment: {}'.format(min_align
))
1600 obj
.min_align
= min_align
1603 if 'fields' in node
:
1604 fields
= node
['fields']
1609 if not _is_assoc_array_prop(fields
):
1610 raise ConfigParseError('Structure type',
1611 '"fields" property must be an associative array')
1613 for field_name
, field_node
in fields
.items():
1614 if not _is_valid_identifier(field_name
):
1615 raise ConfigParseError('Structure type',
1616 '"{}" is not a valid field name'.format(field_name
))
1619 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1620 except ConfigParseError
as exc
:
1621 exc
.append_ctx('Structure type',
1622 'Cannot create field "{}"'.format(field_name
))
1627 def _create_array(self
, obj
, node
):
1629 # create array object
1632 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1638 raise ConfigParseError('Array type',
1639 'Unknown property: "{}"'.format(unk_prop
))
1642 if 'length' in node
:
1643 length
= node
['length']
1645 if not _is_int_prop(length
):
1646 raise ConfigParseError('Array type',
1647 '"length" property must be an integer')
1649 if type(length
) is int and length
< 0:
1650 raise ConfigParseError('Array type',
1651 'Invalid length: {}'.format(length
))
1656 if 'element-type' in node
:
1657 element_type_node
= node
['element-type']
1660 obj
.element_type
= self
._create
_type
(node
['element-type'])
1661 except ConfigParseError
as exc
:
1662 exc
.append_ctx('Array type', 'Cannot create element type')
1667 def _create_type(self
, type_node
):
1668 if type(type_node
) is str:
1669 t
= self
._lookup
_type
_alias
(type_node
)
1672 raise ConfigParseError('Type',
1673 'Unknown type alias "{}"'.format(type_node
))
1677 if not _is_assoc_array_prop(type_node
):
1678 raise ConfigParseError('Type',
1679 'Expecting associative arrays or string (type alias name)')
1686 if self
._version
>= 200:
1687 if 'inherit' in type_node
:
1688 inherit_prop
= 'inherit'
1689 inherit_node
= type_node
[inherit_prop
]
1691 if self
._version
>= 201:
1692 if '$inherit' in type_node
:
1693 if inherit_node
is not None:
1694 raise ConfigParseError('Type',
1695 'Cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1697 inherit_prop
= '$inherit'
1698 inherit_node
= type_node
[inherit_prop
]
1700 if inherit_node
is not None and 'class' in type_node
:
1701 raise ConfigParseError('Type',
1702 'Cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1704 if inherit_node
is not None:
1705 if not _is_str_prop(inherit_node
):
1706 raise ConfigParseError('Type',
1707 '"{}" property of type object must be a string'.format(inherit_prop
))
1709 base
= self
._lookup
_type
_alias
(inherit_node
)
1712 raise ConfigParseError('Type',
1713 'Cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1715 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1717 if 'class' not in type_node
:
1718 raise ConfigParseError('Type',
1719 'Does not inherit, therefore must have a "class" property')
1721 class_name
= type_node
['class']
1723 if type(class_name
) is not str:
1724 raise ConfigParseError('Type', '"class" property must be a string')
1726 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1727 raise ConfigParseError('Type',
1728 'Unknown class "{}"'.format(class_name
))
1731 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1733 return func(base
, type_node
)
1735 def _register_type_aliases(self
, metadata_node
):
1738 if 'type-aliases' not in metadata_node
:
1741 ta_node
= metadata_node
['type-aliases']
1746 if not _is_assoc_array_prop(ta_node
):
1747 raise ConfigParseError('Metadata',
1748 '"type-aliases" property must be an associative array')
1750 for ta_name
, ta_type
in ta_node
.items():
1751 if ta_name
in self
._tas
:
1752 raise ConfigParseError('Metadata',
1753 'Duplicate type alias "{}"'.format(ta_name
))
1756 t
= self
._create
_type
(ta_type
)
1757 except ConfigParseError
as exc
:
1758 exc
.append_ctx('Metadata',
1759 'Cannot create type alias "{}"'.format(ta_name
))
1762 self
._tas
[ta_name
] = t
1764 def _create_clock(self
, node
):
1765 # create clock object
1768 if not _is_assoc_array_prop(node
):
1769 raise ConfigParseError('Metadata',
1770 'Clock objects must be associative arrays')
1782 if self
._version
>= 201:
1783 known_props
.append('$return-ctype')
1785 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1788 raise ConfigParseError('Clock',
1789 'Unknown property: "{}"'.format(unk_prop
))
1793 uuidp
= node
['uuid']
1798 if not _is_str_prop(uuidp
):
1799 raise ConfigParseError('Clock',
1800 '"uuid" property must be a string')
1803 uuidp
= uuid
.UUID(uuidp
)
1805 raise ConfigParseError('Clock', 'Malformed UUID: "{}"'.format(uuidp
))
1810 if 'description' in node
:
1811 desc
= node
['description']
1814 clock
.reset_description()
1816 if not _is_str_prop(desc
):
1817 raise ConfigParseError('Clock',
1818 '"description" property must be a string')
1820 clock
.description
= desc
1829 if not _is_int_prop(freq
):
1830 raise ConfigParseError('Clock',
1831 '"freq" property must be an integer')
1834 raise ConfigParseError('Clock',
1835 'Invalid frequency: {}'.format(freq
))
1840 if 'error-cycles' in node
:
1841 error_cycles
= node
['error-cycles']
1843 if error_cycles
is None:
1844 clock
.reset_error_cycles()
1846 if not _is_int_prop(error_cycles
):
1847 raise ConfigParseError('Clock',
1848 '"error-cycles" property must be an integer')
1850 if error_cycles
< 0:
1851 raise ConfigParseError('Clock',
1852 'Invalid error cycles: {}'.format(error_cycles
))
1854 clock
.error_cycles
= error_cycles
1857 if 'offset' in node
:
1858 offset
= node
['offset']
1861 clock
.reset_offset_seconds()
1862 clock
.reset_offset_cycles()
1864 if not _is_assoc_array_prop(offset
):
1865 raise ConfigParseError('Clock',
1866 '"offset" property must be an associative array')
1868 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1871 raise ConfigParseError('Clock',
1872 'Unknown offset property: "{}"'.format(unk_prop
))
1875 if 'cycles' in offset
:
1876 offset_cycles
= offset
['cycles']
1878 if offset_cycles
is None:
1879 clock
.reset_offset_cycles()
1881 if not _is_int_prop(offset_cycles
):
1882 raise ConfigParseError('Clock\'s "offset" property',
1883 '"cycles" property must be an integer')
1885 if offset_cycles
< 0:
1886 raise ConfigParseError('Clock\'s "offset" property',
1887 'Invalid cycles: {}'.format(offset_cycles
))
1889 clock
.offset_cycles
= offset_cycles
1892 if 'seconds' in offset
:
1893 offset_seconds
= offset
['seconds']
1895 if offset_seconds
is None:
1896 clock
.reset_offset_seconds()
1898 if not _is_int_prop(offset_seconds
):
1899 raise ConfigParseError('Clock\'s "offset" property',
1900 '"seconds" property must be an integer')
1902 if offset_seconds
< 0:
1903 raise ConfigParseError('Clock\'s "offset" property',
1904 'Invalid seconds: {}'.format(offset_seconds
))
1906 clock
.offset_seconds
= offset_seconds
1909 if 'absolute' in node
:
1910 absolute
= node
['absolute']
1912 if absolute
is None:
1913 clock
.reset_absolute()
1915 if not _is_bool_prop(absolute
):
1916 raise ConfigParseError('Clock',
1917 '"absolute" property must be a boolean')
1919 clock
.absolute
= absolute
1922 # v2.0: "return-ctype"
1923 # v2.1+: "$return-ctype"
1924 return_ctype_node
= None
1926 if self
._version
>= 200:
1927 if 'return-ctype' in node
:
1928 return_ctype_prop
= 'return-ctype'
1929 return_ctype_node
= node
[return_ctype_prop
]
1931 if self
._version
>= 201:
1932 if '$return-ctype' in node
:
1933 if return_ctype_node
is not None:
1934 raise ConfigParseError('Clock',
1935 'Cannot specify both "return-ctype" and "$return-ctype" properties: prefer "$return-ctype"')
1937 return_ctype_prop
= '$return-ctype'
1938 return_ctype_node
= node
[return_ctype_prop
]
1940 if return_ctype_node
is not None:
1941 if return_ctype_node
is None:
1942 clock
.reset_return_ctype()
1944 if not _is_str_prop(return_ctype_node
):
1945 raise ConfigParseError('Clock',
1946 '"{}" property of must be a string'.format(return_ctype_prop
))
1948 clock
.return_ctype
= return_ctype_node
1952 def _register_clocks(self
, metadata_node
):
1953 self
._clocks
= collections
.OrderedDict()
1955 if 'clocks' not in metadata_node
:
1958 clocks_node
= metadata_node
['clocks']
1960 if clocks_node
is None:
1963 if not _is_assoc_array_prop(clocks_node
):
1964 raise ConfigParseError('Metadata',
1965 '"clocks" property must be an associative array')
1967 for clock_name
, clock_node
in clocks_node
.items():
1968 if not _is_valid_identifier(clock_name
):
1969 raise ConfigParseError('Metadata',
1970 'Invalid clock name: "{}"'.format(clock_name
))
1972 if clock_name
in self
._clocks
:
1973 raise ConfigParseError('Metadata',
1974 'Duplicate clock "{}"'.format(clock_name
))
1977 clock
= self
._create
_clock
(clock_node
)
1978 except ConfigParseError
as exc
:
1979 exc
.append_ctx('Metadata',
1980 'Cannot create clock "{}"'.format(clock_name
))
1983 clock
.name
= clock_name
1984 self
._clocks
[clock_name
] = clock
1986 def _create_env(self
, metadata_node
):
1987 env
= collections
.OrderedDict()
1989 if 'env' not in metadata_node
:
1992 env_node
= metadata_node
['env']
1994 if env_node
is None:
1997 if not _is_assoc_array_prop(env_node
):
1998 raise ConfigParseError('Metadata',
1999 '"env" property must be an associative array')
2001 for env_name
, env_value
in env_node
.items():
2003 raise ConfigParseError('Metadata',
2004 'Duplicate environment variable "{}"'.format(env_name
))
2006 if not _is_valid_identifier(env_name
):
2007 raise ConfigParseError('Metadata',
2008 'Invalid environment variable name: "{}"'.format(env_name
))
2010 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
2011 raise ConfigParseError('Metadata',
2012 'Invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
2014 env
[env_name
] = env_value
2018 def _register_log_levels(self
, metadata_node
):
2019 self
._log
_levels
= dict()
2022 # v2.0: "log-levels"
2023 # v2.1+: "$log-levels"
2024 log_levels_node
= None
2026 if self
._version
>= 200:
2027 if 'log-levels' in metadata_node
:
2028 log_levels_prop
= 'log-levels'
2029 log_levels_node
= metadata_node
[log_levels_prop
]
2031 if self
._version
>= 201:
2032 if '$log-levels' in metadata_node
:
2033 if log_levels_node
is not None:
2034 raise ConfigParseError('Metadata',
2035 'Cannot specify both "log-levels" and "$log-levels" properties of metadata object: prefer "$log-levels"')
2037 log_levels_prop
= '$log-levels'
2038 log_levels_node
= metadata_node
[log_levels_prop
]
2040 if log_levels_node
is None:
2043 if not _is_assoc_array_prop(log_levels_node
):
2044 raise ConfigParseError('Metadata',
2045 '"{}" property (metadata) must be an associative array'.format(log_levels_prop
))
2047 for ll_name
, ll_value
in log_levels_node
.items():
2048 if ll_name
in self
._log
_levels
:
2049 raise ConfigParseError('"{}" property"'.format(log_levels_prop
),
2050 'Duplicate entry "{}"'.format(ll_name
))
2052 if not _is_int_prop(ll_value
):
2053 raise ConfigParseError('"{}" property"'.format(log_levels_prop
),
2054 'Invalid entry ("{}"): expecting an integer'.format(ll_name
))
2057 raise ConfigParseError('"{}" property"'.format(log_levels_prop
),
2058 'Invalid entry ("{}"): value must be positive'.format(ll_name
))
2060 self
._log
_levels
[ll_name
] = ll_value
2062 def _create_trace(self
, metadata_node
):
2063 # create trace object
2066 if 'trace' not in metadata_node
:
2067 raise ConfigParseError('Metadata', 'Missing "trace" property')
2069 trace_node
= metadata_node
['trace']
2071 if not _is_assoc_array_prop(trace_node
):
2072 raise ConfigParseError('Metadata',
2073 '"trace" property must be an associative array')
2075 unk_prop
= _get_first_unknown_prop(trace_node
, [
2078 'packet-header-type',
2082 raise ConfigParseError('Trace',
2083 'Unknown property: "{}"'.format(unk_prop
))
2085 # set byte order (already parsed)
2086 trace
.byte_order
= self
._bo
2089 if 'uuid' in trace_node
and trace_node
['uuid'] is not None:
2090 uuidp
= trace_node
['uuid']
2092 if not _is_str_prop(uuidp
):
2093 raise ConfigParseError('Trace',
2094 '"uuid" property must be a string')
2097 uuidp
= uuid
.uuid1()
2100 uuidp
= uuid
.UUID(uuidp
)
2102 raise ConfigParseError('Trace',
2103 'Malformed UUID: "{}"'.format(uuidp
))
2107 # packet header type
2108 if 'packet-header-type' in trace_node
and trace_node
['packet-header-type'] is not None:
2110 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
2111 except ConfigParseError
as exc
:
2112 exc
.append_ctx('Trace',
2113 'Cannot create packet header type')
2116 trace
.packet_header_type
= ph_type
2120 def _lookup_log_level(self
, ll
):
2121 if _is_int_prop(ll
):
2123 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
2124 return self
._log
_levels
[ll
]
2126 def _create_event(self
, event_node
):
2129 if not _is_assoc_array_prop(event_node
):
2130 raise ConfigParseError('Event', 'Expecting associative array')
2132 unk_prop
= _get_first_unknown_prop(event_node
, [
2139 raise ConfigParseError('Event',
2140 'Unknown property: "{}"'.format(unk_prop
))
2142 if 'log-level' in event_node
and event_node
['log-level'] is not None:
2143 ll_node
= event_node
['log-level']
2145 if _is_str_prop(ll_node
):
2146 ll_value
= self
._lookup
_log
_level
(event_node
['log-level'])
2148 if ll_value
is None:
2149 raise ConfigParseError('Event\'s "log-level" property',
2150 'Cannot find log level "{}"'.format(ll_node
))
2152 ll
= metadata
.LogLevel(event_node
['log-level'], ll_value
)
2153 elif _is_int_prop(ll_node
):
2155 raise ConfigParseError('Event\'s "log-level" property',
2156 'Invalid value {}: value must be positive'.format(ll_node
))
2158 ll
= metadata
.LogLevel(None, ll_node
)
2160 raise ConfigParseError('Event\'s "log-level" property',
2161 'Must be either a string or an integer')
2163 event
.log_level
= ll
2165 if 'context-type' in event_node
and event_node
['context-type'] is not None:
2166 ctx_type_node
= event_node
['context-type']
2169 t
= self
._create
_type
(event_node
['context-type'])
2170 except ConfigParseError
as exc
:
2171 exc
.append_ctx('Event',
2172 'Cannot create context type object')
2175 event
.context_type
= t
2177 if 'payload-type' in event_node
and event_node
['payload-type'] is not None:
2179 t
= self
._create
_type
(event_node
['payload-type'])
2180 except ConfigParseError
as exc
:
2181 exc
.append_ctx('Event',
2182 'Cannot create payload type object')
2185 event
.payload_type
= t
2189 def _create_stream(self
, stream_name
, stream_node
):
2192 if not _is_assoc_array_prop(stream_node
):
2193 raise ConfigParseError('Stream objects must be associative arrays')
2196 'packet-context-type',
2197 'event-header-type',
2198 'event-context-type',
2202 if self
._version
>= 202:
2203 known_props
.append('$default')
2205 unk_prop
= _get_first_unknown_prop(stream_node
, known_props
)
2210 if unk_prop
== '$default':
2211 add
= ' (use version 2.2 or greater)'
2213 raise ConfigParseError('Stream',
2214 'Unknown property{}: "{}"'.format(add
, unk_prop
))
2216 if 'packet-context-type' in stream_node
and stream_node
['packet-context-type'] is not None:
2218 t
= self
._create
_type
(stream_node
['packet-context-type'])
2219 except ConfigParseError
as exc
:
2220 exc
.append_ctx('Stream',
2221 'Cannot create packet context type object')
2224 stream
.packet_context_type
= t
2226 if 'event-header-type' in stream_node
and stream_node
['event-header-type'] is not None:
2228 t
= self
._create
_type
(stream_node
['event-header-type'])
2229 except ConfigParseError
as exc
:
2230 exc
.append_ctx('Stream',
2231 'Cannot create event header type object')
2234 stream
.event_header_type
= t
2236 if 'event-context-type' in stream_node
and stream_node
['event-context-type'] is not None:
2238 t
= self
._create
_type
(stream_node
['event-context-type'])
2239 except ConfigParseError
as exc
:
2240 exc
.append_ctx('Stream',
2241 'Cannot create event context type object')
2244 stream
.event_context_type
= t
2246 if 'events' not in stream_node
:
2247 raise ConfigParseError('Stream', 'Missing "events" property')
2249 events
= stream_node
['events']
2251 if events
is not None:
2252 if not _is_assoc_array_prop(events
):
2253 raise ConfigParseError('Stream',
2254 '"events" property must be an associative array')
2257 raise ConfigParseError('Stream', 'At least one event is needed')
2261 for ev_name
, ev_node
in events
.items():
2263 ev
= self
._create
_event
(ev_node
)
2264 except ConfigParseError
as exc
:
2265 exc
.append_ctx('Stream',
2266 'Cannot create event "{}"'.format(ev_name
))
2271 stream
.events
[ev_name
] = ev
2274 if '$default' in stream_node
and stream_node
['$default'] is not None:
2275 default_node
= stream_node
['$default']
2277 if not _is_bool_prop(default_node
):
2278 raise ConfigParseError('Stream',
2279 'Invalid "$default" property: expecting a boolean')
2282 if self
._meta
.default_stream_name
is not None and self
._meta
.default_stream_name
!= stream_name
:
2283 fmt
= 'Cannot specify more than one default stream (default stream already set to "{}")'
2284 raise ConfigParseError('Stream',
2285 fmt
.format(self
._meta
.default_stream_name
))
2287 self
._meta
.default_stream_name
= stream_name
2291 def _create_streams(self
, metadata_node
):
2292 streams
= collections
.OrderedDict()
2294 if 'streams' not in metadata_node
:
2295 raise ConfigParseError('Metadata',
2296 'Missing "streams" property')
2298 streams_node
= metadata_node
['streams']
2300 if not _is_assoc_array_prop(streams_node
):
2301 raise ConfigParseError('Metadata',
2302 '"streams" property must be an associative array')
2304 if not streams_node
:
2305 raise ConfigParseError('Metadata\'s "streams" property',
2306 'At least one stream is needed')
2310 for stream_name
, stream_node
in streams_node
.items():
2312 stream
= self
._create
_stream
(stream_name
, stream_node
)
2313 except ConfigParseError
as exc
:
2314 exc
.append_ctx('Metadata',
2315 'Cannot create stream "{}"'.format(stream_name
))
2319 stream
.name
= str(stream_name
)
2320 streams
[stream_name
] = stream
2325 def _create_metadata(self
, root
):
2326 self
._meta
= _Metadata()
2328 if 'metadata' not in root
:
2329 raise ConfigParseError('Configuration',
2330 'Missing "metadata" property')
2332 metadata_node
= root
['metadata']
2334 if not _is_assoc_array_prop(metadata_node
):
2335 raise ConfigParseError('Configuration\'s "metadata" property',
2336 'Must be an associative array')
2347 if self
._version
>= 201:
2348 known_props
.append('$log-levels')
2350 if self
._version
>= 202:
2351 known_props
.append('$default-stream')
2353 unk_prop
= _get_first_unknown_prop(metadata_node
, known_props
)
2358 if unk_prop
== '$include':
2359 add
= ' (use version 2.1 or greater)'
2361 if unk_prop
== '$default-stream':
2362 add
= ' (use version 2.2 or greater)'
2364 raise ConfigParseError('Metadata',
2365 'Unknown property{}: "{}"'.format(add
, unk_prop
))
2367 if '$default-stream' in metadata_node
and metadata_node
['$default-stream'] is not None:
2368 default_stream_node
= metadata_node
['$default-stream']
2370 if not _is_str_prop(default_stream_node
):
2371 raise ConfigParseError('Metadata\'s "$default-stream" property',
2372 'Expecting a string')
2374 self
._meta
.default_stream_name
= default_stream_node
2376 self
._set
_byte
_order
(metadata_node
)
2377 self
._register
_clocks
(metadata_node
)
2378 self
._meta
.clocks
= self
._clocks
2379 self
._register
_type
_aliases
(metadata_node
)
2380 self
._meta
.env
= self
._create
_env
(metadata_node
)
2381 self
._meta
.trace
= self
._create
_trace
(metadata_node
)
2382 self
._register
_log
_levels
(metadata_node
)
2383 self
._meta
.streams
= self
._create
_streams
(metadata_node
)
2387 validator
= _MetadataTypesHistologyValidator()
2388 validator
.validate(self
._meta
)
2389 validator
= _MetadataSpecialFieldsValidator()
2390 validator
.validate(self
._meta
)
2391 except ConfigParseError
as exc
:
2392 exc
.append_ctx('Metadata')
2396 validator
= _BarectfMetadataValidator()
2397 validator
.validate(self
._meta
)
2398 except ConfigParseError
as exc
:
2399 exc
.append_ctx('barectf metadata')
2404 def _get_version(self
, root
):
2405 if 'version' not in root
:
2406 raise ConfigParseError('Configuration',
2407 'Missing "version" property')
2409 version_node
= root
['version']
2411 if not _is_str_prop(version_node
):
2412 raise ConfigParseError('Configuration\'s "version" property',
2415 version_node
= version_node
.strip()
2417 if version_node
not in ['2.0', '2.1', '2.2']:
2418 raise ConfigParseError('Configuration',
2419 'Unsupported version ({}): versions 2.0, 2.1, and 2.2 are supported'.format(version_node
))
2421 # convert version string to comparable version integer
2422 parts
= version_node
.split('.')
2423 version
= int(parts
[0]) * 100 + int(parts
[1])
2427 def _get_prefix(self
, root
):
2428 def_prefix
= 'barectf_'
2430 if 'prefix' not in root
:
2433 prefix_node
= root
['prefix']
2435 if prefix_node
is None:
2438 if not _is_str_prop(prefix_node
):
2439 raise ConfigParseError('Configuration\'s "prefix" property',
2442 if not _is_valid_identifier(prefix_node
):
2443 raise ConfigParseError('Configuration\'s "prefix" property',
2444 'Must be a valid C identifier')
2448 def _get_options(self
, root
):
2449 if 'options' not in root
:
2450 return config
.ConfigOptions()
2452 options_node
= root
['options']
2454 if not _is_assoc_array_prop(options_node
):
2455 raise ConfigParseError('Configuration\'s "options" property',
2456 'Must be an associative array')
2460 'gen-default-stream-def',
2462 unk_prop
= _get_first_unknown_prop(options_node
, known_props
)
2465 raise ConfigParseError('Configuration\'s "options" property',
2466 'Unknown property: "{}"'.format(unk_prop
))
2468 gen_prefix_def
= None
2470 if 'gen-prefix-def' in options_node
and options_node
['gen-prefix-def'] is not None:
2471 gen_prefix_def_node
= options_node
['gen-prefix-def']
2473 if not _is_bool_prop(gen_prefix_def_node
):
2474 raise ConfigParseError('Configuration\'s "options" property',
2475 'Invalid option "gen-prefix-def": expecting a boolean')
2477 gen_prefix_def
= gen_prefix_def_node
2479 gen_default_stream_def
= None
2481 if 'gen-default-stream-def' in options_node
and options_node
['gen-default-stream-def'] is not None:
2482 gen_default_stream_def_node
= options_node
['gen-default-stream-def']
2484 if not _is_bool_prop(gen_default_stream_def_node
):
2485 raise ConfigParseError('Configuration\'s "options" property',
2486 'Invalid option "gen-default-stream-def": expecting a boolean')
2488 gen_default_stream_def
= gen_default_stream_def_node
2490 return config
.ConfigOptions(gen_prefix_def
, gen_default_stream_def
)
2492 def _get_last_include_file(self
):
2493 if self
._include
_stack
:
2494 return self
._include
_stack
[-1]
2496 return self
._root
_yaml
_path
2498 def _load_include(self
, yaml_path
):
2499 for inc_dir
in self
._include
_dirs
:
2500 # current include dir + file name path
2501 # note: os.path.join() only takes the last arg if it's absolute
2502 inc_path
= os
.path
.join(inc_dir
, yaml_path
)
2504 # real path (symbolic links resolved)
2505 real_path
= os
.path
.realpath(inc_path
)
2507 # normalized path (weird stuff removed!)
2508 norm_path
= os
.path
.normpath(real_path
)
2510 if not os
.path
.isfile(norm_path
):
2511 # file does not exist: skip
2514 if norm_path
in self
._include
_stack
:
2515 base_path
= self
._get
_last
_include
_file
()
2516 raise ConfigParseError('In "{}"',
2517 'Cannot recursively include file "{}"'.format(base_path
, norm_path
))
2519 self
._include
_stack
.append(norm_path
)
2522 return self
._yaml
_ordered
_load
(norm_path
)
2524 if not self
._ignore
_include
_not
_found
:
2525 base_path
= self
._get
_last
_include
_file
()
2526 raise ConfigParseError('In "{}"',
2527 'Cannot include file "{}": file not found in include directories'.format(base_path
, yaml_path
))
2531 def _get_include_paths(self
, include_node
):
2532 if include_node
is None:
2535 if _is_str_prop(include_node
):
2536 return [include_node
]
2538 if _is_array_prop(include_node
):
2539 for include_path
in include_node
:
2540 if not _is_str_prop(include_path
):
2541 raise ConfigParseError('"$include" property',
2542 'Expecting array of strings')
2546 raise ConfigParseError('"$include" property',
2547 'Expecting string or array of strings')
2549 def _update_node(self
, base_node
, overlay_node
):
2550 for olay_key
, olay_value
in overlay_node
.items():
2551 if olay_key
in base_node
:
2552 base_value
= base_node
[olay_key
]
2554 if _is_assoc_array_prop(olay_value
) and _is_assoc_array_prop(base_value
):
2555 # merge dictionaries
2556 self
._update
_node
(base_value
, olay_value
)
2557 elif _is_array_prop(olay_value
) and _is_array_prop(base_value
):
2558 # append extension array items to base items
2559 base_value
+= olay_value
2561 # fall back to replacing
2562 base_node
[olay_key
] = olay_value
2564 base_node
[olay_key
] = olay_value
2566 def _process_node_include(self
, last_overlay_node
, name
,
2567 process_base_include_cb
,
2568 process_children_include_cb
=None):
2569 if not _is_assoc_array_prop(last_overlay_node
):
2570 raise ConfigParseError('"$include" property',
2571 '{} objects must be associative arrays'.format(name
))
2573 # process children inclusions first
2574 if process_children_include_cb
:
2575 process_children_include_cb(last_overlay_node
)
2577 if '$include' in last_overlay_node
:
2578 include_node
= last_overlay_node
['$include']
2581 return last_overlay_node
2583 include_paths
= self
._get
_include
_paths
(include_node
)
2584 cur_base_path
= self
._get
_last
_include
_file
()
2587 # keep the include paths and remove the include property
2588 include_paths
= copy
.deepcopy(include_paths
)
2589 del last_overlay_node
['$include']
2591 for include_path
in include_paths
:
2592 # load raw YAML from included file
2593 overlay_node
= self
._load
_include
(include_path
)
2595 if overlay_node
is None:
2596 # cannot find include file, but we're ignoring those
2597 # errors, otherwise _load_include() itself raises
2601 # recursively process includes
2603 overlay_node
= process_base_include_cb(overlay_node
)
2604 except ConfigParseError
as exc
:
2605 exc
.append_ctx('In "{}"'.format(cur_base_path
))
2608 # pop include stack now that we're done including
2609 del self
._include
_stack
[-1]
2611 # at this point, base_node is fully resolved (does not
2612 # contain any include property)
2613 if base_node
is None:
2614 base_node
= overlay_node
2616 self
._update
_node
(base_node
, overlay_node
)
2618 # finally, we update the latest base node with our last overlay
2620 if base_node
is None:
2621 # nothing was included, which is possible when we're
2622 # ignoring include errors
2623 return last_overlay_node
2625 self
._update
_node
(base_node
, last_overlay_node
)
2629 def _process_event_include(self
, event_node
):
2630 return self
._process
_node
_include
(event_node
, 'event',
2631 self
._process
_event
_include
)
2633 def _process_stream_include(self
, stream_node
):
2634 def process_children_include(stream_node
):
2635 if 'events' in stream_node
:
2636 events_node
= stream_node
['events']
2638 if not _is_assoc_array_prop(events_node
):
2639 raise ConfigParseError('"$include" property',
2640 '"events" property must be an associative array')
2642 events_node_keys
= list(events_node
.keys())
2644 for key
in events_node_keys
:
2645 event_node
= events_node
[key
]
2648 events_node
[key
] = self
._process
_event
_include
(event_node
)
2649 except ConfigParseError
as exc
:
2650 exc
.append_ctx('"$include" property',
2651 'Cannot process includes of event object "{}"'.format(key
))
2654 return self
._process
_node
_include
(stream_node
, 'stream',
2655 self
._process
_stream
_include
,
2656 process_children_include
)
2658 def _process_trace_include(self
, trace_node
):
2659 return self
._process
_node
_include
(trace_node
, 'trace',
2660 self
._process
_trace
_include
)
2662 def _process_clock_include(self
, clock_node
):
2663 return self
._process
_node
_include
(clock_node
, 'clock',
2664 self
._process
_clock
_include
)
2666 def _process_metadata_include(self
, metadata_node
):
2667 def process_children_include(metadata_node
):
2668 if 'trace' in metadata_node
:
2669 metadata_node
['trace'] = self
._process
_trace
_include
(metadata_node
['trace'])
2671 if 'clocks' in metadata_node
:
2672 clocks_node
= metadata_node
['clocks']
2674 if not _is_assoc_array_prop(clocks_node
):
2675 raise ConfigParseError('"$include" property',
2676 '"clocks" property must be an associative array')
2678 clocks_node_keys
= list(clocks_node
.keys())
2680 for key
in clocks_node_keys
:
2681 clock_node
= clocks_node
[key
]
2684 clocks_node
[key
] = self
._process
_clock
_include
(clock_node
)
2685 except ConfigParseError
as exc
:
2686 exc
.append_ctx('"$include" property',
2687 'Cannot process includes of clock object "{}"'.format(key
))
2690 if 'streams' in metadata_node
:
2691 streams_node
= metadata_node
['streams']
2693 if not _is_assoc_array_prop(streams_node
):
2694 raise ConfigParseError('"$include" property',
2695 '"streams" property must be an associative array')
2697 streams_node_keys
= list(streams_node
.keys())
2699 for key
in streams_node_keys
:
2700 stream_node
= streams_node
[key
]
2703 streams_node
[key
] = self
._process
_stream
_include
(stream_node
)
2704 except ConfigParseError
as exc
:
2705 exc
.append_ctx('"$include" property',
2706 'Cannot process includes of stream object "{}"'.format(key
))
2709 return self
._process
_node
_include
(metadata_node
, 'metadata',
2710 self
._process
_metadata
_include
,
2711 process_children_include
)
2713 def _process_root_includes(self
, root
):
2714 # The following config objects support includes:
2721 # We need to process the event includes first, then the stream
2722 # includes, then the trace includes, and finally the metadata
2725 # In each object, only one of the $include and $include-replace
2726 # special properties is allowed.
2728 # We keep a stack of absolute paths to included files to detect
2730 if 'metadata' in root
:
2731 root
['metadata'] = self
._process
_metadata
_include
(root
['metadata'])
2735 def _yaml_ordered_dump(self
, node
, **kwds
):
2736 class ODumper(yaml
.Dumper
):
2739 def dict_representer(dumper
, node
):
2740 return dumper
.represent_mapping(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2743 ODumper
.add_representer(collections
.OrderedDict
, dict_representer
)
2745 return yaml
.dump(node
, Dumper
=ODumper
, **kwds
)
2747 def _yaml_ordered_load(self
, yaml_path
):
2748 class OLoader(yaml
.Loader
):
2751 def construct_mapping(loader
, node
):
2752 loader
.flatten_mapping(node
)
2754 return collections
.OrderedDict(loader
.construct_pairs(node
))
2756 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2761 with
open(yaml_path
, 'r') as f
:
2762 node
= yaml
.load(f
, OLoader
)
2763 except (OSError, IOError) as e
:
2764 raise ConfigParseError('Configuration',
2765 'Cannot open file "{}"'.format(yaml_path
))
2766 except ConfigParseError
as exc
:
2767 exc
.append_ctx('Configuration',
2768 'Unknown error while trying to load file "{}"'.format(yaml_path
))
2771 # loaded node must be an associate array
2772 if not _is_assoc_array_prop(node
):
2773 raise ConfigParseError('Configuration',
2774 'Root of YAML file "{}" must be an associative array'.format(yaml_path
))
2779 self
._version
= None
2780 self
._include
_stack
= []
2782 def parse(self
, yaml_path
):
2784 self
._root
_yaml
_path
= yaml_path
2787 root
= self
._yaml
_ordered
_load
(yaml_path
)
2788 except ConfigParseError
as exc
:
2789 exc
.append_ctx('Configuration',
2790 'Cannot parse YAML file "{}"'.format(yaml_path
))
2793 if not _is_assoc_array_prop(root
):
2794 raise ConfigParseError('Configuration',
2795 'Must be an associative array')
2797 # get the config version
2798 self
._version
= self
._get
_version
(root
)
2806 if self
._version
>= 202:
2807 known_props
.append('options')
2809 unk_prop
= _get_first_unknown_prop(root
, known_props
)
2814 if unk_prop
== 'options':
2815 add
= ' (use version 2.2 or greater)'
2817 raise ConfigParseError('Configuration',
2818 'Unknown property{}: "{}"'.format(add
, unk_prop
))
2820 # process includes if supported
2821 if self
._version
>= 201:
2822 root
= self
._process
_root
_includes
(root
)
2824 # dump config if required
2825 if self
._dump
_config
:
2826 print(self
._yaml
_ordered
_dump
(root
, indent
=2,
2827 default_flow_style
=False))
2829 # get prefix and metadata
2830 prefix
= self
._get
_prefix
(root
)
2831 meta
= self
._create
_metadata
(root
)
2832 opts
= self
._get
_options
(root
)
2834 return config
.Config(meta
.to_public(), prefix
, opts
)
2837 def _from_file(path
, include_dirs
, ignore_include_not_found
, dump_config
):
2839 parser
= _YamlConfigParser(include_dirs
, ignore_include_not_found
,
2841 return parser
.parse(path
)
2842 except ConfigParseError
as exc
:
2843 exc
.append_ctx('Configuration',
2844 'Cannot create configuration from YAML file "{}"'.format(path
))