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
35 class _ConfigErrorCtx
:
36 def __init__(self
, name
, msg
=None):
49 class ConfigError(RuntimeError):
50 def __init__(self
, init_ctx_name
, init_ctx_msg
=None):
52 self
.append_ctx(init_ctx_name
, init_ctx_msg
)
58 def append_ctx(self
, name
, msg
=None):
59 self
._ctx
.append(_ConfigErrorCtx(name
, msg
))
63 def __init__(self
, version
, prefix
, metadata
, options
):
65 self
.version
= version
66 self
.metadata
= metadata
67 self
.options
= options
69 def _validate_metadata(self
, meta
):
71 validator
= _MetadataTypesHistologyValidator()
72 validator
.validate(meta
)
73 validator
= _MetadataSpecialFieldsValidator()
74 validator
.validate(meta
)
75 except ConfigError
as exc
:
76 exc
.append_ctx('Metadata')
80 validator
= _BarectfMetadataValidator()
81 validator
.validate(meta
)
82 except ConfigError
as exc
:
83 exc
.append_ctx('barectf metadata')
86 def _augment_metadata_env(self
, meta
):
87 version_tuple
= barectf
.get_version_tuple()
90 'tracer_name': 'barectf',
91 'tracer_major': version_tuple
[0],
92 'tracer_minor': version_tuple
[1],
93 'tracer_patch': version_tuple
[2],
94 'barectf_gen_date': str(datetime
.datetime
.now().isoformat()),
97 base_env
.update(meta
.env
)
105 def version(self
, value
):
106 self
._version
= value
110 return self
._metadata
113 def metadata(self
, value
):
114 self
._validate
_metadata
(value
)
115 self
._augment
_metadata
_env
(value
)
116 self
._metadata
= value
123 def prefix(self
, value
):
124 if not _is_valid_identifier(value
):
125 raise ConfigError('Configuration',
126 'Prefix must be a valid C identifier')
135 def options(self
, options
):
136 self
._options
= options
141 self
._gen
_prefix
_def
= False
142 self
._gen
_default
_stream
_def
= False
145 def gen_prefix_def(self
):
146 return self
._gen
_prefix
_def
148 @gen_prefix_def.setter
149 def gen_prefix_def(self
, value
):
150 self
._gen
_prefix
_def
= value
153 def gen_default_stream_def(self
):
154 return self
._gen
_default
_stream
_def
156 @gen_default_stream_def.setter
157 def gen_default_stream_def(self
, value
):
158 self
._gen
_default
_stream
_def
= value
161 def _is_assoc_array_prop(node
):
162 return isinstance(node
, dict)
165 def _is_array_prop(node
):
166 return isinstance(node
, list)
169 def _is_int_prop(node
):
170 return type(node
) is int
173 def _is_str_prop(node
):
174 return type(node
) is str
177 def _is_bool_prop(node
):
178 return type(node
) is bool
181 def _is_valid_alignment(align
):
182 return ((align
& (align
- 1)) == 0) and align
> 0
185 def _byte_order_str_to_bo(bo_str
):
186 bo_str
= bo_str
.lower()
189 return metadata
.ByteOrder
.LE
191 return metadata
.ByteOrder
.BE
194 def _encoding_str_to_encoding(encoding_str
):
195 encoding_str
= encoding_str
.lower()
197 if encoding_str
== 'utf-8' or encoding_str
== 'utf8':
198 return metadata
.Encoding
.UTF8
199 elif encoding_str
== 'ascii':
200 return metadata
.Encoding
.ASCII
201 elif encoding_str
== 'none':
202 return metadata
.Encoding
.NONE
205 _re_iden
= re
.compile(r
'^[a-zA-Z][a-zA-Z0-9_]*$')
206 _ctf_keywords
= set([
225 def _is_valid_identifier(iden
):
226 if not _re_iden
.match(iden
):
229 if _re_iden
in _ctf_keywords
:
235 def _get_first_unknown_prop(node
, known_props
):
236 for prop_name
in node
:
237 if prop_name
in known_props
:
243 # This validator validates the configured metadata for barectf specific
248 # * all header/contexts are at least byte-aligned
249 # * all integer and floating point number sizes to be <= 64
250 # * no inner structures or arrays
251 class _BarectfMetadataValidator
:
253 self
._type
_to
_validate
_type
_func
= {
254 metadata
.Integer
: self
._validate
_int
_type
,
255 metadata
.FloatingPoint
: self
._validate
_float
_type
,
256 metadata
.Enum
: self
._validate
_enum
_type
,
257 metadata
.String
: self
._validate
_string
_type
,
258 metadata
.Struct
: self
._validate
_struct
_type
,
259 metadata
.Array
: self
._validate
_array
_type
,
262 def _validate_int_type(self
, t
, entity_root
):
264 raise ConfigError('Integer type', 'Size must be lesser than or equal to 64 bits')
266 def _validate_float_type(self
, t
, entity_root
):
268 raise ConfigError('Floating point number type', 'Size must be lesser than or equal to 64 bits')
270 def _validate_enum_type(self
, t
, entity_root
):
271 if t
.value_type
.size
> 64:
272 raise ConfigError('Enumeration type', 'Integer type\'s size must be lesser than or equal to 64 bits')
274 def _validate_string_type(self
, t
, entity_root
):
277 def _validate_struct_type(self
, t
, entity_root
):
279 raise ConfigError('Structure type', 'Inner structure types are not supported as of this version')
281 for field_name
, field_type
in t
.fields
.items():
282 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
283 if field_name
== 'uuid':
288 self
._validate
_type
(field_type
, False)
289 except ConfigError
as exc
:
290 exc
.append_ctx('Structure type\' field "{}"'.format(field_name
))
293 def _validate_array_type(self
, t
, entity_root
):
294 raise ConfigError('Array type', 'Not supported as of this version')
296 def _validate_type(self
, t
, entity_root
):
297 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
299 def _validate_entity(self
, t
):
303 # make sure entity is byte-aligned
305 raise ConfigError('Root type', 'Alignment must be at least byte-aligned')
307 # make sure entity is a structure
308 if type(t
) is not metadata
.Struct
:
309 raise ConfigError('Root type', 'Expecting a structure type')
312 self
._validate
_type
(t
, True)
314 def _validate_entities_and_names(self
, meta
):
315 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
318 self
._validate
_entity
(meta
.trace
.packet_header_type
)
319 except ConfigError
as exc
:
320 exc
.append_ctx('Trace', 'Invalid packet header type')
323 for stream_name
, stream
in meta
.streams
.items():
324 if not _is_valid_identifier(stream_name
):
325 raise ConfigError('Trace', 'Stream name "{}" is not a valid C identifier'.format(stream_name
))
327 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
330 self
._validate
_entity
(stream
.packet_context_type
)
331 except ConfigError
as exc
:
332 exc
.append_ctx('Stream "{}"'.format(stream_name
),
333 'Invalid packet context type')
336 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
339 self
._validate
_entity
(stream
.event_header_type
)
340 except ConfigError
as exc
:
341 exc
.append_ctx('Stream "{}"'.format(stream_name
),
342 'Invalid event header type')
345 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
348 self
._validate
_entity
(stream
.event_context_type
)
349 except ConfigError
as exc
:
350 exc
.append_ctx('Stream "{}"'.format(stream_name
),
351 'Invalid event context type'.format(stream_name
))
355 for ev_name
, ev
in stream
.events
.items():
356 if not _is_valid_identifier(ev_name
):
357 raise ConfigError('Stream "{}"'.format(stream_name
),
358 'Event name "{}" is not a valid C identifier'.format(ev_name
))
360 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
363 self
._validate
_entity
(ev
.context_type
)
364 except ConfigError
as exc
:
365 exc
.append_ctx('Event "{}"'.format(ev_name
),
366 'Invalid context type')
369 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
372 self
._validate
_entity
(ev
.payload_type
)
373 except ConfigError
as exc
:
374 exc
.append_ctx('Event "{}"'.format(ev_name
),
375 'Invalid payload type')
378 if stream
.is_event_empty(ev
):
379 raise ConfigError('Event "{}"'.format(ev_name
), 'Empty')
380 except ConfigError
as exc
:
381 exc
.append_ctx('Stream "{}"'.format(stream_name
))
384 def _validate_default_stream(self
, meta
):
385 if meta
.default_stream_name
:
386 if meta
.default_stream_name
not in meta
.streams
.keys():
387 raise ConfigError('barectf metadata', 'Default stream name ("{}") does not exist'.format(meta
.default_stream_name
))
389 def validate(self
, meta
):
390 self
._validate
_entities
_and
_names
(meta
)
391 self
._validate
_default
_stream
(meta
)
394 # This validator validates special fields of trace, stream, and event
395 # types. For example, if checks that the "stream_id" field exists in the
396 # trace packet header if there's more than one stream, and much more.
397 class _MetadataSpecialFieldsValidator
:
398 def _validate_trace_packet_header_type(self
, t
):
399 # needs "stream_id" field?
400 if len(self
._meta
.streams
) > 1:
403 raise ConfigError('"packet-header-type" property',
404 'Need "stream_id" field (more than one stream), but trace packet header type is missing')
406 if type(t
) is not metadata
.Struct
:
407 raise ConfigError('"packet-header-type" property',
408 'Need "stream_id" field (more than one stream), but trace packet header type is not a structure type')
410 if 'stream_id' not in t
.fields
:
411 raise ConfigError('"packet-header-type" property',
412 'Need "stream_id" field (more than one stream)')
414 # validate "magic" and "stream_id" types
415 if type(t
) is not metadata
.Struct
:
418 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
419 if field_name
== 'magic':
420 if type(field_type
) is not metadata
.Integer
:
421 raise ConfigError('"packet-header-type" property',
422 '"magic" field must be an integer type')
424 if field_type
.signed
or field_type
.size
!= 32:
425 raise ConfigError('"packet-header-type" property',
426 '"magic" field must be a 32-bit unsigned integer type')
429 raise ConfigError('"packet-header-type" property',
430 '"magic" field must be the first trace packet header type\'s field')
431 elif field_name
== 'stream_id':
432 if type(field_type
) is not metadata
.Integer
:
433 raise ConfigError('"packet-header-type" property',
434 '"stream_id" field must be an integer type')
436 if field_type
.signed
:
437 raise ConfigError('"packet-header-type" property',
438 '"stream_id" field must be an unsigned integer type')
440 # "id" size can fit all event IDs
441 if len(self
._meta
.streams
) > (1 << field_type
.size
):
442 raise ConfigError('"packet-header-type" property',
443 '"stream_id" field\' size is too small for the number of trace streams')
444 elif field_name
== 'uuid':
445 if self
._meta
.trace
.uuid
is None:
446 raise ConfigError('"packet-header-type" property',
447 '"uuid" field specified, but no trace UUID provided')
449 if type(field_type
) is not metadata
.Array
:
450 raise ConfigError('"packet-header-type" property',
451 '"uuid" field must be an array')
453 if field_type
.length
!= 16:
454 raise ConfigError('"packet-header-type" property',
455 '"uuid" field must be an array of 16 bytes')
457 element_type
= field_type
.element_type
459 if type(element_type
) is not metadata
.Integer
:
460 raise ConfigError('"packet-header-type" property',
461 '"uuid" field must be an array of 16 unsigned bytes')
463 if element_type
.size
!= 8:
464 raise ConfigError('"packet-header-type" property',
465 '"uuid" field must be an array of 16 unsigned bytes')
467 if element_type
.signed
:
468 raise ConfigError('"packet-header-type" property',
469 '"uuid" field must be an array of 16 unsigned bytes')
471 if element_type
.align
!= 8:
472 raise ConfigError('"packet-header-type" property',
473 '"uuid" field must be an array of 16 unsigned, byte-aligned bytes')
475 def _validate_trace(self
, meta
):
476 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
478 def _validate_stream_packet_context(self
, stream
):
479 t
= stream
.packet_context_type
482 raise ConfigError('Stream',
483 'Missing "packet-context-type" property')
485 if type(t
) is not metadata
.Struct
:
486 raise ConfigError('"packet-context-type" property',
487 'Expecting a structure type')
489 # "timestamp_begin", if exists, is an unsigned integer type,
493 if 'timestamp_begin' in t
.fields
:
494 ts_begin
= t
.fields
['timestamp_begin']
496 if type(ts_begin
) is not metadata
.Integer
:
497 raise ConfigError('"packet-context-type" property',
498 '"timestamp_begin" field must be an integer type')
501 raise ConfigError('"packet-context-type" property',
502 '"timestamp_begin" field must be an unsigned integer type')
504 if not ts_begin
.property_mappings
:
505 raise ConfigError('"packet-context-type" property',
506 '"timestamp_begin" field must be mapped to a clock')
508 # "timestamp_end", if exists, is an unsigned integer type,
512 if 'timestamp_end' in t
.fields
:
513 ts_end
= t
.fields
['timestamp_end']
515 if type(ts_end
) is not metadata
.Integer
:
516 raise ConfigError('"packet-context-type" property',
517 '"timestamp_end" field must be an integer type')
520 raise ConfigError('"packet-context-type" property',
521 '"timestamp_end" field must be an unsigned integer type')
523 if not ts_end
.property_mappings
:
524 raise ConfigError('"packet-context-type" property',
525 '"timestamp_end" field must be mapped to a clock')
527 # "timestamp_begin" and "timestamp_end" exist together
528 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
529 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
531 # "timestamp_begin" and "timestamp_end" are mapped to the same clock
532 if ts_begin
is not None and ts_end
is not None:
533 if ts_begin
.property_mappings
[0].object.name
!= ts_end
.property_mappings
[0].object.name
:
534 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be mapped to the same clock object in stream packet context type')
536 # "events_discarded", if exists, is an unsigned integer type
537 if 'events_discarded' in t
.fields
:
538 events_discarded
= t
.fields
['events_discarded']
540 if type(events_discarded
) is not metadata
.Integer
:
541 raise ConfigError('"packet-context-type" property',
542 '"events_discarded" field must be an integer type')
544 if events_discarded
.signed
:
545 raise ConfigError('"packet-context-type" property',
546 '"events_discarded" field must be an unsigned integer type')
548 # "packet_size" and "content_size" must exist
549 if 'packet_size' not in t
.fields
:
550 raise ConfigError('"packet-context-type" property',
551 'Missing "packet_size" field in stream packet context type')
553 packet_size
= t
.fields
['packet_size']
555 # "content_size" and "content_size" must exist
556 if 'content_size' not in t
.fields
:
557 raise ConfigError('"packet-context-type" property',
558 'Missing "content_size" field in stream packet context type')
560 content_size
= t
.fields
['content_size']
562 # "packet_size" is an unsigned integer type
563 if type(packet_size
) is not metadata
.Integer
:
564 raise ConfigError('"packet-context-type" property',
565 '"packet_size" field in stream packet context type must be an integer type')
567 if packet_size
.signed
:
568 raise ConfigError('"packet-context-type" property',
569 '"packet_size" field in stream packet context type must be an unsigned integer type')
571 # "content_size" is an unsigned integer type
572 if type(content_size
) is not metadata
.Integer
:
573 raise ConfigError('"packet-context-type" property',
574 '"content_size" field in stream packet context type must be an integer type')
576 if content_size
.signed
:
577 raise ConfigError('"packet-context-type" property',
578 '"content_size" field in stream packet context type must be an unsigned integer type')
580 # "packet_size" size should be greater than or equal to "content_size" size
581 if content_size
.size
> packet_size
.size
:
582 raise ConfigError('"packet-context-type" property',
583 '"content_size" field size must be lesser than or equal to "packet_size" field size')
585 def _validate_stream_event_header(self
, stream
):
586 t
= stream
.event_header_type
589 if len(stream
.events
) > 1:
592 raise ConfigError('"event-header-type" property',
593 'Need "id" field (more than one event), but stream event header type is missing')
595 if type(t
) is not metadata
.Struct
:
596 raise ConfigError('"event-header-type" property',
597 'Need "id" field (more than one event), but stream event header type is not a structure type')
599 if 'id' not in t
.fields
:
600 raise ConfigError('"event-header-type" property',
601 'Need "id" field (more than one event)')
603 # validate "id" and "timestamp" types
604 if type(t
) is not metadata
.Struct
:
607 # "timestamp", if exists, is an unsigned integer type,
609 if 'timestamp' in t
.fields
:
610 ts
= t
.fields
['timestamp']
612 if type(ts
) is not metadata
.Integer
:
613 raise ConfigError('"event-header-type" property',
614 '"timestamp" field must be an integer type')
617 raise ConfigError('"event-header-type" property',
618 '"timestamp" field must be an unsigned integer type')
620 if not ts
.property_mappings
:
621 raise ConfigError('"event-header-type" property',
622 '"timestamp" field must be mapped to a clock')
627 # "id" is an unsigned integer type
628 if type(eid
) is not metadata
.Integer
:
629 raise ConfigError('"event-header-type" property',
630 '"id" field must be an integer type')
633 raise ConfigError('"event-header-type" property',
634 '"id" field must be an unsigned integer type')
636 # "id" size can fit all event IDs
637 if len(stream
.events
) > (1 << eid
.size
):
638 raise ConfigError('"event-header-type" property',
639 '"id" field\' size is too small for the number of stream events')
641 def _validate_stream(self
, stream
):
642 self
._validate
_stream
_packet
_context
(stream
)
643 self
._validate
_stream
_event
_header
(stream
)
645 def validate(self
, meta
):
647 self
._validate
_trace
(meta
)
649 for stream
in meta
.streams
.values():
651 self
._validate
_stream
(stream
)
652 except ConfigError
as exc
:
653 exc
.append_ctx('Stream "{}"'.format(stream
.name
), 'Invalid')
657 # Entities. Order of values is important here.
659 class _Entity(enum
.IntEnum
):
660 TRACE_PACKET_HEADER
= 0
661 STREAM_PACKET_CONTEXT
= 1
662 STREAM_EVENT_HEADER
= 2
663 STREAM_EVENT_CONTEXT
= 3
668 # Since type inheritance allows types to be only partially defined at
669 # any place in the configuration, this validator validates that actual
670 # trace, stream, and event types are all complete and valid. Therefore
671 # an invalid, but unusued type alias is accepted.
672 class _MetadataTypesHistologyValidator
:
674 self
._type
_to
_validate
_type
_histology
_func
= {
675 metadata
.Integer
: self
._validate
_integer
_histology
,
676 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
677 metadata
.Enum
: self
._validate
_enum
_histology
,
678 metadata
.String
: self
._validate
_string
_histology
,
679 metadata
.Struct
: self
._validate
_struct
_histology
,
680 metadata
.Array
: self
._validate
_array
_histology
,
683 def _validate_integer_histology(self
, t
):
686 raise ConfigError('Integer type', 'Missing size')
688 def _validate_float_histology(self
, t
):
689 # exponent digits is set
690 if t
.exp_size
is None:
691 raise ConfigError('Floating point number type',
692 'Missing exponent size')
694 # mantissa digits is set
695 if t
.mant_size
is None:
696 raise ConfigError('Floating point number type',
697 'Missing mantissa size')
699 # exponent and mantissa sum is a multiple of 8
700 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
701 raise ConfigError('Floating point number type',
702 'Mantissa and exponent sizes sum must be a multiple of 8')
704 def _validate_enum_histology(self
, t
):
705 # integer type is set
706 if t
.value_type
is None:
707 raise ConfigError('Enumeration type', 'Missing value type')
709 # there's at least one member
711 raise ConfigError('Enumeration type', 'At least one member required')
713 # no overlapping values and all values are valid considering
717 if t
.value_type
.signed
:
718 value_min
= -(1 << t
.value_type
.size
- 1)
719 value_max
= (1 << (t
.value_type
.size
- 1)) - 1
722 value_max
= (1 << t
.value_type
.size
) - 1
724 for label
, value
in t
.members
.items():
726 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
727 raise ConfigError('Enumeration type\'s member "{}"',
728 'Overlaps another member'.format(label
))
730 name_fmt
= 'Enumeration type\'s member "{}"'
731 msg_fmt
= 'Value {} is outside the value type range [{}, {}]'
733 if value
[0] < value_min
or value
[0] > value_max
:
734 raise ConfigError(name_fmt
.format(label
),
735 msg_fmt
.format(value
[0], value_min
, value_max
))
737 if value
[1] < value_min
or value
[1] > value_max
:
738 raise ConfigError(name_fmt
.format(label
),
739 msg_fmt
.format(value
[0], value_min
, value_max
))
743 def _validate_string_histology(self
, t
):
747 def _validate_struct_histology(self
, t
):
748 # all fields are valid
749 for field_name
, field_type
in t
.fields
.items():
751 self
._validate
_type
_histology
(field_type
)
752 except ConfigError
as exc
:
753 exc
.append_ctx('Structure type\'s field "{}"'.format(field_name
))
756 def _validate_array_histology(self
, t
):
759 raise ConfigError('Array type', 'Missing length')
761 # element type is set
762 if t
.element_type
is None:
763 raise ConfigError('Array type', 'Missing element type')
765 # element type is valid
767 self
._validate
_type
_histology
(t
.element_type
)
768 except ConfigError
as exc
:
769 exc
.append_ctx('Array type', 'Invalid element type')
772 def _validate_type_histology(self
, t
):
776 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
778 def _validate_entity_type_histology(self
, t
):
782 if type(t
) is not metadata
.Struct
:
783 raise ConfigError('Root type', 'Expecting a structure type')
785 self
._validate
_type
_histology
(t
)
787 def _validate_event_types_histology(self
, ev
):
790 # validate event context type
792 self
._validate
_entity
_type
_histology
(ev
.context_type
)
793 except ConfigError
as exc
:
794 exc
.append_ctx('Event "{}"'.format(ev
.name
),
795 'Invalid context type')
798 # validate event payload type
800 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
801 except ConfigError
as exc
:
802 exc
.append_ctx('Event "{}"'.format(ev
.name
),
803 'Invalid payload type')
806 def _validate_stream_types_histology(self
, stream
):
807 stream_name
= stream
.name
809 # validate stream packet context type
811 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
812 except ConfigError
as exc
:
813 exc
.append_ctx('Stream "{}"'.format(stream_name
),
814 'Invalid packet context type')
817 # validate stream event header type
819 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
820 except ConfigError
as exc
:
821 exc
.append_ctx('Stream "{}"'.format(stream_name
),
822 'Invalid event header type')
825 # validate stream event context type
827 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
828 except ConfigError
as exc
:
829 exc
.append_ctx('Stream "{}"'.format(stream_name
),
830 'Invalid event context type')
834 for ev
in stream
.events
.values():
836 self
._validate
_event
_types
_histology
(ev
)
837 except ConfigError
as exc
:
838 exc
.append_ctx('Stream "{}"'.format(stream_name
),
842 def validate(self
, meta
):
843 # validate trace packet header type
845 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
846 except ConfigError
as exc
:
847 exc
.append_ctx('Metadata\'s trace', 'Invalid packet header type')
851 for stream
in meta
.streams
.values():
852 self
._validate
_stream
_types
_histology
(stream
)
855 class _YamlConfigParser
:
856 def __init__(self
, include_dirs
, ignore_include_not_found
, dump_config
):
857 self
._class
_name
_to
_create
_type
_func
= {
858 'int': self
._create
_integer
,
859 'integer': self
._create
_integer
,
860 'flt': self
._create
_float
,
861 'float': self
._create
_float
,
862 'floating-point': self
._create
_float
,
863 'enum': self
._create
_enum
,
864 'enumeration': self
._create
_enum
,
865 'str': self
._create
_string
,
866 'string': self
._create
_string
,
867 'struct': self
._create
_struct
,
868 'structure': self
._create
_struct
,
869 'array': self
._create
_array
,
871 self
._type
_to
_create
_type
_func
= {
872 metadata
.Integer
: self
._create
_integer
,
873 metadata
.FloatingPoint
: self
._create
_float
,
874 metadata
.Enum
: self
._create
_enum
,
875 metadata
.String
: self
._create
_string
,
876 metadata
.Struct
: self
._create
_struct
,
877 metadata
.Array
: self
._create
_array
,
879 self
._include
_dirs
= include_dirs
880 self
._ignore
_include
_not
_found
= ignore_include_not_found
881 self
._dump
_config
= dump_config
883 def _set_byte_order(self
, metadata_node
):
884 if 'trace' not in metadata_node
:
885 raise ConfigError('Metadata', 'Missing "trace" property')
887 trace_node
= metadata_node
['trace']
889 if not _is_assoc_array_prop(trace_node
):
890 raise ConfigError('Metadata\'s "trace" property',
891 'Must be an associative array')
893 if 'byte-order' not in trace_node
:
894 raise ConfigError('Metadata\'s "trace" property',
895 'Missing "byte-order" property')
897 bo_node
= trace_node
['byte-order']
899 if not _is_str_prop(bo_node
):
900 raise ConfigError('Metadata\'s "trace" property',
901 '"byte-order" property must be a string ("le" or "be")')
903 self
._bo
= _byte_order_str_to_bo(bo_node
)
906 raise ConfigError('Metadata\'s "trace" property',
907 'Invalid "byte-order" property: must be "le" or "be"')
909 def _lookup_type_alias(self
, name
):
910 if name
in self
._tas
:
911 return copy
.deepcopy(self
._tas
[name
])
913 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
914 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
917 raise ConfigError('Integer type\'s clock property mapping',
918 'Unknown property: "{}"'.format(unk_prop
))
920 if 'name' not in prop_mapping_node
:
921 raise ConfigError('Integer type\'s clock property mapping',
922 'Missing "name" property')
924 if 'property' not in prop_mapping_node
:
925 raise ConfigError('Integer type\'s clock property mapping',
926 'Missing "property" property')
928 clock_name
= prop_mapping_node
['name']
929 prop
= prop_mapping_node
['property']
931 if not _is_str_prop(clock_name
):
932 raise ConfigError('Integer type\'s clock property mapping',
933 '"name" property must be a string')
935 if not _is_str_prop(prop
):
936 raise ConfigError('Integer type\'s clock property mapping',
937 '"property" property must be a string')
939 if clock_name
not in self
._clocks
:
940 raise ConfigError('Integer type\'s clock property mapping',
941 'Invalid clock name "{}"'.format(clock_name
))
944 raise ConfigError('Integer type\'s clock property mapping',
945 'Invalid "property" property: "{}"'.format(prop
))
947 mapped_clock
= self
._clocks
[clock_name
]
948 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
950 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
951 kp
= known_props
+ ['inherit', 'class']
953 if self
._version
>= 201:
954 kp
.append('$inherit')
956 return _get_first_unknown_prop(type_node
, kp
)
958 def _create_integer(self
, obj
, node
):
960 # create integer object
961 obj
= metadata
.Integer()
963 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
974 raise ConfigError('Integer type',
975 'Unknown property: "{}"'.format(unk_prop
))
981 if not _is_int_prop(size
):
982 raise ConfigError('Integer type',
983 '"size" property of integer type object must be an integer')
986 raise ConfigError('Integer type',
987 'Invalid integer size: {}'.format(size
))
993 align
= node
['align']
996 obj
.set_default_align()
998 if not _is_int_prop(align
):
999 raise ConfigError('Integer type',
1000 '"align" property of integer type object must be an integer')
1002 if not _is_valid_alignment(align
):
1003 raise ConfigError('Integer type',
1004 'Invalid alignment: {}'.format(align
))
1009 if 'signed' in node
:
1010 signed
= node
['signed']
1013 obj
.set_default_signed()
1015 if not _is_bool_prop(signed
):
1016 raise ConfigError('Integer type',
1017 '"signed" property of integer type object must be a boolean')
1022 if 'byte-order' in node
:
1023 byte_order
= node
['byte-order']
1025 if byte_order
is None:
1026 obj
.byte_order
= self
._bo
1028 if not _is_str_prop(byte_order
):
1029 raise ConfigError('Integer type',
1030 '"byte-order" property of integer type object must be a string ("le" or "be")')
1032 byte_order
= _byte_order_str_to_bo(byte_order
)
1034 if byte_order
is None:
1035 raise ConfigError('Integer type',
1036 'Invalid "byte-order" property in integer type object')
1038 obj
.byte_order
= byte_order
1040 obj
.byte_order
= self
._bo
1047 obj
.set_default_base()
1049 if not _is_str_prop(base
):
1050 raise ConfigError('Integer type',
1051 '"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1062 raise ConfigError('Integer type',
1063 'Unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base
))
1068 if 'encoding' in node
:
1069 encoding
= node
['encoding']
1071 if encoding
is None:
1072 obj
.set_default_encoding()
1074 if not _is_str_prop(encoding
):
1075 raise ConfigError('Integer type',
1076 '"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1078 encoding
= _encoding_str_to_encoding(encoding
)
1080 if encoding
is None:
1081 raise ConfigError('Integer type',
1082 'Invalid "encoding" property in integer type object')
1084 obj
.encoding
= encoding
1087 if 'property-mappings' in node
:
1088 prop_mappings
= node
['property-mappings']
1090 if prop_mappings
is None:
1091 obj
.set_default_property_mappings()
1093 if not _is_array_prop(prop_mappings
):
1094 raise ConfigError('Integer type',
1095 '"property-mappings" property of integer type object must be an array')
1097 if len(prop_mappings
) > 1:
1098 raise ConfigError('Integer type',
1099 'Length of "property-mappings" array in integer type object must be 1')
1101 for index
, prop_mapping
in enumerate(prop_mappings
):
1102 if not _is_assoc_array_prop(prop_mapping
):
1103 raise ConfigError('Integer type',
1104 'Elements of "property-mappings" property of integer type object must be associative arrays')
1106 if 'type' not in prop_mapping
:
1107 raise ConfigError('Integer type',
1108 'Missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1110 prop_type
= prop_mapping
['type']
1112 if not _is_str_prop(prop_type
):
1113 raise ConfigError('Integer type',
1114 '"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1116 if prop_type
== 'clock':
1117 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1119 raise ConfigError('Integer type',
1120 'Unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1124 def _create_float(self
, obj
, node
):
1126 # create floating point number object
1127 obj
= metadata
.FloatingPoint()
1129 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1136 raise ConfigError('Floating point number type',
1137 'Unknown property: "{}"'.format(unk_prop
))
1143 if not _is_assoc_array_prop(size
):
1144 raise ConfigError('Floating point number type',
1145 '"size" property must be an associative array')
1147 unk_prop
= _get_first_unknown_prop(size
, ['exp', 'mant'])
1150 raise ConfigError('Floating point number type\'s "size" property',
1151 'Unknown property: "{}"'.format(unk_prop
))
1156 if not _is_int_prop(exp
):
1157 raise ConfigError('Floating point number type\'s "size" property',
1158 '"exp" property must be an integer')
1161 raise ConfigError('Floating point number type\'s "size" property',
1162 'Invalid exponent size: {}')
1169 if not _is_int_prop(mant
):
1170 raise ConfigError('Floating point number type\'s "size" property',
1171 '"mant" property must be an integer')
1174 raise ConfigError('Floating point number type\'s "size" property',
1175 'Invalid mantissa size: {}')
1177 obj
.mant_size
= mant
1181 align
= node
['align']
1184 obj
.set_default_align()
1186 if not _is_int_prop(align
):
1187 raise ConfigError('Floating point number type',
1188 '"align" property must be an integer')
1190 if not _is_valid_alignment(align
):
1191 raise ConfigError('Floating point number type',
1192 'Invalid alignment: {}'.format(align
))
1197 if 'byte-order' in node
:
1198 byte_order
= node
['byte-order']
1200 if byte_order
is None:
1201 obj
.byte_order
= self
._bo
1203 if not _is_str_prop(byte_order
):
1204 raise ConfigError('Floating point number type',
1205 '"byte-order" property must be a string ("le" or "be")')
1207 byte_order
= _byte_order_str_to_bo(byte_order
)
1209 if byte_order
is None:
1210 raise ConfigError('Floating point number type',
1211 'Invalid "byte-order" property')
1213 obj
.byte_order
= self
._bo
1217 def _create_enum(self
, obj
, node
):
1219 # create enumeration object
1220 obj
= metadata
.Enum()
1222 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1228 raise ConfigError('Enumeration type',
1229 'Unknown property: "{}"'.format(unk_prop
))
1232 if 'value-type' in node
:
1233 value_type_node
= node
['value-type']
1236 obj
.value_type
= self
._create
_type
(value_type_node
)
1237 except ConfigError
as exc
:
1238 exc
.append_ctx('Enumeration type', 'Cannot create integer type')
1242 if 'members' in node
:
1243 members_node
= node
['members']
1245 if not _is_array_prop(members_node
):
1246 raise ConfigError('Enumeration type',
1247 '"members" property must be an array')
1250 last_value
= obj
.last_value
1252 if last_value
is None:
1255 cur
= last_value
+ 1
1257 for index
, m_node
in enumerate(members_node
):
1258 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1259 raise ConfigError('Enumeration type',
1260 'Invalid member #{}: expecting a string or an associative array'.format(index
))
1262 if _is_str_prop(m_node
):
1267 unk_prop
= _get_first_unknown_prop(m_node
, [
1273 raise ConfigError('Enumeration type',
1274 'Unknown member object property: "{}"'.format(unk_prop
))
1276 if 'label' not in m_node
:
1277 raise ConfigError('Enumeration type',
1278 'Missing "label" property in member #{}'.format(index
))
1280 label
= m_node
['label']
1282 if not _is_str_prop(label
):
1283 raise ConfigError('Enumeration type',
1284 '"label" property of member #{} must be a string'.format(index
))
1286 if 'value' not in m_node
:
1287 raise ConfigError('Enumeration type',
1288 'Missing "value" property in member ("{}")'.format(label
))
1290 value
= m_node
['value']
1292 if not _is_int_prop(value
) and not _is_array_prop(value
):
1293 raise ConfigError('Enumeration type',
1294 'Invalid member ("{}"): expecting an integer or an array'.format(label
))
1296 if _is_int_prop(value
):
1298 value
= (value
, value
)
1301 raise ConfigError('Enumeration type',
1302 'Invalid member ("{}"): range must have exactly two items'.format(label
))
1308 raise ConfigError('Enumeration type',
1309 'Invalid member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1314 obj
.members
[label
] = value
1318 def _create_string(self
, obj
, node
):
1320 # create string object
1321 obj
= metadata
.String()
1323 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1328 raise ConfigError('String type',
1329 'Unknown object property: "{}"'.format(unk_prop
))
1332 if 'encoding' in node
:
1333 encoding
= node
['encoding']
1335 if encoding
is None:
1336 obj
.set_default_encoding()
1338 if not _is_str_prop(encoding
):
1339 raise ConfigError('String type',
1340 '"encoding" property of must be a string ("none", "ascii", or "utf-8")')
1342 encoding
= _encoding_str_to_encoding(encoding
)
1344 if encoding
is None:
1345 raise ConfigError('String type',
1346 'Invalid "encoding" property')
1348 obj
.encoding
= encoding
1352 def _create_struct(self
, obj
, node
):
1354 # create structure object
1355 obj
= metadata
.Struct()
1357 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1363 raise ConfigError('Structure type',
1364 'Unknown object property: "{}"'.format(unk_prop
))
1367 if 'min-align' in node
:
1368 min_align
= node
['min-align']
1370 if min_align
is None:
1371 obj
.set_default_min_align()
1373 if not _is_int_prop(min_align
):
1374 raise ConfigError('Structure type',
1375 '"min-align" property must be an integer')
1377 if not _is_valid_alignment(min_align
):
1378 raise ConfigError('Structure type',
1379 'Invalid minimum alignment: {}'.format(min_align
))
1381 obj
.min_align
= min_align
1384 if 'fields' in node
:
1385 fields
= node
['fields']
1388 obj
.set_default_fields()
1390 if not _is_assoc_array_prop(fields
):
1391 raise ConfigError('Structure type',
1392 '"fields" property must be an associative array')
1394 for field_name
, field_node
in fields
.items():
1395 if not _is_valid_identifier(field_name
):
1396 raise ConfigError('Structure type',
1397 '"{}" is not a valid field name'.format(field_name
))
1400 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1401 except ConfigError
as exc
:
1402 exc
.append_ctx('Structure type',
1403 'Cannot create field "{}"'.format(field_name
))
1408 def _create_array(self
, obj
, node
):
1410 # create array object
1411 obj
= metadata
.Array()
1413 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1419 raise ConfigError('Array type',
1420 'Unknown property: "{}"'.format(unk_prop
))
1423 if 'length' in node
:
1424 length
= node
['length']
1426 if not _is_int_prop(length
):
1427 raise ConfigError('Array type',
1428 '"length" property must be an integer')
1430 if type(length
) is int and length
< 0:
1431 raise ConfigError('Array type',
1432 'Invalid length: {}'.format(length
))
1437 if 'element-type' in node
:
1438 element_type_node
= node
['element-type']
1441 obj
.element_type
= self
._create
_type
(node
['element-type'])
1442 except ConfigError
as exc
:
1443 exc
.append_ctx('Array type', 'Cannot create element type')
1448 def _create_type(self
, type_node
):
1449 if type(type_node
) is str:
1450 t
= self
._lookup
_type
_alias
(type_node
)
1453 raise ConfigError('Type',
1454 'Unknown type alias "{}"'.format(type_node
))
1458 if not _is_assoc_array_prop(type_node
):
1459 raise ConfigError('Type',
1460 'Expecting associative arrays or string (type alias name)')
1467 if self
._version
>= 200:
1468 if 'inherit' in type_node
:
1469 inherit_prop
= 'inherit'
1470 inherit_node
= type_node
[inherit_prop
]
1472 if self
._version
>= 201:
1473 if '$inherit' in type_node
:
1474 if inherit_node
is not None:
1475 raise ConfigError('Type',
1476 'Cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1478 inherit_prop
= '$inherit'
1479 inherit_node
= type_node
[inherit_prop
]
1481 if inherit_node
is not None and 'class' in type_node
:
1482 raise ConfigError('Type',
1483 'Cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1485 if inherit_node
is not None:
1486 if not _is_str_prop(inherit_node
):
1487 raise ConfigError('Type',
1488 '"{}" property of type object must be a string'.format(inherit_prop
))
1490 base
= self
._lookup
_type
_alias
(inherit_node
)
1493 raise ConfigError('Type',
1494 'Cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1496 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1498 if 'class' not in type_node
:
1499 raise ConfigError('Type',
1500 'Does not inherit, therefore must have a "class" property')
1502 class_name
= type_node
['class']
1504 if type(class_name
) is not str:
1505 raise ConfigError('Type', '"class" property must be a string')
1507 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1508 raise ConfigError('Type',
1509 'Unknown class "{}"'.format(class_name
))
1512 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1514 return func(base
, type_node
)
1516 def _register_type_aliases(self
, metadata_node
):
1519 if 'type-aliases' not in metadata_node
:
1522 ta_node
= metadata_node
['type-aliases']
1527 if not _is_assoc_array_prop(ta_node
):
1528 raise ConfigError('Metadata',
1529 '"type-aliases" property must be an associative array')
1531 for ta_name
, ta_type
in ta_node
.items():
1532 if ta_name
in self
._tas
:
1533 raise ConfigError('Metadata',
1534 'Duplicate type alias "{}"'.format(ta_name
))
1537 t
= self
._create
_type
(ta_type
)
1538 except ConfigError
as exc
:
1539 exc
.append_ctx('Metadata',
1540 'Cannot create type alias "{}"'.format(ta_name
))
1543 self
._tas
[ta_name
] = t
1545 def _create_clock(self
, node
):
1546 # create clock object
1547 clock
= metadata
.Clock()
1549 if not _is_assoc_array_prop(node
):
1550 raise ConfigError('Metadata',
1551 'Clock objects must be associative arrays')
1563 if self
._version
>= 201:
1564 known_props
.append('$return-ctype')
1566 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1569 raise ConfigError('Clock',
1570 'Unknown property: "{}"'.format(unk_prop
))
1574 uuidp
= node
['uuid']
1577 clock
.set_default_uuid()
1579 if not _is_str_prop(uuidp
):
1580 raise ConfigError('Clock',
1581 '"uuid" property must be a string')
1584 uuidp
= uuid
.UUID(uuidp
)
1586 raise ConfigError('Clock', 'Malformed UUID: "{}"'.format(uuidp
))
1591 if 'description' in node
:
1592 desc
= node
['description']
1595 clock
.set_default_description()
1597 if not _is_str_prop(desc
):
1598 raise ConfigError('Clock',
1599 '"description" property must be a string')
1601 clock
.description
= desc
1608 clock
.set_default_freq()
1610 if not _is_int_prop(freq
):
1611 raise ConfigError('Clock',
1612 '"freq" property must be an integer')
1615 raise ConfigError('Clock',
1616 'Invalid frequency: {}'.format(freq
))
1621 if 'error-cycles' in node
:
1622 error_cycles
= node
['error-cycles']
1624 if error_cycles
is None:
1625 clock
.set_default_error_cycles()
1627 if not _is_int_prop(error_cycles
):
1628 raise ConfigError('Clock',
1629 '"error-cycles" property must be an integer')
1631 if error_cycles
< 0:
1632 raise ConfigError('Clock',
1633 'Invalid error cycles: {}'.format(error_cycles
))
1635 clock
.error_cycles
= error_cycles
1638 if 'offset' in node
:
1639 offset
= node
['offset']
1642 clock
.set_default_offset_seconds()
1643 clock
.set_default_offset_cycles()
1645 if not _is_assoc_array_prop(offset
):
1646 raise ConfigError('Clock',
1647 '"offset" property must be an associative array')
1649 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1652 raise ConfigError('Clock',
1653 'Unknown offset property: "{}"'.format(unk_prop
))
1656 if 'cycles' in offset
:
1657 offset_cycles
= offset
['cycles']
1659 if offset_cycles
is None:
1660 clock
.set_default_offset_cycles()
1662 if not _is_int_prop(offset_cycles
):
1663 raise ConfigError('Clock\'s "offset" property',
1664 '"cycles" property must be an integer')
1666 if offset_cycles
< 0:
1667 raise ConfigError('Clock\'s "offset" property',
1668 'Invalid cycles: {}'.format(offset_cycles
))
1670 clock
.offset_cycles
= offset_cycles
1673 if 'seconds' in offset
:
1674 offset_seconds
= offset
['seconds']
1676 if offset_seconds
is None:
1677 clock
.set_default_offset_seconds()
1679 if not _is_int_prop(offset_seconds
):
1680 raise ConfigError('Clock\'s "offset" property',
1681 '"seconds" property must be an integer')
1683 if offset_seconds
< 0:
1684 raise ConfigError('Clock\'s "offset" property',
1685 'Invalid seconds: {}'.format(offset_seconds
))
1687 clock
.offset_seconds
= offset_seconds
1690 if 'absolute' in node
:
1691 absolute
= node
['absolute']
1693 if absolute
is None:
1694 clock
.set_default_absolute()
1696 if not _is_bool_prop(absolute
):
1697 raise ConfigError('Clock',
1698 '"absolute" property must be a boolean')
1700 clock
.absolute
= absolute
1703 # v2.0: "return-ctype"
1704 # v2.1+: "$return-ctype"
1705 return_ctype_node
= None
1707 if self
._version
>= 200:
1708 if 'return-ctype' in node
:
1709 return_ctype_prop
= 'return-ctype'
1710 return_ctype_node
= node
[return_ctype_prop
]
1712 if self
._version
>= 201:
1713 if '$return-ctype' in node
:
1714 if return_ctype_node
is not None:
1715 raise ConfigError('Clock',
1716 'Cannot specify both "return-ctype" and "$return-ctype" properties: prefer "$return-ctype"')
1718 return_ctype_prop
= '$return-ctype'
1719 return_ctype_node
= node
[return_ctype_prop
]
1721 if return_ctype_node
is not None:
1722 if return_ctype_node
is None:
1723 clock
.set_default_return_ctype()
1725 if not _is_str_prop(return_ctype_node
):
1726 raise ConfigError('Clock',
1727 '"{}" property of must be a string'.format(return_ctype_prop
))
1729 clock
.return_ctype
= return_ctype_node
1733 def _register_clocks(self
, metadata_node
):
1734 self
._clocks
= collections
.OrderedDict()
1736 if 'clocks' not in metadata_node
:
1739 clocks_node
= metadata_node
['clocks']
1741 if clocks_node
is None:
1744 if not _is_assoc_array_prop(clocks_node
):
1745 raise ConfigError('Metadata',
1746 '"clocks" property must be an associative array')
1748 for clock_name
, clock_node
in clocks_node
.items():
1749 if not _is_valid_identifier(clock_name
):
1750 raise ConfigError('Metadata',
1751 'Invalid clock name: "{}"'.format(clock_name
))
1753 if clock_name
in self
._clocks
:
1754 raise ConfigError('Metadata',
1755 'Duplicate clock "{}"'.format(clock_name
))
1758 clock
= self
._create
_clock
(clock_node
)
1759 except ConfigError
as exc
:
1760 exc
.append_ctx('Metadata',
1761 'Cannot create clock "{}"'.format(clock_name
))
1764 clock
.name
= clock_name
1765 self
._clocks
[clock_name
] = clock
1767 def _create_env(self
, metadata_node
):
1768 env
= collections
.OrderedDict()
1770 if 'env' not in metadata_node
:
1773 env_node
= metadata_node
['env']
1775 if env_node
is None:
1778 if not _is_assoc_array_prop(env_node
):
1779 raise ConfigError('Metadata',
1780 '"env" property must be an associative array')
1782 for env_name
, env_value
in env_node
.items():
1784 raise ConfigError('Metadata',
1785 'Duplicate environment variable "{}"'.format(env_name
))
1787 if not _is_valid_identifier(env_name
):
1788 raise ConfigError('Metadata',
1789 'Invalid environment variable name: "{}"'.format(env_name
))
1791 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
1792 raise ConfigError('Metadata',
1793 'Invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
1795 env
[env_name
] = env_value
1799 def _register_log_levels(self
, metadata_node
):
1800 self
._log
_levels
= dict()
1803 # v2.0: "log-levels"
1804 # v2.1+: "$log-levels"
1805 log_levels_node
= None
1807 if self
._version
>= 200:
1808 if 'log-levels' in metadata_node
:
1809 log_levels_prop
= 'log-levels'
1810 log_levels_node
= metadata_node
[log_levels_prop
]
1812 if self
._version
>= 201:
1813 if '$log-levels' in metadata_node
:
1814 if log_levels_node
is not None:
1815 raise ConfigError('Metadata',
1816 'Cannot specify both "log-levels" and "$log-levels" properties of metadata object: prefer "$log-levels"')
1818 log_levels_prop
= '$log-levels'
1819 log_levels_node
= metadata_node
[log_levels_prop
]
1821 if log_levels_node
is None:
1824 if not _is_assoc_array_prop(log_levels_node
):
1825 raise ConfigError('Metadata',
1826 '"{}" property (metadata) must be an associative array'.format(log_levels_prop
))
1828 for ll_name
, ll_value
in log_levels_node
.items():
1829 if ll_name
in self
._log
_levels
:
1830 raise ConfigError('"{}" property"'.format(log_levels_prop
),
1831 'Duplicate entry "{}"'.format(ll_name
))
1833 if not _is_int_prop(ll_value
):
1834 raise ConfigError('"{}" property"'.format(log_levels_prop
),
1835 'Invalid entry ("{}"): expecting an integer'.format(ll_name
))
1838 raise ConfigError('"{}" property"'.format(log_levels_prop
),
1839 'Invalid entry ("{}"): value must be positive'.format(ll_name
))
1841 self
._log
_levels
[ll_name
] = ll_value
1843 def _create_trace(self
, metadata_node
):
1844 # create trace object
1845 trace
= metadata
.Trace()
1847 if 'trace' not in metadata_node
:
1848 raise ConfigError('Metadata', 'Missing "trace" property')
1850 trace_node
= metadata_node
['trace']
1852 if not _is_assoc_array_prop(trace_node
):
1853 raise ConfigError('Metadata',
1854 '"trace" property must be an associative array')
1856 unk_prop
= _get_first_unknown_prop(trace_node
, [
1859 'packet-header-type',
1863 raise ConfigError('Trace',
1864 'Unknown property: "{}"'.format(unk_prop
))
1866 # set byte order (already parsed)
1867 trace
.byte_order
= self
._bo
1870 if 'uuid' in trace_node
and trace_node
['uuid'] is not None:
1871 uuidp
= trace_node
['uuid']
1873 if not _is_str_prop(uuidp
):
1874 raise ConfigError('Trace',
1875 '"uuid" property must be a string')
1878 uuidp
= uuid
.uuid1()
1881 uuidp
= uuid
.UUID(uuidp
)
1883 raise ConfigError('Trace',
1884 'Malformed UUID: "{}"'.format(uuidp
))
1888 # packet header type
1889 if 'packet-header-type' in trace_node
and trace_node
['packet-header-type'] is not None:
1891 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
1892 except ConfigError
as exc
:
1893 exc
.append_ctx('Trace',
1894 'Cannot create packet header type')
1897 trace
.packet_header_type
= ph_type
1901 def _lookup_log_level(self
, ll
):
1902 if _is_int_prop(ll
):
1904 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
1905 return self
._log
_levels
[ll
]
1907 def _create_event(self
, event_node
):
1908 event
= metadata
.Event()
1910 if not _is_assoc_array_prop(event_node
):
1911 raise ConfigError('Event',
1912 'Expecting associative array')
1914 unk_prop
= _get_first_unknown_prop(event_node
, [
1921 raise ConfigError('Event',
1922 'Unknown property: "{}"'.format(unk_prop
))
1924 if 'log-level' in event_node
and event_node
['log-level'] is not None:
1925 ll_node
= event_node
['log-level']
1927 if _is_str_prop(ll_node
):
1928 ll_value
= self
._lookup
_log
_level
(event_node
['log-level'])
1930 if ll_value
is None:
1931 raise ConfigError('Event\'s "log-level" property',
1932 'Cannot find log level "{}"'.format(ll_node
))
1934 ll
= metadata
.LogLevel(event_node
['log-level'], ll_value
)
1935 elif _is_int_prop(ll_node
):
1937 raise ConfigError('Event\'s "log-level" property',
1938 'Invalid value {}: value must be positive'.format(ll_node
))
1940 ll
= metadata
.LogLevel(None, ll_node
)
1942 raise ConfigError('Event\'s "log-level" property',
1943 'Must be either a string or an integer')
1945 event
.log_level
= ll
1947 if 'context-type' in event_node
and event_node
['context-type'] is not None:
1948 ctx_type_node
= event_node
['context-type']
1951 t
= self
._create
_type
(event_node
['context-type'])
1952 except ConfigError
as exc
:
1953 exc
.append_ctx('Event',
1954 'Cannot create context type object')
1957 event
.context_type
= t
1959 if 'payload-type' in event_node
and event_node
['payload-type'] is not None:
1961 t
= self
._create
_type
(event_node
['payload-type'])
1962 except ConfigError
as exc
:
1963 exc
.append_ctx('Event',
1964 'Cannot create payload type object')
1967 event
.payload_type
= t
1971 def _create_stream(self
, stream_name
, stream_node
):
1972 stream
= metadata
.Stream()
1974 if not _is_assoc_array_prop(stream_node
):
1975 raise ConfigError('Stream objects must be associative arrays')
1978 'packet-context-type',
1979 'event-header-type',
1980 'event-context-type',
1984 if self
._version
>= 202:
1985 known_props
.append('$default')
1987 unk_prop
= _get_first_unknown_prop(stream_node
, known_props
)
1992 if unk_prop
== '$default':
1993 add
= ' (use version 2.2 or greater)'
1995 raise ConfigError('Stream',
1996 'Unknown property{}: "{}"'.format(add
, unk_prop
))
1998 if 'packet-context-type' in stream_node
and stream_node
['packet-context-type'] is not None:
2000 t
= self
._create
_type
(stream_node
['packet-context-type'])
2001 except ConfigError
as exc
:
2002 exc
.append_ctx('Stream',
2003 'Cannot create packet context type object')
2006 stream
.packet_context_type
= t
2008 if 'event-header-type' in stream_node
and stream_node
['event-header-type'] is not None:
2010 t
= self
._create
_type
(stream_node
['event-header-type'])
2011 except ConfigError
as exc
:
2012 exc
.append_ctx('Stream',
2013 'Cannot create event header type object')
2016 stream
.event_header_type
= t
2018 if 'event-context-type' in stream_node
and stream_node
['event-context-type'] is not None:
2020 t
= self
._create
_type
(stream_node
['event-context-type'])
2021 except ConfigError
as exc
:
2022 exc
.append_ctx('Stream',
2023 'Cannot create event context type object')
2026 stream
.event_context_type
= t
2028 if 'events' not in stream_node
:
2029 raise ConfigError('Stream',
2030 'Missing "events" property')
2032 events
= stream_node
['events']
2034 if events
is not None:
2035 if not _is_assoc_array_prop(events
):
2036 raise ConfigError('Stream',
2037 '"events" property must be an associative array')
2040 raise ConfigError('Stream', 'At least one event is needed')
2044 for ev_name
, ev_node
in events
.items():
2046 ev
= self
._create
_event
(ev_node
)
2047 except ConfigError
as exc
:
2048 exc
.append_ctx('Stream',
2049 'Cannot create event "{}"'.format(ev_name
))
2054 stream
.events
[ev_name
] = ev
2057 if '$default' in stream_node
and stream_node
['$default'] is not None:
2058 default_node
= stream_node
['$default']
2060 if not _is_bool_prop(default_node
):
2061 raise ConfigError('Stream',
2062 'Invalid "$default" property: expecting a boolean')
2065 if self
._meta
.default_stream_name
is not None and self
._meta
.default_stream_name
!= stream_name
:
2066 fmt
= 'Cannot specify more than one default stream (default stream already set to "{}")'
2067 raise ConfigError('Stream',
2068 fmt
.format(self
._meta
.default_stream_name
))
2070 self
._meta
.default_stream_name
= stream_name
2074 def _create_streams(self
, metadata_node
):
2075 streams
= collections
.OrderedDict()
2077 if 'streams' not in metadata_node
:
2078 raise ConfigError('Metadata',
2079 'Missing "streams" property')
2081 streams_node
= metadata_node
['streams']
2083 if not _is_assoc_array_prop(streams_node
):
2084 raise ConfigError('Metadata',
2085 '"streams" property must be an associative array')
2087 if not streams_node
:
2088 raise ConfigError('Metadata\'s "streams" property',
2089 'At least one stream is needed')
2093 for stream_name
, stream_node
in streams_node
.items():
2095 stream
= self
._create
_stream
(stream_name
, stream_node
)
2096 except ConfigError
as exc
:
2097 exc
.append_ctx('Metadata',
2098 'Cannot create stream "{}"'.format(stream_name
))
2102 stream
.name
= str(stream_name
)
2103 streams
[stream_name
] = stream
2108 def _create_metadata(self
, root
):
2109 self
._meta
= metadata
.Metadata()
2111 if 'metadata' not in root
:
2112 raise ConfigError('Configuration',
2113 'Missing "metadata" property')
2115 metadata_node
= root
['metadata']
2117 if not _is_assoc_array_prop(metadata_node
):
2118 raise ConfigError('Configuration\'s "metadata" property',
2119 'Must be an associative array')
2130 if self
._version
>= 201:
2131 known_props
.append('$log-levels')
2133 if self
._version
>= 202:
2134 known_props
.append('$default-stream')
2136 unk_prop
= _get_first_unknown_prop(metadata_node
, known_props
)
2141 if unk_prop
== '$include':
2142 add
= ' (use version 2.1 or greater)'
2144 if unk_prop
== '$default-stream':
2145 add
= ' (use version 2.2 or greater)'
2147 raise ConfigError('Metadata',
2148 'Unknown property{}: "{}"'.format(add
, unk_prop
))
2150 if '$default-stream' in metadata_node
and metadata_node
['$default-stream'] is not None:
2151 default_stream_node
= metadata_node
['$default-stream']
2153 if not _is_str_prop(default_stream_node
):
2154 raise ConfigError('Metadata\'s "$default-stream" property',
2155 'Expecting a string')
2157 self
._meta
.default_stream_name
= default_stream_node
2159 self
._set
_byte
_order
(metadata_node
)
2160 self
._register
_clocks
(metadata_node
)
2161 self
._meta
.clocks
= self
._clocks
2162 self
._register
_type
_aliases
(metadata_node
)
2163 self
._meta
.env
= self
._create
_env
(metadata_node
)
2164 self
._meta
.trace
= self
._create
_trace
(metadata_node
)
2165 self
._register
_log
_levels
(metadata_node
)
2166 self
._meta
.streams
= self
._create
_streams
(metadata_node
)
2170 def _get_version(self
, root
):
2171 if 'version' not in root
:
2172 raise ConfigError('Configuration',
2173 'Missing "version" property')
2175 version_node
= root
['version']
2177 if not _is_str_prop(version_node
):
2178 raise ConfigError('Configuration\'s "version" property',
2181 version_node
= version_node
.strip()
2183 if version_node
not in ['2.0', '2.1', '2.2']:
2184 raise ConfigError('Configuration',
2185 'Unsupported version ({}): versions 2.0, 2.1, and 2.2 are supported'.format(version_node
))
2187 # convert version string to comparable version integer
2188 parts
= version_node
.split('.')
2189 version
= int(parts
[0]) * 100 + int(parts
[1])
2193 def _get_prefix(self
, root
):
2194 def_prefix
= 'barectf_'
2196 if 'prefix' not in root
:
2199 prefix_node
= root
['prefix']
2201 if prefix_node
is None:
2204 if not _is_str_prop(prefix_node
):
2205 raise ConfigError('Configuration\'s "prefix" property',
2208 if not _is_valid_identifier(prefix_node
):
2209 raise ConfigError('Configuration\'s "prefix" property',
2210 'Must be a valid C identifier')
2214 def _get_options(self
, root
):
2215 cfg_options
= ConfigOptions()
2217 if 'options' not in root
:
2220 options_node
= root
['options']
2222 if not _is_assoc_array_prop(options_node
):
2223 raise ConfigError('Configuration\'s "options" property',
2224 'Must be an associative array')
2228 'gen-default-stream-def',
2230 unk_prop
= _get_first_unknown_prop(options_node
, known_props
)
2233 raise ConfigError('Configuration\'s "options" property',
2234 'Unknown property: "{}"'.format(unk_prop
))
2236 if 'gen-prefix-def' in options_node
and options_node
['gen-prefix-def'] is not None:
2237 gen_prefix_def_node
= options_node
['gen-prefix-def']
2239 if not _is_bool_prop(gen_prefix_def_node
):
2240 raise ConfigError('Configuration\'s "options" property',
2241 'Invalid option "gen-prefix-def": expecting a boolean')
2243 cfg_options
.gen_prefix_def
= gen_prefix_def_node
2245 if 'gen-default-stream-def' in options_node
and options_node
['gen-default-stream-def'] is not None:
2246 gen_default_stream_def_node
= options_node
['gen-default-stream-def']
2248 if not _is_bool_prop(gen_default_stream_def_node
):
2249 raise ConfigError('Configuration\'s "options" property',
2250 'Invalid option "gen-default-stream-def": expecting a boolean')
2252 cfg_options
.gen_default_stream_def
= gen_default_stream_def_node
2256 def _get_last_include_file(self
):
2257 if self
._include
_stack
:
2258 return self
._include
_stack
[-1]
2260 return self
._root
_yaml
_path
2262 def _load_include(self
, yaml_path
):
2263 for inc_dir
in self
._include
_dirs
:
2264 # current include dir + file name path
2265 # note: os.path.join() only takes the last arg if it's absolute
2266 inc_path
= os
.path
.join(inc_dir
, yaml_path
)
2268 # real path (symbolic links resolved)
2269 real_path
= os
.path
.realpath(inc_path
)
2271 # normalized path (weird stuff removed!)
2272 norm_path
= os
.path
.normpath(real_path
)
2274 if not os
.path
.isfile(norm_path
):
2275 # file does not exist: skip
2278 if norm_path
in self
._include
_stack
:
2279 base_path
= self
._get
_last
_include
_file
()
2280 raise ConfigError('In "{}"',
2281 'Cannot recursively include file "{}"'.format(base_path
, norm_path
))
2283 self
._include
_stack
.append(norm_path
)
2286 return self
._yaml
_ordered
_load
(norm_path
)
2288 if not self
._ignore
_include
_not
_found
:
2289 base_path
= self
._get
_last
_include
_file
()
2290 raise ConfigError('In "{}"',
2291 'Cannot include file "{}": file not found in include directories'.format(base_path
, yaml_path
))
2295 def _get_include_paths(self
, include_node
):
2296 if include_node
is None:
2299 if _is_str_prop(include_node
):
2300 return [include_node
]
2302 if _is_array_prop(include_node
):
2303 for include_path
in include_node
:
2304 if not _is_str_prop(include_path
):
2305 raise ConfigError('"$include" property',
2306 'Expecting array of strings')
2310 raise ConfigError('"$include" property',
2311 'Expecting string or array of strings')
2313 def _update_node(self
, base_node
, overlay_node
):
2314 for olay_key
, olay_value
in overlay_node
.items():
2315 if olay_key
in base_node
:
2316 base_value
= base_node
[olay_key
]
2318 if _is_assoc_array_prop(olay_value
) and _is_assoc_array_prop(base_value
):
2319 # merge dictionaries
2320 self
._update
_node
(base_value
, olay_value
)
2321 elif _is_array_prop(olay_value
) and _is_array_prop(base_value
):
2322 # append extension array items to base items
2323 base_value
+= olay_value
2325 # fall back to replacing
2326 base_node
[olay_key
] = olay_value
2328 base_node
[olay_key
] = olay_value
2330 def _process_node_include(self
, last_overlay_node
, name
,
2331 process_base_include_cb
,
2332 process_children_include_cb
=None):
2333 if not _is_assoc_array_prop(last_overlay_node
):
2334 raise ConfigError('"$include" property',
2335 '{} objects must be associative arrays'.format(name
))
2337 # process children inclusions first
2338 if process_children_include_cb
:
2339 process_children_include_cb(last_overlay_node
)
2341 if '$include' in last_overlay_node
:
2342 include_node
= last_overlay_node
['$include']
2345 return last_overlay_node
2347 include_paths
= self
._get
_include
_paths
(include_node
)
2348 cur_base_path
= self
._get
_last
_include
_file
()
2351 # keep the include paths and remove the include property
2352 include_paths
= copy
.deepcopy(include_paths
)
2353 del last_overlay_node
['$include']
2355 for include_path
in include_paths
:
2356 # load raw YAML from included file
2357 overlay_node
= self
._load
_include
(include_path
)
2359 if overlay_node
is None:
2360 # cannot find include file, but we're ignoring those
2361 # errors, otherwise _load_include() itself raises
2365 # recursively process includes
2367 overlay_node
= process_base_include_cb(overlay_node
)
2368 except ConfigError
as exc
:
2369 exc
.append_ctx('In "{}"'.format(cur_base_path
))
2372 # pop include stack now that we're done including
2373 del self
._include
_stack
[-1]
2375 # at this point, base_node is fully resolved (does not
2376 # contain any include property)
2377 if base_node
is None:
2378 base_node
= overlay_node
2380 self
._update
_node
(base_node
, overlay_node
)
2382 # finally, we update the latest base node with our last overlay
2384 if base_node
is None:
2385 # nothing was included, which is possible when we're
2386 # ignoring include errors
2387 return last_overlay_node
2389 self
._update
_node
(base_node
, last_overlay_node
)
2393 def _process_event_include(self
, event_node
):
2394 return self
._process
_node
_include
(event_node
, 'event',
2395 self
._process
_event
_include
)
2397 def _process_stream_include(self
, stream_node
):
2398 def process_children_include(stream_node
):
2399 if 'events' in stream_node
:
2400 events_node
= stream_node
['events']
2402 if not _is_assoc_array_prop(events_node
):
2403 raise ConfigError('"$include" property',
2404 '"events" property must be an associative array')
2406 events_node_keys
= list(events_node
.keys())
2408 for key
in events_node_keys
:
2409 event_node
= events_node
[key
]
2412 events_node
[key
] = self
._process
_event
_include
(event_node
)
2413 except ConfigError
as exc
:
2414 exc
.append_ctx('"$include" property',
2415 'Cannot process includes of event object "{}"'.format(key
))
2418 return self
._process
_node
_include
(stream_node
, 'stream',
2419 self
._process
_stream
_include
,
2420 process_children_include
)
2422 def _process_trace_include(self
, trace_node
):
2423 return self
._process
_node
_include
(trace_node
, 'trace',
2424 self
._process
_trace
_include
)
2426 def _process_clock_include(self
, clock_node
):
2427 return self
._process
_node
_include
(clock_node
, 'clock',
2428 self
._process
_clock
_include
)
2430 def _process_metadata_include(self
, metadata_node
):
2431 def process_children_include(metadata_node
):
2432 if 'trace' in metadata_node
:
2433 metadata_node
['trace'] = self
._process
_trace
_include
(metadata_node
['trace'])
2435 if 'clocks' in metadata_node
:
2436 clocks_node
= metadata_node
['clocks']
2438 if not _is_assoc_array_prop(clocks_node
):
2439 raise ConfigError('"$include" property',
2440 '"clocks" property must be an associative array')
2442 clocks_node_keys
= list(clocks_node
.keys())
2444 for key
in clocks_node_keys
:
2445 clock_node
= clocks_node
[key
]
2448 clocks_node
[key
] = self
._process
_clock
_include
(clock_node
)
2449 except ConfigError
as exc
:
2450 exc
.append_ctx('"$include" property',
2451 'Cannot process includes of clock object "{}"'.format(key
))
2454 if 'streams' in metadata_node
:
2455 streams_node
= metadata_node
['streams']
2457 if not _is_assoc_array_prop(streams_node
):
2458 raise ConfigError('"$include" property',
2459 '"streams" property must be an associative array')
2461 streams_node_keys
= list(streams_node
.keys())
2463 for key
in streams_node_keys
:
2464 stream_node
= streams_node
[key
]
2467 streams_node
[key
] = self
._process
_stream
_include
(stream_node
)
2468 except ConfigError
as exc
:
2469 exc
.append_ctx('"$include" property',
2470 'Cannot process includes of stream object "{}"'.format(key
))
2473 return self
._process
_node
_include
(metadata_node
, 'metadata',
2474 self
._process
_metadata
_include
,
2475 process_children_include
)
2477 def _process_root_includes(self
, root
):
2478 # The following config objects support includes:
2485 # We need to process the event includes first, then the stream
2486 # includes, then the trace includes, and finally the metadata
2489 # In each object, only one of the $include and $include-replace
2490 # special properties is allowed.
2492 # We keep a stack of absolute paths to included files to detect
2494 if 'metadata' in root
:
2495 root
['metadata'] = self
._process
_metadata
_include
(root
['metadata'])
2499 def _yaml_ordered_dump(self
, node
, **kwds
):
2500 class ODumper(yaml
.Dumper
):
2503 def dict_representer(dumper
, node
):
2504 return dumper
.represent_mapping(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2507 ODumper
.add_representer(collections
.OrderedDict
, dict_representer
)
2509 return yaml
.dump(node
, Dumper
=ODumper
, **kwds
)
2511 def _yaml_ordered_load(self
, yaml_path
):
2512 class OLoader(yaml
.Loader
):
2515 def construct_mapping(loader
, node
):
2516 loader
.flatten_mapping(node
)
2518 return collections
.OrderedDict(loader
.construct_pairs(node
))
2520 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2525 with
open(yaml_path
, 'r') as f
:
2526 node
= yaml
.load(f
, OLoader
)
2527 except (OSError, IOError) as e
:
2528 raise ConfigError('Configuration',
2529 'Cannot open file "{}"'.format(yaml_path
))
2530 except ConfigError
as exc
:
2531 exc
.append_ctx('Configuration',
2532 'Unknown error while trying to load file "{}"'.format(yaml_path
))
2535 # loaded node must be an associate array
2536 if not _is_assoc_array_prop(node
):
2537 raise ConfigError('Configuration',
2538 'Root of YAML file "{}" must be an associative array'.format(yaml_path
))
2543 self
._version
= None
2544 self
._include
_stack
= []
2546 def parse(self
, yaml_path
):
2548 self
._root
_yaml
_path
= yaml_path
2551 root
= self
._yaml
_ordered
_load
(yaml_path
)
2552 except ConfigError
as exc
:
2553 exc
.append_ctx('Configuration',
2554 'Cannot parse YAML file "{}"'.format(yaml_path
))
2557 if not _is_assoc_array_prop(root
):
2558 raise ConfigError('Configuration',
2559 'Must be an associative array')
2561 # get the config version
2562 self
._version
= self
._get
_version
(root
)
2570 if self
._version
>= 202:
2571 known_props
.append('options')
2573 unk_prop
= _get_first_unknown_prop(root
, known_props
)
2578 if unk_prop
== 'options':
2579 add
= ' (use version 2.2 or greater)'
2581 raise ConfigError('Configuration',
2582 'Unknown property{}: "{}"'.format(add
, unk_prop
))
2584 # process includes if supported
2585 if self
._version
>= 201:
2586 root
= self
._process
_root
_includes
(root
)
2588 # dump config if required
2589 if self
._dump
_config
:
2590 print(self
._yaml
_ordered
_dump
(root
, indent
=2,
2591 default_flow_style
=False))
2593 # get prefix and metadata
2594 prefix
= self
._get
_prefix
(root
)
2595 meta
= self
._create
_metadata
(root
)
2596 opts
= self
._get
_options
(root
)
2598 return Config(self
._version
, prefix
, meta
, opts
)
2601 def from_yaml_file(path
, include_dirs
, ignore_include_not_found
, dump_config
):
2603 parser
= _YamlConfigParser(include_dirs
, ignore_include_not_found
,
2605 cfg
= parser
.parse(path
)
2608 except ConfigError
as exc
:
2609 exc
.append_ctx('Configuration',
2610 'Cannot create configuration from YAML file "{}"'.format(path
))