1 # The MIT License (MIT)
3 # Copyright (c) 2015 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 ConfigError(RuntimeError):
36 def __init__(self
, msg
, prev
=None):
46 def __init__(self
, version
, prefix
, metadata
):
48 self
.version
= version
49 self
.metadata
= metadata
51 def _validate_metadata(self
, meta
):
53 validator
= _MetadataTypesHistologyValidator()
54 validator
.validate(meta
)
55 validator
= _MetadataDynamicTypesValidator()
56 validator
.validate(meta
)
57 validator
= _MetadataSpecialFieldsValidator()
58 validator
.validate(meta
)
59 except Exception as e
:
60 raise ConfigError('metadata error', e
)
63 validator
= _BarectfMetadataValidator()
64 validator
.validate(meta
)
65 except Exception as e
:
66 raise ConfigError('barectf metadata error', e
)
68 def _augment_metadata_env(self
, meta
):
71 env
['domain'] = 'bare'
72 env
['tracer_name'] = 'barectf'
73 version_tuple
= barectf
.get_version_tuple()
74 env
['tracer_major'] = version_tuple
[0]
75 env
['tracer_minor'] = version_tuple
[1]
76 env
['tracer_patch'] = version_tuple
[2]
77 env
['barectf_gen_date'] = str(datetime
.datetime
.now().isoformat())
84 def version(self
, value
):
92 def metadata(self
, value
):
93 self
._validate
_metadata
(value
)
94 self
._augment
_metadata
_env
(value
)
95 self
._metadata
= value
102 def prefix(self
, value
):
103 if not is_valid_identifier(value
):
104 raise ConfigError('prefix must be a valid C identifier')
109 def _is_assoc_array_prop(node
):
110 return isinstance(node
, dict)
113 def _is_array_prop(node
):
114 return isinstance(node
, list)
117 def _is_int_prop(node
):
118 return type(node
) is int
121 def _is_str_prop(node
):
122 return type(node
) is str
125 def _is_bool_prop(node
):
126 return type(node
) is bool
129 def _is_valid_alignment(align
):
130 return ((align
& (align
- 1)) == 0) and align
> 0
133 def _byte_order_str_to_bo(bo_str
):
134 bo_str
= bo_str
.lower()
137 return metadata
.ByteOrder
.LE
139 return metadata
.ByteOrder
.BE
142 def _encoding_str_to_encoding(encoding_str
):
143 encoding_str
= encoding_str
.lower()
145 if encoding_str
== 'utf-8' or encoding_str
== 'utf8':
146 return metadata
.Encoding
.UTF8
147 elif encoding_str
== 'ascii':
148 return metadata
.Encoding
.ASCII
149 elif encoding_str
== 'none':
150 return metadata
.Encoding
.NONE
153 _re_iden
= re
.compile(r
'^[a-zA-Z][a-zA-Z0-9_]*$')
154 _ctf_keywords
= set([
173 def is_valid_identifier(iden
):
174 if not _re_iden
.match(iden
):
177 if _re_iden
in _ctf_keywords
:
183 def _get_first_unknown_prop(node
, known_props
):
184 for prop_name
in node
:
185 if prop_name
in known_props
:
191 # This validator validates the configured metadata for barectf specific
196 # * all header/contexts are at least byte-aligned
197 # * all integer and floating point number sizes to be <= 64
198 # * no inner structures, arrays, or variants
199 class _BarectfMetadataValidator
:
201 self
._type
_to
_validate
_type
_func
= {
202 metadata
.Integer
: self
._validate
_int
_type
,
203 metadata
.FloatingPoint
: self
._validate
_float
_type
,
204 metadata
.Enum
: self
._validate
_enum
_type
,
205 metadata
.String
: self
._validate
_string
_type
,
206 metadata
.Struct
: self
._validate
_struct
_type
,
207 metadata
.Array
: self
._validate
_array
_type
,
208 metadata
.Variant
: self
._validate
_variant
_type
,
211 def _validate_int_type(self
, t
, entity_root
):
213 raise ConfigError('integer type\'s size must be lesser than or equal to 64 bits')
215 def _validate_float_type(self
, t
, entity_root
):
217 raise ConfigError('floating point number type\'s size must be lesser than or equal to 64 bits')
219 def _validate_enum_type(self
, t
, entity_root
):
220 if t
.value_type
.size
> 64:
221 raise ConfigError('enumeration type\'s integer type\'s size must be lesser than or equal to 64 bits')
223 def _validate_string_type(self
, t
, entity_root
):
226 def _validate_struct_type(self
, t
, entity_root
):
228 raise ConfigError('inner structure types are not supported as of this version')
230 for field_name
, field_type
in t
.fields
.items():
231 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
232 if field_name
== 'uuid':
237 self
._validate
_type
(field_type
, False)
238 except Exception as e
:
239 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
241 def _validate_array_type(self
, t
, entity_root
):
242 raise ConfigError('array types are not supported as of this version')
244 def _validate_variant_type(self
, t
, entity_root
):
245 raise ConfigError('variant types are not supported as of this version')
247 def _validate_type(self
, t
, entity_root
):
248 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
250 def _validate_entity(self
, t
):
254 # make sure entity is byte-aligned
256 raise ConfigError('type\'s alignment must be at least byte-aligned')
258 # make sure entity is a structure
259 if type(t
) is not metadata
.Struct
:
260 raise ConfigError('expecting a structure type')
263 self
._validate
_type
(t
, True)
265 def _validate_entities_and_names(self
, meta
):
266 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
269 self
._validate
_entity
(meta
.trace
.packet_header_type
)
270 except Exception as e
:
271 raise ConfigError('invalid trace packet header type', e
)
273 for stream_name
, stream
in meta
.streams
.items():
274 if not is_valid_identifier(stream_name
):
275 raise ConfigError('stream name "{}" is not a valid C identifier'.format(stream_name
))
277 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
280 self
._validate
_entity
(stream
.packet_context_type
)
281 except Exception as e
:
282 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
284 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
287 self
._validate
_entity
(stream
.event_header_type
)
288 except Exception as e
:
289 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
291 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
294 self
._validate
_entity
(stream
.event_context_type
)
295 except Exception as e
:
296 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
299 for ev_name
, ev
in stream
.events
.items():
300 if not is_valid_identifier(ev_name
):
301 raise ConfigError('event name "{}" is not a valid C identifier'.format(ev_name
))
303 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
306 self
._validate
_entity
(ev
.context_type
)
307 except Exception as e
:
308 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
310 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
312 if ev
.payload_type
is None:
313 raise ConfigError('missing payload type in event "{}"'.format(ev_name
), e
)
316 self
._validate
_entity
(ev
.payload_type
)
317 except Exception as e
:
318 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
320 if not ev
.payload_type
.fields
:
321 raise ConfigError('empty payload type in event "{}"'.format(ev_name
), e
)
322 except Exception as e
:
323 raise ConfigError('invalid stream "{}"'.format(stream_name
), e
)
325 def validate(self
, meta
):
326 self
._validate
_entities
_and
_names
(meta
)
329 # This validator validates special fields of trace, stream, and event
330 # types. For example, if checks that the "stream_id" field exists in the
331 # trace packet header if there's more than one stream, and much more.
332 class _MetadataSpecialFieldsValidator
:
333 def _validate_trace_packet_header_type(self
, t
):
334 # needs "stream_id" field?
335 if len(self
._meta
.streams
) > 1:
338 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is missing')
340 if type(t
) is not metadata
.Struct
:
341 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is not a structure type')
343 if 'stream_id' not in t
.fields
:
344 raise ConfigError('need "stream_id" field in trace packet header type')
346 # validate "magic" and "stream_id" types
347 if type(t
) is not metadata
.Struct
:
350 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
351 if field_name
== 'magic':
352 if type(field_type
) is not metadata
.Integer
:
353 raise ConfigError('"magic" field in trace packet header type must be an integer type')
355 if field_type
.signed
or field_type
.size
!= 32:
356 raise ConfigError('"magic" field in trace packet header type must be a 32-bit unsigned integer type')
359 raise ConfigError('"magic" field must be the first trace packet header type\'s field')
360 elif field_name
== 'stream_id':
361 if type(field_type
) is not metadata
.Integer
:
362 raise ConfigError('"stream_id" field in trace packet header type must be an integer type')
364 if field_type
.signed
:
365 raise ConfigError('"stream_id" field in trace packet header type must be an unsigned integer type')
366 elif field_name
== 'uuid':
367 if self
._meta
.trace
.uuid
is None:
368 raise ConfigError('"uuid" field in trace packet header type specified, but no trace UUID provided')
370 if type(field_type
) is not metadata
.Array
:
371 raise ConfigError('"uuid" field in trace packet header type must be an array')
373 if field_type
.length
!= 16:
374 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
376 element_type
= field_type
.element_type
378 if type(element_type
) is not metadata
.Integer
:
379 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
381 if element_type
.size
!= 8:
382 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
384 if element_type
.signed
:
385 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
387 if element_type
.align
!= 8:
388 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned, byte-aligned bytes')
390 def _validate_trace(self
, meta
):
391 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
393 def _validate_stream_packet_context(self
, stream
):
394 t
= stream
.packet_context_type
399 if type(t
) is not metadata
.Struct
:
402 # "timestamp_begin", if exists, is an unsigned integer type,
404 if 'timestamp_begin' in t
.fields
:
405 ts_begin
= t
.fields
['timestamp_begin']
407 if type(ts_begin
) is not metadata
.Integer
:
408 raise ConfigError('"timestamp_begin" field in stream packet context type must be an integer type')
411 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
413 if not ts_begin
.property_mappings
:
414 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
416 # "timestamp_end", if exists, is an unsigned integer type,
418 if 'timestamp_end' in t
.fields
:
419 ts_end
= t
.fields
['timestamp_end']
421 if type(ts_end
) is not metadata
.Integer
:
422 raise ConfigError('"timestamp_end" field in stream packet context type must be an integer type')
425 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
427 if not ts_end
.property_mappings
:
428 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
430 # "timestamp_begin" and "timestamp_end" exist together
431 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
432 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
434 # "events_discarded", if exists, is an unsigned integer type
435 if 'events_discarded' in t
.fields
:
436 events_discarded
= t
.fields
['events_discarded']
438 if type(events_discarded
) is not metadata
.Integer
:
439 raise ConfigError('"events_discarded" field in stream packet context type must be an integer type')
441 if events_discarded
.signed
:
442 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
444 # "packet_size" and "content_size" must exist
445 if 'packet_size' not in t
.fields
:
446 raise ConfigError('missing "packet_size" field in stream packet context type')
448 packet_size
= t
.fields
['packet_size']
450 # "content_size" and "content_size" must exist
451 if 'content_size' not in t
.fields
:
452 raise ConfigError('missing "content_size" field in stream packet context type')
454 content_size
= t
.fields
['content_size']
456 # "packet_size" is an unsigned integer type
457 if type(packet_size
) is not metadata
.Integer
:
458 raise ConfigError('"packet_size" field in stream packet context type must be an integer type')
460 if packet_size
.signed
:
461 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
463 # "content_size" is an unsigned integer type
464 if type(content_size
) is not metadata
.Integer
:
465 raise ConfigError('"content_size" field in stream packet context type must be an integer type')
467 if content_size
.signed
:
468 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
470 def _validate_stream_event_header(self
, stream
):
471 t
= stream
.event_header_type
474 if len(stream
.events
) > 1:
477 raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
479 if type(t
) is not metadata
.Struct
:
480 raise ConfigError('need "id" field in stream event header type, but stream event header type is not a structure type')
482 if 'id' not in t
.fields
:
483 raise ConfigError('need "id" field in stream event header type')
485 # validate "id" and "timestamp" types
486 if type(t
) is not metadata
.Struct
:
489 # "timestamp", if exists, is an unsigned integer type,
491 if 'timestamp' in t
.fields
:
492 ts
= t
.fields
['timestamp']
494 if type(ts
) is not metadata
.Integer
:
495 raise ConfigError('"ts" field in stream event header type must be an integer type')
498 raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
500 if not ts
.property_mappings
:
501 raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
503 # "id" is an unsigned integer type
507 if type(eid
) is not metadata
.Integer
:
508 raise ConfigError('"id" field in stream event header type must be an integer type')
511 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
513 def _validate_stream(self
, stream
):
514 self
._validate
_stream
_packet
_context
(stream
)
515 self
._validate
_stream
_event
_header
(stream
)
517 def validate(self
, meta
):
519 self
._validate
_trace
(meta
)
521 for stream
in meta
.streams
.values():
523 self
._validate
_stream
(stream
)
524 except Exception as e
:
525 raise ConfigError('invalid stream "{}"'.format(stream
.name
), e
)
528 class _MetadataDynamicTypesValidatorStackEntry
:
529 def __init__(self
, base_t
):
530 self
._base
_t
= base_t
538 def index(self
, value
):
546 def base_t(self
, value
):
550 # Entities. Order of values is important here.
552 class _Entity(enum
.IntEnum
):
553 TRACE_PACKET_HEADER
= 0
554 STREAM_PACKET_CONTEXT
= 1
555 STREAM_EVENT_HEADER
= 2
556 STREAM_EVENT_CONTEXT
= 3
561 # This validator validates dynamic metadata types, that is, it ensures
562 # variable-length array lengths and variant tags actually point to
563 # something that exists. It also checks that variable-length array
564 # lengths point to integer types and variant tags to enumeration types.
565 class _MetadataDynamicTypesValidator
:
567 self
._type
_to
_visit
_type
_func
= {
568 metadata
.Integer
: None,
569 metadata
.FloatingPoint
: None,
571 metadata
.String
: None,
572 metadata
.Struct
: self
._visit
_struct
_type
,
573 metadata
.Array
: self
._visit
_array
_type
,
574 metadata
.Variant
: self
._visit
_variant
_type
,
577 self
._cur
_trace
= None
578 self
._cur
_stream
= None
579 self
._cur
_event
= None
581 def _lookup_path_from_base(self
, path
, parts
, base
, start_index
,
582 base_is_current
, from_t
):
587 while index
< len(parts
):
591 if type(cur_t
) is metadata
.Struct
:
592 enumerated_items
= enumerate(cur_t
.fields
.items())
595 for i
, (field_name
, field_type
) in enumerated_items
:
596 if field_name
== part
:
598 found_path
.append((i
, field_type
))
601 raise ConfigError('invalid path "{}": cannot find field "{}" in structure type'.format(path
, part
))
602 elif type(cur_t
) is metadata
.Variant
:
603 enumerated_items
= enumerate(cur_t
.types
.items())
606 for i
, (type_name
, type_type
) in enumerated_items
:
607 if type_name
== part
:
609 found_path
.append((i
, type_type
))
612 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path
, part
))
614 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path
, part
))
619 # make sure that the pointed type is not the pointing type
621 raise ConfigError('invalid path "{}": pointing to self'.format(path
))
623 # if we're here, we found the type; however, it could be located
624 # _after_ the variant/VLA looking for it, if the pointing
625 # and pointed types are in the same entity, so compare the
626 # current stack entries indexes to our index path in that case
627 if not base_is_current
:
630 for index
, entry
in enumerate(self
._stack
):
631 if index
== len(found_path
):
632 # end of index path; valid so far
635 if found_path
[index
][0] > entry
.index
:
636 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path
))
638 # also make sure that both pointed and pointing types share
639 # a common structure ancestor
640 for index
, entry
in enumerate(self
._stack
):
641 if index
== len(found_path
):
644 if entry
.base_t
is not found_path
[index
][1]:
645 # found common ancestor
646 if type(entry
.base_t
) is metadata
.Variant
:
647 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
651 def _lookup_path_from_top(self
, path
, parts
):
653 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path
))
656 index
= len(self
._stack
) - 1
659 # check stack entries in reversed order
660 for entry
in reversed(self
._stack
):
661 # structure base type
662 if type(entry
.base_t
) is metadata
.Struct
:
664 enumerated_items
= enumerate(entry
.base_t
.fields
.items())
666 # lookup each field, until the current visiting index is met
667 for i
, (field_name
, field_type
) in enumerated_items
:
671 if field_name
== find_name
:
675 elif type(entry
.base_t
) is metadata
.Variant
:
676 enumerated_items
= enumerate(entry
.base_t
.types
.items())
678 # lookup each type, until the current visiting index is met
679 for i
, (type_name
, type_type
) in enumerated_items
:
683 if type_name
== find_name
:
685 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
689 # nothing returned here: cannot find type
690 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path
))
692 def _lookup_path(self
, path
, from_t
):
693 parts
= path
.lower().split('.')
695 base_is_current
= False
698 if parts
[0] == 'trace':
699 if parts
[1] == 'packet' and parts
[2] == 'header':
700 # make sure packet header exists
701 if self
._cur
_trace
.packet_header_type
is None:
702 raise ConfigError('invalid path "{}": no defined trace packet header type'.format(path
))
704 base
= self
._cur
_trace
.packet_header_type
706 if self
._cur
_entity
== _Entity
.TRACE_PACKET_HEADER
:
707 base_is_current
= True
709 raise ConfigError('invalid path "{}": unknown names after "trace"'.format(path
))
710 elif parts
[0] == 'stream':
711 if parts
[1] == 'packet' and parts
[2] == 'context':
712 if self
._cur
_entity
< _Entity
.STREAM_PACKET_CONTEXT
:
713 raise ConfigError('invalid path "{}": cannot access stream packet context here'.format(path
))
715 if self
._cur
_stream
.packet_context_type
is None:
716 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path
))
718 base
= self
._cur
_stream
.packet_context_type
720 if self
._cur
_entity
== _Entity
.STREAM_PACKET_CONTEXT
:
721 base_is_current
= True
722 elif parts
[1] == 'event':
723 if parts
[2] == 'header':
724 if self
._cur
_entity
< _Entity
.STREAM_EVENT_HEADER
:
725 raise ConfigError('invalid path "{}": cannot access stream event header here'.format(path
))
727 if self
._cur
_stream
.event_header_type
is None:
728 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path
))
730 base
= self
._cur
_stream
.event_header_type
732 if self
._cur
_entity
== _Entity
.STREAM_EVENT_HEADER
:
733 base_is_current
= True
734 elif parts
[2] == 'context':
735 if self
._cur
_entity
< _Entity
.STREAM_EVENT_CONTEXT
:
736 raise ConfigError('invalid path "{}": cannot access stream event context here'.format(path
))
738 if self
._cur
_stream
.event_context_type
is None:
739 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path
))
741 base
= self
._cur
_stream
.event_context_type
743 if self
._cur
_entity
== _Entity
.STREAM_EVENT_CONTEXT
:
744 base_is_current
= True
746 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path
))
748 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path
))
753 if len(parts
) >= 2 and base
is None:
754 if parts
[0] == 'event':
755 if parts
[1] == 'context':
756 if self
._cur
_entity
< _Entity
.EVENT_CONTEXT
:
757 raise ConfigError('invalid path "{}": cannot access event context here'.format(path
))
759 if self
._cur
_event
.context_type
is None:
760 raise ConfigError('invalid path "{}": no defined event context type'.format(path
))
762 base
= self
._cur
_event
.context_type
764 if self
._cur
_entity
== _Entity
.EVENT_CONTEXT
:
765 base_is_current
= True
766 elif parts
[1] == 'payload' or parts
[1] == 'fields':
767 if self
._cur
_entity
< _Entity
.EVENT_PAYLOAD
:
768 raise ConfigError('invalid path "{}": cannot access event payload here'.format(path
))
770 if self
._cur
_event
.payload_type
is None:
771 raise ConfigError('invalid path "{}": no defined event payload type'.format(path
))
773 base
= self
._cur
_event
.payload_type
775 if self
._cur
_entity
== _Entity
.EVENT_PAYLOAD
:
776 base_is_current
= True
778 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path
))
784 return self
._lookup
_path
_from
_base
(path
, parts
, base
, start_index
,
785 base_is_current
, from_t
)
787 return self
._lookup
_path
_from
_top
(path
, parts
)
789 def _stack_reset(self
):
792 def _stack_push(self
, base_t
):
793 entry
= _MetadataDynamicTypesValidatorStackEntry(base_t
)
794 self
._stack
.append(entry
)
796 def _stack_pop(self
):
799 def _stack_incr_index(self
):
800 self
._stack
[-1].index
+= 1
802 def _visit_struct_type(self
, t
):
805 for field_name
, field_type
in t
.fields
.items():
807 self
._visit
_type
(field_type
)
808 except Exception as e
:
809 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
811 self
._stack
_incr
_index
()
815 def _visit_array_type(self
, t
):
816 if t
.is_variable_length
:
819 length_type
= self
._lookup
_path
(t
.length
, t
)
820 except Exception as e
:
821 raise ConfigError('invalid array type\'s length', e
)
823 # make sure length type an unsigned integer
824 if type(length_type
) is not metadata
.Integer
:
825 raise ConfigError('array type\'s length does not point to an integer type')
827 if length_type
.signed
:
828 raise ConfigError('array type\'s length does not point to an unsigned integer type')
830 self
._visit
_type
(t
.element_type
)
832 def _visit_variant_type(self
, t
):
835 tag_type
= self
._lookup
_path
(t
.tag
, t
)
836 except Exception as e
:
837 raise ConfigError('invalid variant type\'s tag', e
)
839 # make sure tag type is an enumeration
840 if type(tag_type
) is not metadata
.Enum
:
841 raise ConfigError('variant type\'s tag does not point to an enumeration type')
843 # verify that each variant type's type exists as an enumeration member
844 for tag_name
in t
.types
.keys():
845 if tag_name
not in tag_type
.members
:
846 raise ConfigError('cannot find variant type\'s type "{}" in pointed tag type'.format(tag_name
))
850 for type_name
, type_type
in t
.types
.items():
852 self
._visit
_type
(type_type
)
853 except Exception as e
:
854 raise ConfigError('in variant type\'s type "{}"'.format(type_name
), e
)
856 self
._stack
_incr
_index
()
860 def _visit_type(self
, t
):
864 if type(t
) in self
._type
_to
_visit
_type
_func
:
865 func
= self
._type
_to
_visit
_type
_func
[type(t
)]
870 def _visit_event(self
, ev
):
876 # visit event context type
878 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
881 self
._visit
_type
(ev
.context_type
)
882 except Exception as e
:
883 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
885 # visit event payload type
887 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
890 self
._visit
_type
(ev
.payload_type
)
891 except Exception as e
:
892 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
894 def _visit_stream(self
, stream
):
895 stream_name
= stream
.name
898 self
._cur
_stream
= stream
900 # reset current event
901 self
._cur
_event
= None
903 # visit stream packet context type
905 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
908 self
._visit
_type
(stream
.packet_context_type
)
909 except Exception as e
:
910 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
912 # visit stream event header type
914 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
917 self
._visit
_type
(stream
.event_header_type
)
918 except Exception as e
:
919 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
921 # visit stream event context type
923 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
926 self
._visit
_type
(stream
.event_context_type
)
927 except Exception as e
:
928 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
931 for ev
in stream
.events
.values():
933 self
._visit
_event
(ev
)
934 except Exception as e
:
935 raise ConfigError('invalid stream "{}"'.format(stream_name
))
937 def validate(self
, meta
):
939 self
._cur
_trace
= meta
.trace
941 # visit trace packet header type
943 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
946 self
._visit
_type
(meta
.trace
.packet_header_type
)
947 except Exception as e
:
948 raise ConfigError('invalid packet header type in trace', e
)
951 for stream
in meta
.streams
.values():
952 self
._visit
_stream
(stream
)
955 # Since type inheritance allows types to be only partially defined at
956 # any place in the configuration, this validator validates that actual
957 # trace, stream, and event types are all complete and valid.
958 class _MetadataTypesHistologyValidator
:
960 self
._type
_to
_validate
_type
_histology
_func
= {
961 metadata
.Integer
: self
._validate
_integer
_histology
,
962 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
963 metadata
.Enum
: self
._validate
_enum
_histology
,
964 metadata
.String
: self
._validate
_string
_histology
,
965 metadata
.Struct
: self
._validate
_struct
_histology
,
966 metadata
.Array
: self
._validate
_array
_histology
,
967 metadata
.Variant
: self
._validate
_variant
_histology
,
970 def _validate_integer_histology(self
, t
):
973 raise ConfigError('missing integer type\'s size')
975 def _validate_float_histology(self
, t
):
976 # exponent digits is set
977 if t
.exp_size
is None:
978 raise ConfigError('missing floating point number type\'s exponent size')
980 # mantissa digits is set
981 if t
.mant_size
is None:
982 raise ConfigError('missing floating point number type\'s mantissa size')
984 # exponent and mantissa sum is a multiple of 8
985 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
986 raise ConfigError('floating point number type\'s mantissa and exponent sizes sum must be a multiple of 8')
988 def _validate_enum_histology(self
, t
):
989 # integer type is set
990 if t
.value_type
is None:
991 raise ConfigError('missing enumeration type\'s value type')
993 # there's at least one member
995 raise ConfigError('enumeration type needs at least one member')
997 # no overlapping values and all values are valid considering
1001 if t
.value_type
.signed
:
1002 value_min
= -(1 << t
.value_type
.size
- 1)
1003 value_max
= (1 << (t
.value_type
.size
- 1)) - 1
1006 value_max
= (1 << t
.value_type
.size
) - 1
1008 for label
, value
in t
.members
.items():
1010 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
1011 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label
))
1013 fmt
= 'enumeration type\'s member "{}": value {} is outside the value type range [{}, {}]'
1015 if value
[0] < value_min
or value
[0] > value_max
:
1016 raise ConfigError(fmt
.format(label
, value
[0], value_min
, value_max
))
1018 if value
[1] < value_min
or value
[1] > value_max
:
1019 raise ConfigError(fmt
.format(label
, value
[1], value_min
, value_max
))
1021 ranges
.append(value
)
1023 def _validate_string_histology(self
, t
):
1027 def _validate_struct_histology(self
, t
):
1028 # all fields are valid
1029 for field_name
, field_type
in t
.fields
.items():
1031 self
._validate
_type
_histology
(field_type
)
1032 except Exception as e
:
1033 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name
), e
)
1035 def _validate_array_histology(self
, t
):
1037 if t
.length
is None:
1038 raise ConfigError('missing array type\'s length')
1040 # element type is set
1041 if t
.element_type
is None:
1042 raise ConfigError('missing array type\'s element type')
1044 # element type is valid
1046 self
._validate
_type
_histology
(t
.element_type
)
1047 except Exception as e
:
1048 raise ConfigError('invalid array type\'s element type', e
)
1050 def _validate_variant_histology(self
, t
):
1053 raise ConfigError('missing variant type\'s tag')
1055 # there's at least one type
1057 raise ConfigError('variant type needs at least one type')
1059 # all types are valid
1060 for type_name
, type_t
in t
.types
.items():
1062 self
._validate
_type
_histology
(type_t
)
1063 except Exception as e
:
1064 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name
), e
)
1066 def _validate_type_histology(self
, t
):
1070 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
1072 def _validate_entity_type_histology(self
, t
):
1076 if type(t
) is not metadata
.Struct
:
1077 raise ConfigError('expecting a structure type')
1079 self
._validate
_type
_histology
(t
)
1081 def _validate_event_types_histology(self
, ev
):
1084 # validate event context type
1086 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1087 except Exception as e
:
1088 raise ConfigError('invalid event context type for event "{}"'.format(ev_name
), e
)
1090 # validate event payload type
1091 if ev
.payload_type
is None:
1092 raise ConfigError('event payload type must exist in event "{}"'.format(ev_name
))
1094 # TODO: also check arrays, sequences, and variants
1095 if type(ev
.payload_type
) is metadata
.Struct
:
1096 if not ev
.payload_type
.fields
:
1097 raise ConfigError('event payload type must have at least one field for event "{}"'.format(ev_name
))
1100 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1101 except Exception as e
:
1102 raise ConfigError('invalid event payload type for event "{}"'.format(ev_name
), e
)
1104 def _validate_stream_types_histology(self
, stream
):
1105 stream_name
= stream
.name
1107 # validate stream packet context type
1109 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1110 except Exception as e
:
1111 raise ConfigError('invalid stream packet context type for stream "{}"'.format(stream_name
), e
)
1113 # validate stream event header type
1115 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1116 except Exception as e
:
1117 raise ConfigError('invalid stream event header type for stream "{}"'.format(stream_name
), e
)
1119 # validate stream event context type
1121 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1122 except Exception as e
:
1123 raise ConfigError('invalid stream event context type for stream "{}"'.format(stream_name
), e
)
1126 for ev
in stream
.events
.values():
1128 self
._validate
_event
_types
_histology
(ev
)
1129 except Exception as e
:
1130 raise ConfigError('invalid event in stream "{}"'.format(stream_name
), e
)
1132 def validate(self
, meta
):
1133 # validate trace packet header type
1135 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1136 except Exception as e
:
1137 raise ConfigError('invalid trace packet header type', e
)
1140 for stream
in meta
.streams
.values():
1141 self
._validate
_stream
_types
_histology
(stream
)
1144 class _YamlConfigParser
:
1145 def __init__(self
, include_dirs
, ignore_include_not_found
, dump_config
):
1146 self
._class
_name
_to
_create
_type
_func
= {
1147 'int': self
._create
_integer
,
1148 'integer': self
._create
_integer
,
1149 'flt': self
._create
_float
,
1150 'float': self
._create
_float
,
1151 'floating-point': self
._create
_float
,
1152 'enum': self
._create
_enum
,
1153 'enumeration': self
._create
_enum
,
1154 'str': self
._create
_string
,
1155 'string': self
._create
_string
,
1156 'struct': self
._create
_struct
,
1157 'structure': self
._create
_struct
,
1158 'array': self
._create
_array
,
1159 'var': self
._create
_variant
,
1160 'variant': self
._create
_variant
,
1162 self
._type
_to
_create
_type
_func
= {
1163 metadata
.Integer
: self
._create
_integer
,
1164 metadata
.FloatingPoint
: self
._create
_float
,
1165 metadata
.Enum
: self
._create
_enum
,
1166 metadata
.String
: self
._create
_string
,
1167 metadata
.Struct
: self
._create
_struct
,
1168 metadata
.Array
: self
._create
_array
,
1169 metadata
.Variant
: self
._create
_variant
,
1171 self
._include
_dirs
= include_dirs
1172 self
._ignore
_include
_not
_found
= ignore_include_not_found
1173 self
._dump
_config
= dump_config
1175 def _set_byte_order(self
, metadata_node
):
1176 if 'trace' not in metadata_node
:
1177 raise ConfigError('missing "trace" property (metadata)')
1179 trace_node
= metadata_node
['trace']
1181 if not _is_assoc_array_prop(trace_node
):
1182 raise ConfigError('"trace" property (metadata) must be an associative array')
1184 if 'byte-order' not in trace_node
:
1185 raise ConfigError('missing "byte-order" property (trace)')
1187 bo_node
= trace_node
['byte-order']
1189 if not _is_str_prop(bo_node
):
1190 raise ConfigError('"byte-order" property of trace object must be a string ("le" or "be")')
1192 self
._bo
= _byte_order_str_to_bo(bo_node
)
1194 if self
._bo
is None:
1195 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1197 def _lookup_type_alias(self
, name
):
1198 if name
in self
._tas
:
1199 return copy
.deepcopy(self
._tas
[name
])
1201 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1202 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1205 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop
))
1207 if 'name' not in prop_mapping_node
:
1208 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1210 if 'property' not in prop_mapping_node
:
1211 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1213 clock_name
= prop_mapping_node
['name']
1214 prop
= prop_mapping_node
['property']
1216 if not _is_str_prop(clock_name
):
1217 raise ConfigError('"name" property of integer type object\'s clock property mapping must be a string')
1219 if not _is_str_prop(prop
):
1220 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1222 if clock_name
not in self
._clocks
:
1223 raise ConfigError('invalid clock name "{}" in integer type object\'s clock property mapping'.format(clock_name
))
1226 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop
))
1228 mapped_clock
= self
._clocks
[clock_name
]
1229 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
1231 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
1232 kp
= known_props
+ ['inherit', 'class']
1234 if self
._version
>= 201:
1235 kp
.append('$inherit')
1237 return _get_first_unknown_prop(type_node
, kp
)
1239 def _create_integer(self
, obj
, node
):
1241 # create integer object
1242 obj
= metadata
.Integer()
1244 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1251 'property-mappings',
1255 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop
))
1261 if not _is_int_prop(size
):
1262 raise ConfigError('"size" property of integer type object must be an integer')
1265 raise ConfigError('invalid integer size: {}'.format(size
))
1271 align
= node
['align']
1273 if not _is_int_prop(align
):
1274 raise ConfigError('"align" property of integer type object must be an integer')
1276 if not _is_valid_alignment(align
):
1277 raise ConfigError('invalid alignment: {}'.format(align
))
1282 if 'signed' in node
:
1283 signed
= node
['signed']
1285 if not _is_bool_prop(signed
):
1286 raise ConfigError('"signed" property of integer type object must be a boolean')
1291 if 'byte-order' in node
:
1292 byte_order
= node
['byte-order']
1294 if not _is_str_prop(byte_order
):
1295 raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
1297 byte_order
= _byte_order_str_to_bo(byte_order
)
1299 if byte_order
is None:
1300 raise ConfigError('invalid "byte-order" property in integer type object')
1302 byte_order
= self
._bo
1304 obj
.byte_order
= byte_order
1310 if not _is_str_prop(base
):
1311 raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1322 raise ConfigError('unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base
))
1327 if 'encoding' in node
:
1328 encoding
= node
['encoding']
1330 if not _is_str_prop(encoding
):
1331 raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1333 encoding
= _encoding_str_to_encoding(encoding
)
1335 if encoding
is None:
1336 raise ConfigError('invalid "encoding" property in integer type object')
1338 obj
.encoding
= encoding
1341 if 'property-mappings' in node
:
1342 prop_mappings
= node
['property-mappings']
1344 if not _is_array_prop(prop_mappings
):
1345 raise ConfigError('"property-mappings" property of integer type object must be an array')
1347 if len(prop_mappings
) > 1:
1348 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1350 del obj
.property_mappings
[:]
1352 for index
, prop_mapping
in enumerate(prop_mappings
):
1353 if not _is_assoc_array_prop(prop_mapping
):
1354 raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
1356 if 'type' not in prop_mapping
:
1357 raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1359 prop_type
= prop_mapping
['type']
1361 if not _is_str_prop(prop_type
):
1362 raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1364 if prop_type
== 'clock':
1365 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1367 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1371 def _create_float(self
, obj
, node
):
1373 # create floating point number object
1374 obj
= metadata
.FloatingPoint()
1376 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1383 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop
))
1389 if not _is_assoc_array_prop(size
):
1390 raise ConfigError('"size" property of floating point number type object must be an associative array')
1392 unk_prop
= _get_first_unknown_prop(size
, ['exp', 'mant'])
1395 raise ConfigError('unknown floating point number type object\'s "size" property: "{}"'.format(unk_prop
))
1400 if not _is_int_prop(exp
):
1401 raise ConfigError('"exp" property of floating point number type object\'s "size" property must be an integer')
1404 raise ConfigError('invalid floating point number exponent size: {}')
1411 if not _is_int_prop(mant
):
1412 raise ConfigError('"mant" property of floating point number type object\'s "size" property must be an integer')
1415 raise ConfigError('invalid floating point number mantissa size: {}')
1417 obj
.mant_size
= mant
1421 align
= node
['align']
1423 if not _is_int_prop(align
):
1424 raise ConfigError('"align" property of floating point number type object must be an integer')
1426 if not _is_valid_alignment(align
):
1427 raise ConfigError('invalid alignment: {}'.format(align
))
1432 if 'byte-order' in node
:
1433 byte_order
= node
['byte-order']
1435 if not _is_str_prop(byte_order
):
1436 raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
1438 byte_order
= _byte_order_str_to_bo(byte_order
)
1440 if byte_order
is None:
1441 raise ConfigError('invalid "byte-order" property in floating point number type object')
1443 byte_order
= self
._bo
1445 obj
.byte_order
= byte_order
1449 def _create_enum(self
, obj
, node
):
1451 # create enumeration object
1452 obj
= metadata
.Enum()
1454 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1460 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop
))
1463 if 'value-type' in node
:
1465 obj
.value_type
= self
._create
_type
(node
['value-type'])
1466 except Exception as e
:
1467 raise ConfigError('cannot create enumeration type\'s integer type', e
)
1470 if 'members' in node
:
1471 members_node
= node
['members']
1473 if not _is_array_prop(members_node
):
1474 raise ConfigError('"members" property of enumeration type object must be an array')
1478 for index
, m_node
in enumerate(members_node
):
1479 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1480 raise ConfigError('invalid enumeration member #{}: expecting a string or an associative array'.format(index
))
1482 if _is_str_prop(m_node
):
1487 unk_prop
= _get_first_unknown_prop(m_node
, [
1493 raise ConfigError('unknown enumeration type member object property: "{}"'.format(unk_prop
))
1495 if 'label' not in m_node
:
1496 raise ConfigError('missing "label" property in enumeration member #{}'.format(index
))
1498 label
= m_node
['label']
1500 if not _is_str_prop(label
):
1501 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index
))
1503 if 'value' not in m_node
:
1504 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label
))
1506 value
= m_node
['value']
1508 if not _is_int_prop(value
) and not _is_array_prop(value
):
1509 raise ConfigError('invalid enumeration member ("{}"): expecting an integer or an array'.format(label
))
1511 if _is_int_prop(value
):
1513 value
= (value
, value
)
1516 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label
))
1522 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1527 obj
.members
[label
] = value
1531 def _create_string(self
, obj
, node
):
1533 # create string object
1534 obj
= metadata
.String()
1536 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1541 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1544 if 'encoding' in node
:
1545 encoding
= node
['encoding']
1547 if not _is_str_prop(encoding
):
1548 raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
1550 encoding
= _encoding_str_to_encoding(encoding
)
1552 if encoding
is None:
1553 raise ConfigError('invalid "encoding" property in string type object')
1555 obj
.encoding
= encoding
1559 def _create_struct(self
, obj
, node
):
1561 # create structure object
1562 obj
= metadata
.Struct()
1564 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1570 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1573 if 'min-align' in node
:
1574 min_align
= node
['min-align']
1576 if not _is_int_prop(min_align
):
1577 raise ConfigError('"min-align" property of structure type object must be an integer')
1579 if not _is_valid_alignment(min_align
):
1580 raise ConfigError('invalid minimum alignment: {}'.format(min_align
))
1582 obj
.min_align
= min_align
1585 if 'fields' in node
:
1586 fields
= node
['fields']
1588 if not _is_assoc_array_prop(fields
):
1589 raise ConfigError('"fields" property of structure type object must be an associative array')
1591 for field_name
, field_node
in fields
.items():
1592 if not is_valid_identifier(field_name
):
1593 raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name
))
1596 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1597 except Exception as e
:
1598 raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name
), e
)
1602 def _create_array(self
, obj
, node
):
1604 # create array object
1605 obj
= metadata
.Array()
1607 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1613 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop
))
1616 if 'length' in node
:
1617 length
= node
['length']
1619 if not _is_int_prop(length
) and not _is_str_prop(length
):
1620 raise ConfigError('"length" property of array type object must be an integer or a string')
1622 if type(length
) is int and length
< 0:
1623 raise ConfigError('invalid static array length: {}'.format(length
))
1628 if 'element-type' in node
:
1630 obj
.element_type
= self
._create
_type
(node
['element-type'])
1631 except Exception as e
:
1632 raise ConfigError('cannot create array type\'s element type', e
)
1636 def _create_variant(self
, obj
, node
):
1638 # create variant object
1639 obj
= metadata
.Variant()
1641 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1647 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop
))
1653 if not _is_str_prop(tag
):
1654 raise ConfigError('"tag" property of variant type object must be a string')
1656 # do not validate variant tag for the moment; will be done in a
1662 types
= node
['types']
1664 if not _is_assoc_array_prop(types
):
1665 raise ConfigError('"types" property of variant type object must be an associative array')
1667 # do not validate type names for the moment; will be done in a
1669 for type_name
, type_node
in types
.items():
1670 if not is_valid_identifier(type_name
):
1671 raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name
))
1674 obj
.types
[type_name
] = self
._create
_type
(type_node
)
1675 except Exception as e
:
1676 raise ConfigError('cannot create variant type\'s type "{}"'.format(type_name
), e
)
1680 def _create_type(self
, type_node
):
1681 if type(type_node
) is str:
1682 t
= self
._lookup
_type
_alias
(type_node
)
1685 raise ConfigError('unknown type alias "{}"'.format(type_node
))
1689 if not _is_assoc_array_prop(type_node
):
1690 raise ConfigError('type objects must be associative arrays or strings (type alias name)')
1697 if self
._version
>= 200:
1698 if 'inherit' in type_node
:
1699 inherit_prop
= 'inherit'
1700 inherit_node
= type_node
[inherit_prop
]
1702 if self
._version
>= 201:
1703 if '$inherit' in type_node
:
1704 if inherit_node
is not None:
1705 raise ConfigError('cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1707 inherit_prop
= '$inherit'
1708 inherit_node
= type_node
[inherit_prop
]
1710 if inherit_node
is not None and 'class' in type_node
:
1711 raise ConfigError('cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1713 if inherit_node
is not None:
1714 if not _is_str_prop(inherit_node
):
1715 raise ConfigError('"{}" property of type object must be a string'.format(inherit_prop
))
1717 base
= self
._lookup
_type
_alias
(inherit_node
)
1720 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1722 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1724 if 'class' not in type_node
:
1725 raise ConfigError('type objects which do not inherit must have a "class" property')
1727 class_name
= type_node
['class']
1729 if type(class_name
) is not str:
1730 raise ConfigError('type objects\' "class" property must be a string')
1732 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1733 raise ConfigError('unknown type class "{}"'.format(class_name
))
1736 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1738 return func(base
, type_node
)
1740 def _register_type_aliases(self
, metadata_node
):
1743 if 'type-aliases' not in metadata_node
:
1746 ta_node
= metadata_node
['type-aliases']
1748 if not _is_assoc_array_prop(ta_node
):
1749 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1751 for ta_name
, ta_type
in ta_node
.items():
1752 if ta_name
in self
._tas
:
1753 raise ConfigError('duplicate type alias "{}"'.format(ta_name
))
1756 t
= self
._create
_type
(ta_type
)
1757 except Exception as e
:
1758 raise ConfigError('cannot create type alias "{}"'.format(ta_name
), e
)
1760 self
._tas
[ta_name
] = t
1762 def _create_clock(self
, node
):
1763 # create clock object
1764 clock
= metadata
.Clock()
1766 if not _is_assoc_array_prop(node
):
1767 raise ConfigError('clock objects must be associative arrays')
1779 if self
._version
>= 201:
1780 known_props
.append('$return-ctype')
1782 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1785 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop
))
1789 uuidp
= node
['uuid']
1791 if not _is_str_prop(uuidp
):
1792 raise ConfigError('"uuid" property of clock object must be a string')
1795 uuidp
= uuid
.UUID(uuidp
)
1797 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp
))
1802 if 'description' in node
:
1803 desc
= node
['description']
1805 if not _is_str_prop(desc
):
1806 raise ConfigError('"description" property of clock object must be a string')
1808 clock
.description
= desc
1814 if not _is_int_prop(freq
):
1815 raise ConfigError('"freq" property of clock object must be an integer')
1818 raise ConfigError('invalid clock frequency: {}'.format(freq
))
1823 if 'error-cycles' in node
:
1824 error_cycles
= node
['error-cycles']
1826 if not _is_int_prop(error_cycles
):
1827 raise ConfigError('"error-cycles" property of clock object must be an integer')
1829 if error_cycles
< 0:
1830 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles
))
1832 clock
.error_cycles
= error_cycles
1835 if 'offset' in node
:
1836 offset
= node
['offset']
1838 if not _is_assoc_array_prop(offset
):
1839 raise ConfigError('"offset" property of clock object must be an associative array')
1841 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1844 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop
))
1847 if 'cycles' in offset
:
1848 offset_cycles
= offset
['cycles']
1850 if not _is_int_prop(offset_cycles
):
1851 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1853 if offset_cycles
< 0:
1854 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles
))
1856 clock
.offset_cycles
= offset_cycles
1859 if 'seconds' in offset
:
1860 offset_seconds
= offset
['seconds']
1862 if not _is_int_prop(offset_seconds
):
1863 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1865 if offset_seconds
< 0:
1866 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds
))
1868 clock
.offset_seconds
= offset_seconds
1871 if 'absolute' in node
:
1872 absolute
= node
['absolute']
1874 if not _is_bool_prop(absolute
):
1875 raise ConfigError('"absolute" property of clock object must be a boolean')
1877 clock
.absolute
= absolute
1880 # v2.0: "return-ctype"
1881 # v2.1+: "$return-ctype"
1882 return_ctype_node
= None
1884 if self
._version
>= 200:
1885 if 'return-ctype' in node
:
1886 return_ctype_prop
= 'return-ctype'
1887 return_ctype_node
= node
[return_ctype_prop
]
1889 if self
._version
>= 201:
1890 if '$return-ctype' in node
:
1891 if return_ctype_node
is not None:
1892 raise ConfigError('cannot specify both "return-ctype" and "$return-ctype" properties of clock object: prefer "$return-ctype"')
1894 return_ctype_prop
= '$return-ctype'
1895 return_ctype_node
= node
[return_ctype_prop
]
1897 if return_ctype_node
is not None:
1898 if not _is_str_prop(return_ctype_node
):
1899 raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop
))
1901 clock
.return_ctype
= return_ctype_node
1905 def _register_clocks(self
, metadata_node
):
1906 self
._clocks
= collections
.OrderedDict()
1908 if 'clocks' not in metadata_node
:
1911 clocks_node
= metadata_node
['clocks']
1913 if not _is_assoc_array_prop(clocks_node
):
1914 raise ConfigError('"clocks" property (metadata) must be an associative array')
1916 for clock_name
, clock_node
in clocks_node
.items():
1917 if not is_valid_identifier(clock_name
):
1918 raise ConfigError('invalid clock name: "{}"'.format(clock_name
))
1920 if clock_name
in self
._clocks
:
1921 raise ConfigError('duplicate clock "{}"'.format(clock_name
))
1924 clock
= self
._create
_clock
(clock_node
)
1925 except Exception as e
:
1926 raise ConfigError('cannot create clock "{}"'.format(clock_name
), e
)
1928 clock
.name
= clock_name
1929 self
._clocks
[clock_name
] = clock
1931 def _create_env(self
, metadata_node
):
1932 env
= collections
.OrderedDict()
1934 if 'env' not in metadata_node
:
1937 env_node
= metadata_node
['env']
1939 if not _is_assoc_array_prop(env_node
):
1940 raise ConfigError('"env" property (metadata) must be an associative array')
1942 for env_name
, env_value
in env_node
.items():
1944 raise ConfigError('duplicate environment variable "{}"'.format(env_name
))
1946 if not is_valid_identifier(env_name
):
1947 raise ConfigError('invalid environment variable name: "{}"'.format(env_name
))
1949 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
1950 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
1952 env
[env_name
] = env_value
1956 def _register_log_levels(self
, metadata_node
):
1957 self
._log
_levels
= dict()
1960 # v2.0: "log-levels"
1961 # v2.1+: "$log-levels"
1962 log_levels_node
= None
1964 if self
._version
>= 200:
1965 if 'log-levels' in metadata_node
:
1966 log_levels_prop
= 'log-levels'
1967 log_levels_node
= metadata_node
[log_levels_prop
]
1969 if self
._version
>= 201:
1970 if '$log-levels' in metadata_node
:
1971 if log_levels_node
is not None:
1972 raise ConfigError('cannot specify both "log-levels" and "$log-levels" properties of metadata object: prefer "$log-levels"')
1974 log_levels_prop
= '$log-levels'
1975 log_levels_node
= metadata_node
[log_levels_prop
]
1977 if log_levels_node
is None:
1980 if not _is_assoc_array_prop(log_levels_node
):
1981 raise ConfigError('"{}" property (metadata) must be an associative array'.format(log_levels_prop
))
1983 for ll_name
, ll_value
in log_levels_node
.items():
1984 if ll_name
in self
._log
_levels
:
1985 raise ConfigError('duplicate log level entry "{}"'.format(ll_name
))
1987 if not _is_int_prop(ll_value
):
1988 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name
))
1991 raise ConfigError('invalid log level entry ("{}"): log level value must be positive'.format(ll_name
))
1993 self
._log
_levels
[ll_name
] = ll_value
1995 def _create_trace(self
, metadata_node
):
1996 # create trace object
1997 trace
= metadata
.Trace()
1999 if 'trace' not in metadata_node
:
2000 raise ConfigError('missing "trace" property (metadata)')
2002 trace_node
= metadata_node
['trace']
2004 if not _is_assoc_array_prop(trace_node
):
2005 raise ConfigError('"trace" property (metadata) must be an associative array')
2007 unk_prop
= _get_first_unknown_prop(trace_node
, [
2010 'packet-header-type',
2014 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop
))
2016 # set byte order (already parsed)
2017 trace
.byte_order
= self
._bo
2020 if 'uuid' in trace_node
:
2021 uuidp
= trace_node
['uuid']
2023 if not _is_str_prop(uuidp
):
2024 raise ConfigError('"uuid" property of trace object must be a string')
2027 uuidp
= uuid
.uuid1()
2030 uuidp
= uuid
.UUID(uuidp
)
2032 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp
))
2036 # packet header type
2037 if 'packet-header-type' in trace_node
:
2039 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
2040 except Exception as e
:
2041 raise ConfigError('cannot create packet header type (trace)', e
)
2043 trace
.packet_header_type
= ph_type
2047 def _lookup_log_level(self
, ll
):
2048 if _is_int_prop(ll
):
2050 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
2051 return self
._log
_levels
[ll
]
2053 def _create_event(self
, event_node
):
2054 event
= metadata
.Event()
2056 if not _is_assoc_array_prop(event_node
):
2057 raise ConfigError('event objects must be associative arrays')
2059 unk_prop
= _get_first_unknown_prop(event_node
, [
2066 raise ConfigError('unknown event object property: "{}"'.format(unk_prop
))
2068 if 'log-level' in event_node
:
2069 ll_node
= event_node
['log-level']
2071 if _is_str_prop(ll_node
):
2072 ll
= self
._lookup
_log
_level
(event_node
['log-level'])
2075 raise ConfigError('cannot find log level "{}"'.format(ll_node
))
2076 elif _is_int_prop(ll_node
):
2078 raise ConfigError('invalid log level value {}: value must be positive'.format(ll_node
))
2082 raise ConfigError('"log-level" property must be either a string or an integer')
2084 event
.log_level
= ll
2086 if 'context-type' in event_node
:
2088 t
= self
._create
_type
(event_node
['context-type'])
2089 except Exception as e
:
2090 raise ConfigError('cannot create event\'s context type object', e
)
2092 event
.context_type
= t
2094 if 'payload-type' not in event_node
:
2095 raise ConfigError('missing "payload-type" property in event object')
2098 t
= self
._create
_type
(event_node
['payload-type'])
2099 except Exception as e
:
2100 raise ConfigError('cannot create event\'s payload type object', e
)
2102 event
.payload_type
= t
2106 def _create_stream(self
, stream_node
):
2107 stream
= metadata
.Stream()
2109 if not _is_assoc_array_prop(stream_node
):
2110 raise ConfigError('stream objects must be associative arrays')
2112 unk_prop
= _get_first_unknown_prop(stream_node
, [
2113 'packet-context-type',
2114 'event-header-type',
2115 'event-context-type',
2120 raise ConfigError('unknown stream object property: "{}"'.format(unk_prop
))
2122 if 'packet-context-type' in stream_node
:
2124 t
= self
._create
_type
(stream_node
['packet-context-type'])
2125 except Exception as e
:
2126 raise ConfigError('cannot create stream\'s packet context type object', e
)
2128 stream
.packet_context_type
= t
2130 if 'event-header-type' in stream_node
:
2132 t
= self
._create
_type
(stream_node
['event-header-type'])
2133 except Exception as e
:
2134 raise ConfigError('cannot create stream\'s event header type object', e
)
2136 stream
.event_header_type
= t
2138 if 'event-context-type' in stream_node
:
2140 t
= self
._create
_type
(stream_node
['event-context-type'])
2141 except Exception as e
:
2142 raise ConfigError('cannot create stream\'s event context type object', e
)
2144 stream
.event_context_type
= t
2146 if 'events' not in stream_node
:
2147 raise ConfigError('missing "events" property in stream object')
2149 events
= stream_node
['events']
2151 if not _is_assoc_array_prop(events
):
2152 raise ConfigError('"events" property of stream object must be an associative array')
2155 raise ConfigError('at least one event is needed within a stream object')
2159 for ev_name
, ev_node
in events
.items():
2161 ev
= self
._create
_event
(ev_node
)
2162 except Exception as e
:
2163 raise ConfigError('cannot create event "{}"'.format(ev_name
), e
)
2167 stream
.events
[ev_name
] = ev
2172 def _create_streams(self
, metadata_node
):
2173 streams
= collections
.OrderedDict()
2175 if 'streams' not in metadata_node
:
2176 raise ConfigError('missing "streams" property (metadata)')
2178 streams_node
= metadata_node
['streams']
2180 if not _is_assoc_array_prop(streams_node
):
2181 raise ConfigError('"streams" property (metadata) must be an associative array')
2183 if not streams_node
:
2184 raise ConfigError('at least one stream is needed (metadata)')
2188 for stream_name
, stream_node
in streams_node
.items():
2190 stream
= self
._create
_stream
(stream_node
)
2191 except Exception as e
:
2192 raise ConfigError('cannot create stream "{}"'.format(stream_name
), e
)
2195 stream
.name
= str(stream_name
)
2196 streams
[stream_name
] = stream
2201 def _create_metadata(self
, root
):
2202 meta
= metadata
.Metadata()
2204 if 'metadata' not in root
:
2205 raise ConfigError('missing "metadata" property (configuration)')
2207 metadata_node
= root
['metadata']
2209 if not _is_assoc_array_prop(metadata_node
):
2210 raise ConfigError('"metadata" property (configuration) must be an associative array')
2221 if self
._version
>= 201:
2222 known_props
.append('$log-levels')
2224 unk_prop
= _get_first_unknown_prop(metadata_node
, known_props
)
2229 if unk_prop
== '$include':
2230 add
= ' (use version 2.1 or greater)'
2232 raise ConfigError('unknown metadata property{}: "{}"'.format(add
, unk_prop
))
2234 self
._set
_byte
_order
(metadata_node
)
2235 self
._register
_clocks
(metadata_node
)
2236 meta
.clocks
= self
._clocks
2237 self
._register
_type
_aliases
(metadata_node
)
2238 meta
.env
= self
._create
_env
(metadata_node
)
2239 meta
.trace
= self
._create
_trace
(metadata_node
)
2240 self
._register
_log
_levels
(metadata_node
)
2241 meta
.streams
= self
._create
_streams
(metadata_node
)
2245 def _get_version(self
, root
):
2246 if 'version' not in root
:
2247 raise ConfigError('missing "version" property (configuration)')
2249 version_node
= root
['version']
2251 if not _is_str_prop(version_node
):
2252 raise ConfigError('"version" property (configuration) must be a string')
2254 version_node
= version_node
.strip()
2256 if version_node
not in ['2.0', '2.1']:
2257 raise ConfigError('unsupported version ({}): versions 2.0 and 2.1 are supported'.format(version_node
))
2259 # convert version string to comparable version integer
2260 parts
= version_node
.split('.')
2261 version
= int(parts
[0]) * 100 + int(parts
[1])
2265 def _get_prefix(self
, root
):
2266 if 'prefix' not in root
:
2269 prefix_node
= root
['prefix']
2271 if not _is_str_prop(prefix_node
):
2272 raise ConfigError('"prefix" property (configuration) must be a string')
2274 if not is_valid_identifier(prefix_node
):
2275 raise ConfigError('"prefix" property (configuration) must be a valid C identifier')
2279 def _get_last_include_file(self
):
2280 if self
._include
_stack
:
2281 return self
._include
_stack
[-1]
2283 return self
._root
_yaml
_path
2285 def _load_include(self
, yaml_path
):
2286 for inc_dir
in self
._include
_dirs
:
2287 # current include dir + file name path
2288 # note: os.path.join() only takes the last arg if it's absolute
2289 inc_path
= os
.path
.join(inc_dir
, yaml_path
)
2291 # real path (symbolic links resolved)
2292 real_path
= os
.path
.realpath(inc_path
)
2294 # normalized path (weird stuff removed!)
2295 norm_path
= os
.path
.normpath(real_path
)
2297 if not os
.path
.isfile(norm_path
):
2298 # file does not exist: skip
2301 if norm_path
in self
._include
_stack
:
2302 base_path
= self
._get
_last
_include
_file
()
2303 raise ConfigError('in "{}": cannot recursively include file "{}"'.format(base_path
, norm_path
))
2305 self
._include
_stack
.append(norm_path
)
2308 return self
._yaml
_ordered
_load
(norm_path
)
2310 if not self
._ignore
_include
_not
_found
:
2311 base_path
= self
._get
_last
_include
_file
()
2312 raise ConfigError('in "{}": cannot include file "{}": file not found in include directories'.format(base_path
, yaml_path
))
2316 def _get_include_paths(self
, include_node
):
2317 if _is_str_prop(include_node
):
2318 return [include_node
]
2319 elif _is_array_prop(include_node
):
2320 for include_path
in include_node
:
2321 if not _is_str_prop(include_path
):
2322 raise ConfigError('invalid include property: expecting array of strings')
2326 raise ConfigError('invalid include property: expecting string or array of strings')
2328 def _update_node(self
, base_node
, overlay_node
):
2329 for olay_key
, olay_value
in overlay_node
.items():
2330 if olay_key
in base_node
:
2331 base_value
= base_node
[olay_key
]
2333 if _is_assoc_array_prop(olay_value
) and _is_assoc_array_prop(base_value
):
2334 # merge dictionaries
2335 self
._update
_node
(base_value
, olay_value
)
2336 elif _is_array_prop(olay_value
) and _is_array_prop(base_value
):
2337 # append extension array items to base items
2338 base_value
+= olay_value
2340 # fall back to replacing
2341 base_node
[olay_key
] = olay_value
2343 base_node
[olay_key
] = olay_value
2345 def _process_node_include(self
, last_overlay_node
, name
,
2346 process_base_include_cb
,
2347 process_children_include_cb
=None):
2348 if not _is_assoc_array_prop(last_overlay_node
):
2349 raise ConfigError('{} objects must be associative arrays'.format(name
))
2351 # process children inclusions first
2352 if process_children_include_cb
:
2353 process_children_include_cb(last_overlay_node
)
2355 if '$include' in last_overlay_node
:
2356 include_node
= last_overlay_node
['$include']
2359 return last_overlay_node
2361 include_paths
= self
._get
_include
_paths
(include_node
)
2362 cur_base_path
= self
._get
_last
_include
_file
()
2365 # keep the include paths and remove the include property
2366 include_paths
= copy
.deepcopy(include_paths
)
2367 del last_overlay_node
['$include']
2369 for include_path
in include_paths
:
2370 # load raw YAML from included file
2371 overlay_node
= self
._load
_include
(include_path
)
2373 if overlay_node
is None:
2374 # cannot find include file, but we're ignoring those
2375 # errors, otherwise _load_include() itself raises
2379 # recursively process includes
2381 overlay_node
= process_base_include_cb(overlay_node
)
2382 except Exception as e
:
2383 raise ConfigError('in "{}"'.format(cur_base_path
), e
)
2385 # pop include stack now that we're done including
2386 del self
._include
_stack
[-1]
2388 # at this point, base_node is fully resolved (does not
2389 # contain any include property)
2390 if base_node
is None:
2391 base_node
= overlay_node
2393 self
._update
_node
(base_node
, overlay_node
)
2395 # finally, we update the latest base node with our last overlay
2397 if base_node
is None:
2398 # nothing was included, which is possible when we're
2399 # ignoring include errors
2400 return last_overlay_node
2402 self
._update
_node
(base_node
, last_overlay_node
)
2406 def _process_event_include(self
, event_node
):
2407 return self
._process
_node
_include
(event_node
, 'event',
2408 self
._process
_event
_include
)
2410 def _process_stream_include(self
, stream_node
):
2411 def process_children_include(stream_node
):
2412 if 'events' in stream_node
:
2413 events_node
= stream_node
['events']
2415 if not _is_assoc_array_prop(events_node
):
2416 raise ConfigError('"events" property must be an associative array')
2418 events_node_keys
= list(events_node
.keys())
2420 for key
in events_node_keys
:
2421 event_node
= events_node
[key
]
2424 events_node
[key
] = self
._process
_event
_include
(event_node
)
2425 except Exception as e
:
2426 raise ConfigError('cannot process includes of event object "{}"'.format(key
), e
)
2428 return self
._process
_node
_include
(stream_node
, 'stream',
2429 self
._process
_stream
_include
,
2430 process_children_include
)
2432 def _process_trace_include(self
, trace_node
):
2433 return self
._process
_node
_include
(trace_node
, 'trace',
2434 self
._process
_trace
_include
)
2436 def _process_clock_include(self
, clock_node
):
2437 return self
._process
_node
_include
(clock_node
, 'clock',
2438 self
._process
_clock
_include
)
2440 def _process_metadata_include(self
, metadata_node
):
2441 def process_children_include(metadata_node
):
2442 if 'trace' in metadata_node
:
2443 metadata_node
['trace'] = self
._process
_trace
_include
(metadata_node
['trace'])
2445 if 'clocks' in metadata_node
:
2446 clocks_node
= metadata_node
['clocks']
2448 if not _is_assoc_array_prop(clocks_node
):
2449 raise ConfigError('"clocks" property (metadata) must be an associative array')
2451 clocks_node_keys
= list(clocks_node
.keys())
2453 for key
in clocks_node_keys
:
2454 clock_node
= clocks_node
[key
]
2457 clocks_node
[key
] = self
._process
_clock
_include
(clock_node
)
2458 except Exception as e
:
2459 raise ConfigError('cannot process includes of clock object "{}"'.format(key
), e
)
2461 if 'streams' in metadata_node
:
2462 streams_node
= metadata_node
['streams']
2464 if not _is_assoc_array_prop(streams_node
):
2465 raise ConfigError('"streams" property (metadata) must be an associative array')
2467 streams_node_keys
= list(streams_node
.keys())
2469 for key
in streams_node_keys
:
2470 stream_node
= streams_node
[key
]
2473 streams_node
[key
] = self
._process
_stream
_include
(stream_node
)
2474 except Exception as e
:
2475 raise ConfigError('cannot process includes of stream object "{}"'.format(key
), e
)
2477 return self
._process
_node
_include
(metadata_node
, 'metadata',
2478 self
._process
_metadata
_include
,
2479 process_children_include
)
2481 def _process_root_includes(self
, root
):
2482 # The following config objects support includes:
2489 # We need to process the event includes first, then the stream
2490 # includes, then the trace includes, and finally the metadata
2493 # In each object, only one of the $include and $include-replace
2494 # special properties is allowed.
2496 # We keep a stack of absolute paths to included files to detect
2498 if 'metadata' in root
:
2499 root
['metadata'] = self
._process
_metadata
_include
(root
['metadata'])
2503 def _yaml_ordered_dump(self
, node
, **kwds
):
2504 class ODumper(yaml
.Dumper
):
2507 def dict_representer(dumper
, node
):
2508 return dumper
.represent_mapping(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2511 ODumper
.add_representer(collections
.OrderedDict
, dict_representer
)
2513 return yaml
.dump(node
, Dumper
=ODumper
, **kwds
)
2515 def _yaml_ordered_load(self
, yaml_path
):
2516 class OLoader(yaml
.Loader
):
2519 def construct_mapping(loader
, node
):
2520 loader
.flatten_mapping(node
)
2522 return collections
.OrderedDict(loader
.construct_pairs(node
))
2524 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2529 with
open(yaml_path
, 'r') as f
:
2530 node
= yaml
.load(f
, OLoader
)
2531 except (OSError, IOError) as e
:
2532 raise ConfigError('cannot open file "{}"'.format(yaml_path
))
2533 except Exception as e
:
2534 raise ConfigError('unknown error while trying to load file "{}"'.format(yaml_path
), e
)
2536 # loaded node must be an associate array
2537 if not _is_assoc_array_prop(node
):
2538 raise ConfigError('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 Exception as e
:
2553 raise ConfigError('cannot parse YAML file "{}"'.format(yaml_path
), e
)
2555 if not _is_assoc_array_prop(root
):
2556 raise ConfigError('configuration must be an associative array')
2558 unk_prop
= _get_first_unknown_prop(root
, [
2565 raise ConfigError('unknown configuration property: "{}"'.format(unk_prop
))
2567 # get the config version
2568 self
._version
= self
._get
_version
(root
)
2570 # process includes if supported
2571 if self
._version
>= 201:
2572 root
= self
._process
_root
_includes
(root
)
2574 # dump config if required
2575 if self
._dump
_config
:
2576 print(self
._yaml
_ordered
_dump
(root
, indent
=2,
2577 default_flow_style
=False))
2579 # get prefix and metadata
2580 prefix
= self
._get
_prefix
(root
)
2581 meta
= self
._create
_metadata
(root
)
2583 return Config(self
._version
, prefix
, meta
)
2586 def from_yaml_file(path
, include_dirs
, ignore_include_not_found
, dump_config
):
2588 parser
= _YamlConfigParser(include_dirs
, ignore_include_not_found
,
2590 cfg
= parser
.parse(path
)
2593 except Exception as e
:
2594 raise ConfigError('cannot create configuration from YAML file "{}"'.format(path
), e
)