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 bytes')
381 if element_type
.size
!= 8:
382 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
384 if element_type
.align
!= 8:
385 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 byte-aligned bytes')
387 def _validate_trace(self
, meta
):
388 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
390 def _validate_stream_packet_context(self
, stream
):
391 t
= stream
.packet_context_type
396 if type(t
) is not metadata
.Struct
:
399 # "timestamp_begin", if exists, is an unsigned integer type,
401 if 'timestamp_begin' in t
.fields
:
402 ts_begin
= t
.fields
['timestamp_begin']
404 if type(ts_begin
) is not metadata
.Integer
:
405 raise ConfigError('"timestamp_begin" field in stream packet context type must be an integer type')
408 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
410 if not ts_begin
.property_mappings
:
411 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
413 # "timestamp_end", if exists, is an unsigned integer type,
415 if 'timestamp_end' in t
.fields
:
416 ts_end
= t
.fields
['timestamp_end']
418 if type(ts_end
) is not metadata
.Integer
:
419 raise ConfigError('"timestamp_end" field in stream packet context type must be an integer type')
422 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
424 if not ts_end
.property_mappings
:
425 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
427 # "timestamp_begin" and "timestamp_end" exist together
428 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
429 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
431 # "events_discarded", if exists, is an unsigned integer type
432 if 'events_discarded' in t
.fields
:
433 events_discarded
= t
.fields
['events_discarded']
435 if type(events_discarded
) is not metadata
.Integer
:
436 raise ConfigError('"events_discarded" field in stream packet context type must be an integer type')
438 if events_discarded
.signed
:
439 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
441 # "packet_size" and "content_size" must exist
442 if 'packet_size' not in t
.fields
:
443 raise ConfigError('missing "packet_size" field in stream packet context type')
445 packet_size
= t
.fields
['packet_size']
447 # "content_size" and "content_size" must exist
448 if 'content_size' not in t
.fields
:
449 raise ConfigError('missing "content_size" field in stream packet context type')
451 content_size
= t
.fields
['content_size']
453 # "packet_size" is an unsigned integer type
454 if type(packet_size
) is not metadata
.Integer
:
455 raise ConfigError('"packet_size" field in stream packet context type must be an integer type')
457 if packet_size
.signed
:
458 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
460 # "content_size" is an unsigned integer type
461 if type(content_size
) is not metadata
.Integer
:
462 raise ConfigError('"content_size" field in stream packet context type must be an integer type')
464 if content_size
.signed
:
465 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
467 def _validate_stream_event_header(self
, stream
):
468 t
= stream
.event_header_type
471 if len(stream
.events
) > 1:
474 raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
476 if type(t
) is not metadata
.Struct
:
477 raise ConfigError('need "id" field in stream event header type, but stream event header type is not a structure type')
479 if 'id' not in t
.fields
:
480 raise ConfigError('need "id" field in stream event header type')
482 # validate "id" and "timestamp" types
483 if type(t
) is not metadata
.Struct
:
486 # "timestamp", if exists, is an unsigned integer type,
488 if 'timestamp' in t
.fields
:
489 ts
= t
.fields
['timestamp']
491 if type(ts
) is not metadata
.Integer
:
492 raise ConfigError('"ts" field in stream event header type must be an integer type')
495 raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
497 if not ts
.property_mappings
:
498 raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
500 # "id" is an unsigned integer type
504 if type(eid
) is not metadata
.Integer
:
505 raise ConfigError('"id" field in stream event header type must be an integer type')
508 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
510 def _validate_stream(self
, stream
):
511 self
._validate
_stream
_packet
_context
(stream
)
512 self
._validate
_stream
_event
_header
(stream
)
514 def validate(self
, meta
):
516 self
._validate
_trace
(meta
)
518 for stream
in meta
.streams
.values():
520 self
._validate
_stream
(stream
)
521 except Exception as e
:
522 raise ConfigError('invalid stream "{}"'.format(stream
.name
), e
)
525 class _MetadataDynamicTypesValidatorStackEntry
:
526 def __init__(self
, base_t
):
527 self
._base
_t
= base_t
535 def index(self
, value
):
543 def base_t(self
, value
):
547 # Entities. Order of values is important here.
549 class _Entity(enum
.IntEnum
):
550 TRACE_PACKET_HEADER
= 0
551 STREAM_PACKET_CONTEXT
= 1
552 STREAM_EVENT_HEADER
= 2
553 STREAM_EVENT_CONTEXT
= 3
558 # This validator validates dynamic metadata types, that is, it ensures
559 # variable-length array lengths and variant tags actually point to
560 # something that exists. It also checks that variable-length array
561 # lengths point to integer types and variant tags to enumeration types.
562 class _MetadataDynamicTypesValidator
:
564 self
._type
_to
_visit
_type
_func
= {
565 metadata
.Integer
: None,
566 metadata
.FloatingPoint
: None,
568 metadata
.String
: None,
569 metadata
.Struct
: self
._visit
_struct
_type
,
570 metadata
.Array
: self
._visit
_array
_type
,
571 metadata
.Variant
: self
._visit
_variant
_type
,
574 self
._cur
_trace
= None
575 self
._cur
_stream
= None
576 self
._cur
_event
= None
578 def _lookup_path_from_base(self
, path
, parts
, base
, start_index
,
579 base_is_current
, from_t
):
584 while index
< len(parts
):
588 if type(cur_t
) is metadata
.Struct
:
589 enumerated_items
= enumerate(cur_t
.fields
.items())
592 for i
, (field_name
, field_type
) in enumerated_items
:
593 if field_name
== part
:
595 found_path
.append((i
, field_type
))
598 raise ConfigError('invalid path "{}": cannot find field "{}" in structure type'.format(path
, part
))
599 elif type(cur_t
) is metadata
.Variant
:
600 enumerated_items
= enumerate(cur_t
.types
.items())
603 for i
, (type_name
, type_type
) in enumerated_items
:
604 if type_name
== part
:
606 found_path
.append((i
, type_type
))
609 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path
, part
))
611 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path
, part
))
616 # make sure that the pointed type is not the pointing type
618 raise ConfigError('invalid path "{}": pointing to self'.format(path
))
620 # if we're here, we found the type; however, it could be located
621 # _after_ the variant/VLA looking for it, if the pointing
622 # and pointed types are in the same entity, so compare the
623 # current stack entries indexes to our index path in that case
624 if not base_is_current
:
627 for index
, entry
in enumerate(self
._stack
):
628 if index
== len(found_path
):
629 # end of index path; valid so far
632 if found_path
[index
][0] > entry
.index
:
633 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path
))
635 # also make sure that both pointed and pointing types share
636 # a common structure ancestor
637 for index
, entry
in enumerate(self
._stack
):
638 if index
== len(found_path
):
641 if entry
.base_t
is not found_path
[index
][1]:
642 # found common ancestor
643 if type(entry
.base_t
) is metadata
.Variant
:
644 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
648 def _lookup_path_from_top(self
, path
, parts
):
650 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path
))
653 index
= len(self
._stack
) - 1
656 # check stack entries in reversed order
657 for entry
in reversed(self
._stack
):
658 # structure base type
659 if type(entry
.base_t
) is metadata
.Struct
:
661 enumerated_items
= enumerate(entry
.base_t
.fields
.items())
663 # lookup each field, until the current visiting index is met
664 for i
, (field_name
, field_type
) in enumerated_items
:
668 if field_name
== find_name
:
672 elif type(entry
.base_t
) is metadata
.Variant
:
673 enumerated_items
= enumerate(entry
.base_t
.types
.items())
675 # lookup each type, until the current visiting index is met
676 for i
, (type_name
, type_type
) in enumerated_items
:
680 if type_name
== find_name
:
682 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
686 # nothing returned here: cannot find type
687 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path
))
689 def _lookup_path(self
, path
, from_t
):
690 parts
= path
.lower().split('.')
692 base_is_current
= False
695 if parts
[0] == 'trace':
696 if parts
[1] == 'packet' and parts
[2] == 'header':
697 # make sure packet header exists
698 if self
._cur
_trace
.packet_header_type
is None:
699 raise ConfigError('invalid path "{}": no defined trace packet header type'.format(path
))
701 base
= self
._cur
_trace
.packet_header_type
703 if self
._cur
_entity
== _Entity
.TRACE_PACKET_HEADER
:
704 base_is_current
= True
706 raise ConfigError('invalid path "{}": unknown names after "trace"'.format(path
))
707 elif parts
[0] == 'stream':
708 if parts
[1] == 'packet' and parts
[2] == 'context':
709 if self
._cur
_entity
< _Entity
.STREAM_PACKET_CONTEXT
:
710 raise ConfigError('invalid path "{}": cannot access stream packet context here'.format(path
))
712 if self
._cur
_stream
.packet_context_type
is None:
713 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path
))
715 base
= self
._cur
_stream
.packet_context_type
717 if self
._cur
_entity
== _Entity
.STREAM_PACKET_CONTEXT
:
718 base_is_current
= True
719 elif parts
[1] == 'event':
720 if parts
[2] == 'header':
721 if self
._cur
_entity
< _Entity
.STREAM_EVENT_HEADER
:
722 raise ConfigError('invalid path "{}": cannot access stream event header here'.format(path
))
724 if self
._cur
_stream
.event_header_type
is None:
725 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path
))
727 base
= self
._cur
_stream
.event_header_type
729 if self
._cur
_entity
== _Entity
.STREAM_EVENT_HEADER
:
730 base_is_current
= True
731 elif parts
[2] == 'context':
732 if self
._cur
_entity
< _Entity
.STREAM_EVENT_CONTEXT
:
733 raise ConfigError('invalid path "{}": cannot access stream event context here'.format(path
))
735 if self
._cur
_stream
.event_context_type
is None:
736 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path
))
738 base
= self
._cur
_stream
.event_context_type
740 if self
._cur
_entity
== _Entity
.STREAM_EVENT_CONTEXT
:
741 base_is_current
= True
743 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path
))
745 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path
))
750 if len(parts
) >= 2 and base
is None:
751 if parts
[0] == 'event':
752 if parts
[1] == 'context':
753 if self
._cur
_entity
< _Entity
.EVENT_CONTEXT
:
754 raise ConfigError('invalid path "{}": cannot access event context here'.format(path
))
756 if self
._cur
_event
.context_type
is None:
757 raise ConfigError('invalid path "{}": no defined event context type'.format(path
))
759 base
= self
._cur
_event
.context_type
761 if self
._cur
_entity
== _Entity
.EVENT_CONTEXT
:
762 base_is_current
= True
763 elif parts
[1] == 'payload' or parts
[1] == 'fields':
764 if self
._cur
_entity
< _Entity
.EVENT_PAYLOAD
:
765 raise ConfigError('invalid path "{}": cannot access event payload here'.format(path
))
767 if self
._cur
_event
.payload_type
is None:
768 raise ConfigError('invalid path "{}": no defined event payload type'.format(path
))
770 base
= self
._cur
_event
.payload_type
772 if self
._cur
_entity
== _Entity
.EVENT_PAYLOAD
:
773 base_is_current
= True
775 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path
))
781 return self
._lookup
_path
_from
_base
(path
, parts
, base
, start_index
,
782 base_is_current
, from_t
)
784 return self
._lookup
_path
_from
_top
(path
, parts
)
786 def _stack_reset(self
):
789 def _stack_push(self
, base_t
):
790 entry
= _MetadataDynamicTypesValidatorStackEntry(base_t
)
791 self
._stack
.append(entry
)
793 def _stack_pop(self
):
796 def _stack_incr_index(self
):
797 self
._stack
[-1].index
+= 1
799 def _visit_struct_type(self
, t
):
802 for field_name
, field_type
in t
.fields
.items():
804 self
._visit
_type
(field_type
)
805 except Exception as e
:
806 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
808 self
._stack
_incr
_index
()
812 def _visit_array_type(self
, t
):
816 length_type
= self
._lookup
_path
(t
.length
, t
)
817 except Exception as e
:
818 raise ConfigError('invalid array type\'s length', e
)
820 # make sure length type an unsigned integer
821 if type(length_type
) is not metadata
.Integer
:
822 raise ConfigError('array type\'s length does not point to an integer type')
824 if length_type
.signed
:
825 raise ConfigError('array type\'s length does not point to an unsigned integer type')
827 self
._visit
_type
(t
.element_type
)
829 def _visit_variant_type(self
, t
):
832 tag_type
= self
._lookup
_path
(t
.tag
, t
)
833 except Exception as e
:
834 raise ConfigError('invalid variant type\'s tag', e
)
836 # make sure tag type is an enumeration
837 if type(tag_type
) is not metadata
.Enum
:
838 raise ConfigError('variant type\'s tag does not point to an enumeration type')
840 # verify that each variant type's type exists as an enumeration member
841 for tag_name
in t
.types
.keys():
842 if tag_name
not in tag_type
.members
:
843 raise ConfigError('cannot find variant type\'s type "{}" in pointed tag type'.format(tag_name
))
847 for type_name
, type_type
in t
.types
.items():
849 self
._visit
_type
(type_type
)
850 except Exception as e
:
851 raise ConfigError('in variant type\'s type "{}"'.format(type_name
), e
)
853 self
._stack
_incr
_index
()
857 def _visit_type(self
, t
):
861 if type(t
) in self
._type
_to
_visit
_type
_func
:
862 func
= self
._type
_to
_visit
_type
_func
[type(t
)]
867 def _visit_event(self
, ev
):
873 # visit event context type
875 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
878 self
._visit
_type
(ev
.context_type
)
879 except Exception as e
:
880 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
882 # visit event payload type
884 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
887 self
._visit
_type
(ev
.payload_type
)
888 except Exception as e
:
889 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
891 def _visit_stream(self
, stream
):
892 stream_name
= stream
.name
895 self
._cur
_stream
= stream
897 # reset current event
898 self
._cur
_event
= None
900 # visit stream packet context type
902 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
905 self
._visit
_type
(stream
.packet_context_type
)
906 except Exception as e
:
907 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
909 # visit stream event header type
911 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
914 self
._visit
_type
(stream
.event_header_type
)
915 except Exception as e
:
916 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
918 # visit stream event context type
920 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
923 self
._visit
_type
(stream
.event_context_type
)
924 except Exception as e
:
925 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
928 for ev
in stream
.events
.values():
930 self
._visit
_event
(ev
)
931 except Exception as e
:
932 raise ConfigError('invalid stream "{}"'.format(stream_name
))
934 def validate(self
, meta
):
936 self
._cur
_trace
= meta
.trace
938 # visit trace packet header type
940 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
943 self
._visit
_type
(meta
.trace
.packet_header_type
)
944 except Exception as e
:
945 raise ConfigError('invalid packet header type in trace', e
)
948 for stream
in meta
.streams
.values():
949 self
._visit
_stream
(stream
)
952 # Since type inheritance allows types to be only partially defined at
953 # any place in the configuration, this validator validates that actual
954 # trace, stream, and event types are all complete and valid.
955 class _MetadataTypesHistologyValidator
:
957 self
._type
_to
_validate
_type
_histology
_func
= {
958 metadata
.Integer
: self
._validate
_integer
_histology
,
959 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
960 metadata
.Enum
: self
._validate
_enum
_histology
,
961 metadata
.String
: self
._validate
_string
_histology
,
962 metadata
.Struct
: self
._validate
_struct
_histology
,
963 metadata
.Array
: self
._validate
_array
_histology
,
964 metadata
.Variant
: self
._validate
_variant
_histology
,
967 def _validate_integer_histology(self
, t
):
970 raise ConfigError('missing integer type\'s size')
972 def _validate_float_histology(self
, t
):
973 # exponent digits is set
974 if t
.exp_size
is None:
975 raise ConfigError('missing floating point number type\'s exponent size')
977 # mantissa digits is set
978 if t
.mant_size
is None:
979 raise ConfigError('missing floating point number type\'s mantissa size')
981 # exponent and mantissa sum is a multiple of 8
982 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
983 raise ConfigError('floating point number type\'s mantissa and exponent sizes sum must be a multiple of 8')
985 def _validate_enum_histology(self
, t
):
986 # integer type is set
987 if t
.value_type
is None:
988 raise ConfigError('missing enumeration type\'s integer type')
990 # there's at least one member
992 raise ConfigError('enumeration type needs at least one member')
994 # no overlapping values
997 for label
, value
in t
.members
.items():
999 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
1000 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label
))
1002 ranges
.append(value
)
1004 def _validate_string_histology(self
, t
):
1008 def _validate_struct_histology(self
, t
):
1009 # all fields are valid
1010 for field_name
, field_type
in t
.fields
.items():
1012 self
._validate
_type
_histology
(field_type
)
1013 except Exception as e
:
1014 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name
), e
)
1016 def _validate_array_histology(self
, t
):
1018 if t
.length
is None:
1019 raise ConfigError('missing array type\'s length')
1021 # element type is set
1022 if t
.element_type
is None:
1023 raise ConfigError('missing array type\'s element type')
1025 # element type is valid
1027 self
._validate
_type
_histology
(t
.element_type
)
1028 except Exception as e
:
1029 raise ConfigError('invalid array type\'s element type', e
)
1031 def _validate_variant_histology(self
, t
):
1034 raise ConfigError('missing variant type\'s tag')
1036 # there's at least one type
1038 raise ConfigError('variant type needs at least one type')
1040 # all types are valid
1041 for type_name
, type_t
in t
.types
.items():
1043 self
._validate
_type
_histology
(type_t
)
1044 except Exception as e
:
1045 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name
), e
)
1047 def _validate_type_histology(self
, t
):
1051 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
1053 def _validate_entity_type_histology(self
, t
):
1057 # entity cannot be an array
1058 if type(t
) is metadata
.Array
:
1059 raise ConfigError('cannot use an array here')
1061 self
._validate
_type
_histology
(t
)
1063 def _validate_event_types_histology(self
, ev
):
1066 # validate event context type
1068 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1069 except Exception as e
:
1070 raise ConfigError('invalid event context type for event "{}"'.format(ev_name
), e
)
1072 # validate event payload type
1073 if ev
.payload_type
is None:
1074 raise ConfigError('event payload type must exist in event "{}"'.format(ev_name
))
1076 # TODO: also check arrays, sequences, and variants
1077 if type(ev
.payload_type
) is metadata
.Struct
:
1078 if not ev
.payload_type
.fields
:
1079 raise ConfigError('event payload type must have at least one field for event "{}"'.format(ev_name
))
1082 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1083 except Exception as e
:
1084 raise ConfigError('invalid event payload type for event "{}"'.format(ev_name
), e
)
1086 def _validate_stream_types_histology(self
, stream
):
1087 stream_name
= stream
.name
1089 # validate stream packet context type
1091 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1092 except Exception as e
:
1093 raise ConfigError('invalid stream packet context type for stream "{}"'.format(stream_name
), e
)
1095 # validate stream event header type
1097 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1098 except Exception as e
:
1099 raise ConfigError('invalid stream event header type for stream "{}"'.format(stream_name
), e
)
1101 # validate stream event context type
1103 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1104 except Exception as e
:
1105 raise ConfigError('invalid stream event context type for stream "{}"'.format(stream_name
), e
)
1108 for ev
in stream
.events
.values():
1110 self
._validate
_event
_types
_histology
(ev
)
1111 except Exception as e
:
1112 raise ConfigError('invalid event in stream "{}"'.format(stream_name
), e
)
1114 def validate(self
, meta
):
1115 # validate trace packet header type
1117 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1118 except Exception as e
:
1119 raise ConfigError('invalid trace packet header type', e
)
1122 for stream
in meta
.streams
.values():
1123 self
._validate
_stream
_types
_histology
(stream
)
1126 class _YamlConfigParser
:
1127 def __init__(self
, include_dirs
, ignore_include_not_found
, dump_config
):
1128 self
._class
_name
_to
_create
_type
_func
= {
1129 'int': self
._create
_integer
,
1130 'integer': self
._create
_integer
,
1131 'flt': self
._create
_float
,
1132 'float': self
._create
_float
,
1133 'floating-point': self
._create
_float
,
1134 'enum': self
._create
_enum
,
1135 'enumeration': self
._create
_enum
,
1136 'str': self
._create
_string
,
1137 'string': self
._create
_string
,
1138 'struct': self
._create
_struct
,
1139 'structure': self
._create
_struct
,
1140 'array': self
._create
_array
,
1141 'var': self
._create
_variant
,
1142 'variant': self
._create
_variant
,
1144 self
._type
_to
_create
_type
_func
= {
1145 metadata
.Integer
: self
._create
_integer
,
1146 metadata
.FloatingPoint
: self
._create
_float
,
1147 metadata
.Enum
: self
._create
_enum
,
1148 metadata
.String
: self
._create
_string
,
1149 metadata
.Struct
: self
._create
_struct
,
1150 metadata
.Array
: self
._create
_array
,
1151 metadata
.Variant
: self
._create
_variant
,
1153 self
._include
_dirs
= include_dirs
1154 self
._include
_dirs
.append(os
.getcwd())
1155 self
._ignore
_include
_not
_found
= ignore_include_not_found
1156 self
._dump
_config
= dump_config
1158 def _set_byte_order(self
, metadata_node
):
1159 if 'trace' not in metadata_node
:
1160 raise ConfigError('missing "trace" property (metadata)')
1162 trace_node
= metadata_node
['trace']
1164 if not _is_assoc_array_prop(trace_node
):
1165 raise ConfigError('"trace" property (metadata) must be an associative array')
1167 if 'byte-order' not in trace_node
:
1168 raise ConfigError('missing "byte-order" property (trace)')
1170 self
._bo
= _byte_order_str_to_bo(trace_node
['byte-order'])
1172 if self
._bo
is None:
1173 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1175 def _lookup_type_alias(self
, name
):
1176 if name
in self
._tas
:
1177 return copy
.deepcopy(self
._tas
[name
])
1179 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1180 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1183 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop
))
1185 if 'name' not in prop_mapping_node
:
1186 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1188 if 'property' not in prop_mapping_node
:
1189 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1191 clock_name
= prop_mapping_node
['name']
1192 prop
= prop_mapping_node
['property']
1194 if not _is_str_prop(clock_name
):
1195 raise ConfigError('"name" property of integer type object\'s clock property mapping must be a string')
1197 if not _is_str_prop(prop
):
1198 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1200 if clock_name
not in self
._clocks
:
1201 raise ConfigError('invalid clock name "{}" in integer type object\'s clock property mapping'.format(clock_name
))
1204 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop
))
1206 mapped_clock
= self
._clocks
[clock_name
]
1207 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
1209 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
1210 kp
= known_props
+ ['inherit', 'class']
1212 if self
._version
>= 201:
1213 kp
.append('$inherit')
1215 return _get_first_unknown_prop(type_node
, kp
)
1217 def _create_integer(self
, obj
, node
):
1219 # create integer object
1220 obj
= metadata
.Integer()
1222 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1229 'property-mappings',
1233 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop
))
1239 if not _is_int_prop(size
):
1240 raise ConfigError('"size" property of integer type object must be an integer')
1243 raise ConfigError('invalid integer size: {}'.format(size
))
1249 align
= node
['align']
1251 if not _is_int_prop(align
):
1252 raise ConfigError('"align" property of integer type object must be an integer')
1254 if not _is_valid_alignment(align
):
1255 raise ConfigError('invalid alignment: {}'.format(align
))
1260 if 'signed' in node
:
1261 signed
= node
['signed']
1263 if not _is_bool_prop(signed
):
1264 raise ConfigError('"signed" property of integer type object must be a boolean')
1269 if 'byte-order' in node
:
1270 byte_order
= node
['byte-order']
1272 if not _is_str_prop(byte_order
):
1273 raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
1275 byte_order
= _byte_order_str_to_bo(byte_order
)
1277 if byte_order
is None:
1278 raise ConfigError('invalid "byte-order" property in integer type object')
1280 byte_order
= self
._bo
1282 obj
.byte_order
= byte_order
1288 if not _is_str_prop(base
):
1289 raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1303 if 'encoding' in node
:
1304 encoding
= node
['encoding']
1306 if not _is_str_prop(encoding
):
1307 raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1309 encoding
= _encoding_str_to_encoding(encoding
)
1311 if encoding
is None:
1312 raise ConfigError('invalid "encoding" property in integer type object')
1314 obj
.encoding
= encoding
1317 if 'property-mappings' in node
:
1318 prop_mappings
= node
['property-mappings']
1320 if not _is_array_prop(prop_mappings
):
1321 raise ConfigError('"property-mappings" property of integer type object must be an array')
1323 if len(prop_mappings
) > 1:
1324 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1326 del obj
.property_mappings
[:]
1328 for index
, prop_mapping
in enumerate(prop_mappings
):
1329 if not _is_assoc_array_prop(prop_mapping
):
1330 raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
1332 if 'type' not in prop_mapping
:
1333 raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1335 prop_type
= prop_mapping
['type']
1337 if not _is_str_prop(prop_type
):
1338 raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1340 if prop_type
== 'clock':
1341 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1343 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1347 def _create_float(self
, obj
, node
):
1349 # create floating point number object
1350 obj
= metadata
.FloatingPoint()
1352 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1359 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop
))
1365 if not _is_assoc_array_prop(size
):
1366 raise ConfigError('"size" property of floating point number type object must be an associative array')
1368 unk_prop
= _get_first_unknown_prop(node
, ['exp', 'mant'])
1373 if not _is_int_prop(exp
):
1374 raise ConfigError('"exp" property of floating point number type object\'s "size" property must be an integer')
1377 raise ConfigError('invalid floating point number exponent size: {}')
1384 if not _is_int_prop(mant
):
1385 raise ConfigError('"mant" property of floating point number type object\'s "size" property must be an integer')
1388 raise ConfigError('invalid floating point number mantissa size: {}')
1390 obj
.mant_size
= mant
1394 align
= node
['align']
1396 if not _is_int_prop(align
):
1397 raise ConfigError('"align" property of floating point number type object must be an integer')
1399 if not _is_valid_alignment(align
):
1400 raise ConfigError('invalid alignment: {}'.format(align
))
1405 if 'byte-order' in node
:
1406 byte_order
= node
['byte-order']
1408 if not _is_str_prop(byte_order
):
1409 raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
1411 byte_order
= _byte_order_str_to_bo(byte_order
)
1413 if byte_order
is None:
1414 raise ConfigError('invalid "byte-order" property in floating point number type object')
1416 byte_order
= self
._bo
1418 obj
.byte_order
= byte_order
1422 def _create_enum(self
, obj
, node
):
1424 # create enumeration object
1425 obj
= metadata
.Enum()
1427 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1433 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop
))
1436 if 'value-type' in node
:
1438 obj
.value_type
= self
._create
_type
(node
['value-type'])
1439 except Exception as e
:
1440 raise ConfigError('cannot create enumeration type\'s integer type', e
)
1443 if 'members' in node
:
1444 members_node
= node
['members']
1446 if not _is_array_prop(members_node
):
1447 raise ConfigError('"members" property of enumeration type object must be an array')
1451 for index
, m_node
in enumerate(members_node
):
1452 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1453 raise ConfigError('invalid enumeration member #{}: expecting a string or an associative array'.format(index
))
1455 if _is_str_prop(m_node
):
1460 if 'label' not in m_node
:
1461 raise ConfigError('missing "label" property in enumeration member #{}'.format(index
))
1463 label
= m_node
['label']
1465 if not _is_str_prop(label
):
1466 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index
))
1468 if 'value' not in m_node
:
1469 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label
))
1471 value
= m_node
['value']
1473 if not _is_int_prop(value
) and not _is_array_prop(value
):
1474 raise ConfigError('invalid enumeration member ("{}"): expecting an integer or an array'.format(label
))
1476 if _is_int_prop(value
):
1478 value
= (value
, value
)
1481 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label
))
1487 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1492 obj
.members
[label
] = value
1496 def _create_string(self
, obj
, node
):
1498 # create string object
1499 obj
= metadata
.String()
1501 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1506 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1509 if 'encoding' in node
:
1510 encoding
= node
['encoding']
1512 if not _is_str_prop(encoding
):
1513 raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
1515 encoding
= _encoding_str_to_encoding(encoding
)
1517 if encoding
is None:
1518 raise ConfigError('invalid "encoding" property in string type object')
1520 obj
.encoding
= encoding
1524 def _create_struct(self
, obj
, node
):
1526 # create structure object
1527 obj
= metadata
.Struct()
1529 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1535 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1538 if 'min-align' in node
:
1539 min_align
= node
['min-align']
1541 if not _is_int_prop(min_align
):
1542 raise ConfigError('"min-align" property of structure type object must be an integer')
1544 if not _is_valid_alignment(min_align
):
1545 raise ConfigError('invalid minimum alignment: {}'.format(min_align
))
1547 obj
.min_align
= min_align
1550 if 'fields' in node
:
1551 fields
= node
['fields']
1553 if not _is_assoc_array_prop(fields
):
1554 raise ConfigError('"fields" property of structure type object must be an associative array')
1556 for field_name
, field_node
in fields
.items():
1557 if not is_valid_identifier(field_name
):
1558 raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name
))
1561 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1562 except Exception as e
:
1563 raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name
), e
)
1567 def _create_array(self
, obj
, node
):
1569 # create array object
1570 obj
= metadata
.Array()
1572 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1578 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop
))
1581 if 'length' in node
:
1582 length
= node
['length']
1584 if not _is_int_prop(length
) and not _is_str_prop(length
):
1585 raise ConfigError('"length" property of array type object must be an integer or a string')
1587 if type(length
) is int and length
< 0:
1588 raise ConfigError('invalid static array length: {}'.format(length
))
1593 if 'element-type' in node
:
1595 obj
.element_type
= self
._create
_type
(node
['element-type'])
1596 except Exception as e
:
1597 raise ConfigError('cannot create array type\'s element type', e
)
1601 def _create_variant(self
, obj
, node
):
1603 # create variant object
1604 obj
= metadata
.Variant()
1606 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1612 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop
))
1618 if not _is_str_prop(tag
):
1619 raise ConfigError('"tag" property of variant type object must be a string')
1621 # do not validate variant tag for the moment; will be done in a
1627 types
= node
['types']
1629 if not _is_assoc_array_prop(types
):
1630 raise ConfigError('"types" property of variant type object must be an associative array')
1632 # do not validate type names for the moment; will be done in a
1634 for type_name
, type_node
in types
.items():
1635 if not is_valid_identifier(type_name
):
1636 raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name
))
1639 obj
.types
[type_name
] = self
._create
_type
(type_node
)
1640 except Exception as e
:
1641 raise ConfigError('cannot create variant type\'s type "{}"'.format(type_name
), e
)
1645 def _create_type(self
, type_node
):
1646 if type(type_node
) is str:
1647 t
= self
._lookup
_type
_alias
(type_node
)
1650 raise ConfigError('unknown type alias "{}"'.format(type_node
))
1654 if not _is_assoc_array_prop(type_node
):
1655 raise ConfigError('type objects must be associative arrays')
1662 if self
._version
>= 200:
1663 if 'inherit' in type_node
:
1664 inherit_prop
= 'inherit'
1665 inherit_node
= type_node
[inherit_prop
]
1667 if self
._version
>= 201:
1668 if '$inherit' in type_node
:
1669 if inherit_node
is not None:
1670 raise ConfigError('cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1672 inherit_prop
= '$inherit'
1673 inherit_node
= type_node
[inherit_prop
]
1675 if inherit_node
is not None and 'class' in type_node
:
1676 raise ConfigError('cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1678 if inherit_node
is not None:
1679 if not _is_str_prop(inherit_node
):
1680 raise ConfigError('"{}" property of type object must be a string'.format(inherit_prop
))
1682 base
= self
._lookup
_type
_alias
(inherit_node
)
1685 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1687 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1689 if 'class' not in type_node
:
1690 raise ConfigError('type objects which do not inherit must have a "class" property')
1692 class_name
= type_node
['class']
1694 if type(class_name
) is not str:
1695 raise ConfigError('type objects\' "class" property must be a string')
1697 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1698 raise ConfigError('unknown type class "{}"'.format(class_name
))
1701 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1703 return func(base
, type_node
)
1705 def _register_type_aliases(self
, metadata_node
):
1708 if 'type-aliases' not in metadata_node
:
1711 ta_node
= metadata_node
['type-aliases']
1713 if not _is_assoc_array_prop(ta_node
):
1714 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1716 for ta_name
, ta_type
in ta_node
.items():
1717 if ta_name
in self
._tas
:
1718 raise ConfigError('duplicate type alias "{}"'.format(ta_name
))
1721 t
= self
._create
_type
(ta_type
)
1722 except Exception as e
:
1723 raise ConfigError('cannot create type alias "{}"'.format(ta_name
), e
)
1725 self
._tas
[ta_name
] = t
1727 def _create_clock(self
, node
):
1728 # create clock object
1729 clock
= metadata
.Clock()
1731 if not _is_assoc_array_prop(node
):
1732 raise ConfigError('clock objects must be associative arrays')
1744 if self
._version
>= 201:
1745 known_props
.append('$return-ctype')
1747 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1750 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop
))
1754 uuidp
= node
['uuid']
1756 if not _is_str_prop(uuidp
):
1757 raise ConfigError('"uuid" property of clock object must be a string')
1760 uuidp
= uuid
.UUID(uuidp
)
1762 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp
))
1767 if 'description' in node
:
1768 desc
= node
['description']
1770 if not _is_str_prop(desc
):
1771 raise ConfigError('"description" property of clock object must be a string')
1773 clock
.description
= desc
1779 if not _is_int_prop(freq
):
1780 raise ConfigError('"freq" property of clock object must be an integer')
1783 raise ConfigError('invalid clock frequency: {}'.format(freq
))
1788 if 'error-cycles' in node
:
1789 error_cycles
= node
['error-cycles']
1791 if not _is_int_prop(error_cycles
):
1792 raise ConfigError('"error-cycles" property of clock object must be an integer')
1794 if error_cycles
< 0:
1795 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles
))
1797 clock
.error_cycles
= error_cycles
1800 if 'offset' in node
:
1801 offset
= node
['offset']
1803 if not _is_assoc_array_prop(offset
):
1804 raise ConfigError('"offset" property of clock object must be an associative array')
1806 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1809 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop
))
1812 if 'cycles' in offset
:
1813 offset_cycles
= offset
['cycles']
1815 if not _is_int_prop(offset_cycles
):
1816 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1818 if offset_cycles
< 0:
1819 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles
))
1821 clock
.offset_cycles
= offset_cycles
1824 if 'seconds' in offset
:
1825 offset_seconds
= offset
['seconds']
1827 if not _is_int_prop(offset_seconds
):
1828 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1830 if offset_seconds
< 0:
1831 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds
))
1833 clock
.offset_seconds
= offset_seconds
1836 if 'absolute' in node
:
1837 absolute
= node
['absolute']
1839 if not _is_bool_prop(absolute
):
1840 raise ConfigError('"absolute" property of clock object must be a boolean')
1842 clock
.absolute
= absolute
1845 # v2.0: "return-ctype"
1846 # v2.1+: "$return-ctype"
1847 return_ctype_node
= None
1849 if self
._version
>= 200:
1850 if 'return-ctype' in node
:
1851 return_ctype_prop
= 'return-ctype'
1852 return_ctype_node
= node
[return_ctype_prop
]
1854 if self
._version
>= 201:
1855 if '$return-ctype' in node
:
1856 if return_ctype_node
is not None:
1857 raise ConfigError('cannot specify both "return-ctype" and "$return-ctype" properties of clock object: prefer "$return-ctype"')
1859 return_ctype_prop
= '$return-ctype'
1860 return_ctype_node
= node
[return_ctype_prop
]
1862 if return_ctype_node
is not None:
1863 if not _is_str_prop(return_ctype_node
):
1864 raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop
))
1866 clock
.return_ctype
= return_ctype_node
1870 def _register_clocks(self
, metadata_node
):
1871 self
._clocks
= collections
.OrderedDict()
1873 if 'clocks' not in metadata_node
:
1876 clocks_node
= metadata_node
['clocks']
1878 if not _is_assoc_array_prop(clocks_node
):
1879 raise ConfigError('"clocks" property (metadata) must be an associative array')
1881 for clock_name
, clock_node
in clocks_node
.items():
1882 if not is_valid_identifier(clock_name
):
1883 raise ConfigError('invalid clock name: "{}"'.format(clock_name
))
1885 if clock_name
in self
._clocks
:
1886 raise ConfigError('duplicate clock "{}"'.format(clock_name
))
1889 clock
= self
._create
_clock
(clock_node
)
1890 except Exception as e
:
1891 raise ConfigError('cannot create clock "{}"'.format(clock_name
), e
)
1893 clock
.name
= clock_name
1894 self
._clocks
[clock_name
] = clock
1896 def _create_env(self
, metadata_node
):
1897 env
= collections
.OrderedDict()
1899 if 'env' not in metadata_node
:
1902 env_node
= metadata_node
['env']
1904 if not _is_assoc_array_prop(env_node
):
1905 raise ConfigError('"env" property (metadata) must be an associative array')
1907 for env_name
, env_value
in env_node
.items():
1909 raise ConfigError('duplicate environment variable "{}"'.format(env_name
))
1911 if not is_valid_identifier(env_name
):
1912 raise ConfigError('invalid environment variable name: "{}"'.format(env_name
))
1914 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
1915 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
1917 env
[env_name
] = env_value
1921 def _register_log_levels(self
, metadata_node
):
1922 self
._log
_levels
= dict()
1925 # v2.0: "log-levels"
1926 # v2.1+: "$log-levels"
1927 log_levels_node
= None
1929 if self
._version
>= 200:
1930 if 'log-levels' in metadata_node
:
1931 log_levels_prop
= 'log-levels'
1932 log_levels_node
= metadata_node
[log_levels_prop
]
1934 if self
._version
>= 201:
1935 if '$log-levels' in metadata_node
:
1936 if log_levels_node
is not None:
1937 raise ConfigError('cannot specify both "log-levels" and "$log-levels" properties of metadata object: prefer "$log-levels"')
1939 log_levels_prop
= '$log-levels'
1940 log_levels_node
= metadata_node
[log_levels_prop
]
1942 if log_levels_node
is None:
1945 if not _is_assoc_array_prop(log_levels_node
):
1946 raise ConfigError('"{}" property (metadata) must be an associative array'.format(log_levels_prop
))
1948 for ll_name
, ll_value
in log_levels_node
.items():
1949 if ll_name
in self
._log
_levels
:
1950 raise ConfigError('duplicate log level entry "{}"'.format(ll_name
))
1952 if not _is_int_prop(ll_value
):
1953 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name
))
1956 raise ConfigError('invalid log level entry ("{}"): log level value must be positive'.format(ll_name
))
1958 self
._log
_levels
[ll_name
] = ll_value
1960 def _create_trace(self
, metadata_node
):
1961 # create trace object
1962 trace
= metadata
.Trace()
1964 if 'trace' not in metadata_node
:
1965 raise ConfigError('missing "trace" property (metadata)')
1967 trace_node
= metadata_node
['trace']
1969 if not _is_assoc_array_prop(trace_node
):
1970 raise ConfigError('"trace" property (metadata) must be an associative array')
1972 unk_prop
= _get_first_unknown_prop(trace_node
, [
1975 'packet-header-type',
1979 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop
))
1981 # set byte order (already parsed)
1982 trace
.byte_order
= self
._bo
1985 if 'uuid' in trace_node
:
1986 uuidp
= trace_node
['uuid']
1988 if not _is_str_prop(uuidp
):
1989 raise ConfigError('"uuid" property of trace object must be a string')
1992 uuidp
= uuid
.uuid1()
1995 uuidp
= uuid
.UUID(uuidp
)
1997 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp
))
2001 # packet header type
2002 if 'packet-header-type' in trace_node
:
2004 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
2005 except Exception as e
:
2006 raise ConfigError('cannot create packet header type (trace)', e
)
2008 trace
.packet_header_type
= ph_type
2012 def _lookup_log_level(self
, ll
):
2013 if _is_int_prop(ll
):
2015 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
2016 return self
._log
_levels
[ll
]
2018 def _create_event(self
, event_node
):
2019 event
= metadata
.Event()
2021 if not _is_assoc_array_prop(event_node
):
2022 raise ConfigError('event objects must be associative arrays')
2024 unk_prop
= _get_first_unknown_prop(event_node
, [
2031 raise ConfigError('unknown event object property: "{}"'.format(unk_prop
))
2033 if 'log-level' in event_node
:
2034 ll_node
= event_node
['log-level']
2036 if _is_str_prop(ll_node
):
2037 ll
= self
._lookup
_log
_level
(event_node
['log-level'])
2040 raise ConfigError('cannot find log level "{}"'.format(ll_node
))
2041 elif _is_int_prop(ll_node
):
2043 raise ConfigError('invalid log level value {}: value must be positive'.format(ll_node
))
2047 raise ConfigError('"log-level" property must be either a string or an integer')
2049 event
.log_level
= ll
2051 if 'context-type' in event_node
:
2053 t
= self
._create
_type
(event_node
['context-type'])
2054 except Exception as e
:
2055 raise ConfigError('cannot create event\'s context type object', e
)
2057 event
.context_type
= t
2059 if 'payload-type' not in event_node
:
2060 raise ConfigError('missing "payload-type" property in event object')
2063 t
= self
._create
_type
(event_node
['payload-type'])
2064 except Exception as e
:
2065 raise ConfigError('cannot create event\'s payload type object', e
)
2067 event
.payload_type
= t
2071 def _create_stream(self
, stream_node
):
2072 stream
= metadata
.Stream()
2074 if not _is_assoc_array_prop(stream_node
):
2075 raise ConfigError('stream objects must be associative arrays')
2077 unk_prop
= _get_first_unknown_prop(stream_node
, [
2078 'packet-context-type',
2079 'event-header-type',
2080 'event-context-type',
2085 raise ConfigError('unknown stream object property: "{}"'.format(unk_prop
))
2087 if 'packet-context-type' in stream_node
:
2089 t
= self
._create
_type
(stream_node
['packet-context-type'])
2090 except Exception as e
:
2091 raise ConfigError('cannot create stream\'s packet context type object', e
)
2093 stream
.packet_context_type
= t
2095 if 'event-header-type' in stream_node
:
2097 t
= self
._create
_type
(stream_node
['event-header-type'])
2098 except Exception as e
:
2099 raise ConfigError('cannot create stream\'s event header type object', e
)
2101 stream
.event_header_type
= t
2103 if 'event-context-type' in stream_node
:
2105 t
= self
._create
_type
(stream_node
['event-context-type'])
2106 except Exception as e
:
2107 raise ConfigError('cannot create stream\'s event context type object', e
)
2109 stream
.event_context_type
= t
2111 if 'events' not in stream_node
:
2112 raise ConfigError('missing "events" property in stream object')
2114 events
= stream_node
['events']
2116 if not _is_assoc_array_prop(events
):
2117 raise ConfigError('"events" property of stream object must be an associative array')
2120 raise ConfigError('at least one event is needed within a stream object')
2124 for ev_name
, ev_node
in events
.items():
2126 ev
= self
._create
_event
(ev_node
)
2127 except Exception as e
:
2128 raise ConfigError('cannot create event "{}"'.format(ev_name
), e
)
2132 stream
.events
[ev_name
] = ev
2137 def _create_streams(self
, metadata_node
):
2138 streams
= collections
.OrderedDict()
2140 if 'streams' not in metadata_node
:
2141 raise ConfigError('missing "streams" property (metadata)')
2143 streams_node
= metadata_node
['streams']
2145 if not _is_assoc_array_prop(streams_node
):
2146 raise ConfigError('"streams" property (metadata) must be an associative array')
2148 if not streams_node
:
2149 raise ConfigError('at least one stream is needed (metadata)')
2153 for stream_name
, stream_node
in streams_node
.items():
2155 stream
= self
._create
_stream
(stream_node
)
2156 except Exception as e
:
2157 raise ConfigError('cannot create stream "{}"'.format(stream_name
), e
)
2160 stream
.name
= str(stream_name
)
2161 streams
[stream_name
] = stream
2166 def _create_metadata(self
, root
):
2167 meta
= metadata
.Metadata()
2169 if 'metadata' not in root
:
2170 raise ConfigError('missing "metadata" property (configuration)')
2172 metadata_node
= root
['metadata']
2174 if not _is_assoc_array_prop(metadata_node
):
2175 raise ConfigError('"metadata" property (configuration) must be an associative array')
2186 if self
._version
>= 201:
2187 known_props
.append('$log-levels')
2189 unk_prop
= _get_first_unknown_prop(metadata_node
, known_props
)
2194 if unk_prop
== '$include':
2195 add
= ' (use version 2.1 or greater)'
2197 raise ConfigError('unknown metadata property{}: "{}"'.format(add
, unk_prop
))
2199 self
._set
_byte
_order
(metadata_node
)
2200 self
._register
_clocks
(metadata_node
)
2201 meta
.clocks
= self
._clocks
2202 self
._register
_type
_aliases
(metadata_node
)
2203 meta
.env
= self
._create
_env
(metadata_node
)
2204 meta
.trace
= self
._create
_trace
(metadata_node
)
2205 self
._register
_log
_levels
(metadata_node
)
2206 meta
.streams
= self
._create
_streams
(metadata_node
)
2210 def _get_version(self
, root
):
2211 if 'version' not in root
:
2212 raise ConfigError('missing "version" property (configuration)')
2214 version_node
= root
['version']
2216 if not _is_str_prop(version_node
):
2217 raise ConfigError('"version" property (configuration) must be a string')
2219 version_node
= version_node
.strip()
2221 if version_node
not in ['2.0', '2.1']:
2222 raise ConfigError('unsupported version ({}): versions 2.0 and 2.1 are supported'.format(version_node
))
2224 # convert version string to comparable version integer
2225 parts
= version_node
.split('.')
2226 version
= int(parts
[0]) * 100 + int(parts
[1])
2230 def _get_prefix(self
, root
):
2231 if 'prefix' not in root
:
2234 prefix_node
= root
['prefix']
2236 if not _is_str_prop(prefix_node
):
2237 raise ConfigError('"prefix" property (configuration) must be a string')
2239 if not is_valid_identifier(prefix_node
):
2240 raise ConfigError('"prefix" property (configuration) must be a valid C identifier')
2244 def _get_last_include_file(self
):
2245 if self
._include
_stack
:
2246 return self
._include
_stack
[-1]
2248 return self
._root
_yaml
_path
2250 def _load_include(self
, yaml_path
):
2251 for inc_dir
in self
._include
_dirs
:
2252 # current include dir + file name path
2253 # note: os.path.join() only takes the last arg if it's absolute
2254 inc_path
= os
.path
.join(inc_dir
, yaml_path
)
2256 # real path (symbolic links resolved)
2257 real_path
= os
.path
.realpath(inc_path
)
2259 # normalized path (weird stuff removed!)
2260 norm_path
= os
.path
.normpath(real_path
)
2262 if not os
.path
.isfile(norm_path
):
2263 # file does not exist: skip
2266 if norm_path
in self
._include
_stack
:
2267 base_path
= self
._get
_last
_include
_file
()
2268 raise ConfigError('in "{}": cannot recursively include file "{}"'.format(base_path
, norm_path
))
2270 self
._include
_stack
.append(norm_path
)
2273 return self
._yaml
_ordered
_load
(norm_path
)
2275 if not self
._ignore
_include
_not
_found
:
2276 base_path
= self
._get
_last
_include
_file
()
2277 raise ConfigError('in "{}": cannot include file "{}": file not found in include directories'.format(base_path
, yaml_path
))
2281 def _get_include_paths(self
, include_node
):
2282 if _is_str_prop(include_node
):
2283 return [include_node
]
2284 elif _is_array_prop(include_node
):
2285 for include_path
in include_node
:
2286 if not _is_str_prop(include_path
):
2287 raise ConfigError('invalid include property: expecting array of strings')
2291 raise ConfigError('invalid include property: expecting string or array of strings')
2293 def _update_node(self
, base_node
, overlay_node
):
2294 for olay_key
, olay_value
in overlay_node
.items():
2295 if olay_key
in base_node
:
2296 base_value
= base_node
[olay_key
]
2298 if _is_assoc_array_prop(olay_value
) and _is_assoc_array_prop(base_value
):
2299 # merge dictionaries
2300 self
._update
_node
(base_value
, olay_value
)
2301 elif _is_array_prop(olay_value
) and _is_array_prop(base_value
):
2302 # append extension array items to base items
2303 base_value
+= olay_value
2305 # fall back to replacing
2306 base_node
[olay_key
] = olay_value
2308 base_node
[olay_key
] = olay_value
2310 def _process_node_include(self
, last_overlay_node
, name
,
2311 process_base_include_cb
,
2312 process_children_include_cb
=None):
2313 if not _is_assoc_array_prop(last_overlay_node
):
2314 raise ConfigError('{} objects must be associative arrays'.format(name
))
2316 # process children inclusions first
2317 if process_children_include_cb
:
2318 process_children_include_cb(last_overlay_node
)
2320 if '$include' in last_overlay_node
:
2321 include_node
= last_overlay_node
['$include']
2324 return last_overlay_node
2326 include_paths
= self
._get
_include
_paths
(include_node
)
2327 cur_base_path
= self
._get
_last
_include
_file
()
2330 # keep the include paths and remove the include property
2331 include_paths
= copy
.deepcopy(include_paths
)
2332 del last_overlay_node
['$include']
2334 for include_path
in include_paths
:
2335 # load raw YAML from included file
2336 overlay_node
= self
._load
_include
(include_path
)
2338 if overlay_node
is None:
2339 # cannot find include file, but we're ignoring those
2340 # errors, otherwise _load_include() itself raises
2344 # recursively process includes
2346 overlay_node
= process_base_include_cb(overlay_node
)
2347 except Exception as e
:
2348 raise ConfigError('in "{}"'.format(cur_base_path
), e
)
2350 # pop include stack now that we're done including
2351 del self
._include
_stack
[-1]
2353 # at this point, base_node is fully resolved (does not
2354 # contain any include property)
2355 if base_node
is None:
2356 base_node
= overlay_node
2358 self
._update
_node
(base_node
, overlay_node
)
2360 # finally, we update the latest base node with our last overlay
2362 if base_node
is None:
2363 # nothing was included, which is possible when we're
2364 # ignoring include errors
2365 return last_overlay_node
2367 self
._update
_node
(base_node
, last_overlay_node
)
2371 def _process_event_include(self
, event_node
):
2372 return self
._process
_node
_include
(event_node
, 'event',
2373 self
._process
_event
_include
)
2375 def _process_stream_include(self
, stream_node
):
2376 def process_children_include(stream_node
):
2377 if 'events' in stream_node
:
2378 events_node
= stream_node
['events']
2380 if not _is_assoc_array_prop(events_node
):
2381 raise ConfigError('"events" property must be an associative array')
2383 events_node_keys
= list(events_node
.keys())
2385 for key
in events_node_keys
:
2386 event_node
= events_node
[key
]
2389 events_node
[key
] = self
._process
_event
_include
(event_node
)
2390 except Exception as e
:
2391 raise ConfigError('cannot process includes of event object "{}"'.format(key
), e
)
2393 return self
._process
_node
_include
(stream_node
, 'stream',
2394 self
._process
_stream
_include
,
2395 process_children_include
)
2397 def _process_trace_include(self
, trace_node
):
2398 return self
._process
_node
_include
(trace_node
, 'trace',
2399 self
._process
_trace
_include
)
2401 def _process_clock_include(self
, clock_node
):
2402 return self
._process
_node
_include
(clock_node
, 'clock',
2403 self
._process
_clock
_include
)
2405 def _process_metadata_include(self
, metadata_node
):
2406 def process_children_include(metadata_node
):
2407 if 'trace' in metadata_node
:
2408 metadata_node
['trace'] = self
._process
_trace
_include
(metadata_node
['trace'])
2410 if 'clocks' in metadata_node
:
2411 clocks_node
= metadata_node
['clocks']
2413 if not _is_assoc_array_prop(clocks_node
):
2414 raise ConfigError('"clocks" property (metadata) must be an associative array')
2416 clocks_node_keys
= list(clocks_node
.keys())
2418 for key
in clocks_node_keys
:
2419 clock_node
= clocks_node
[key
]
2422 clocks_node
[key
] = self
._process
_clock
_include
(clock_node
)
2423 except Exception as e
:
2424 raise ConfigError('cannot process includes of clock object "{}"'.format(key
), e
)
2426 if 'streams' in metadata_node
:
2427 streams_node
= metadata_node
['streams']
2429 if not _is_assoc_array_prop(streams_node
):
2430 raise ConfigError('"streams" property (metadata) must be an associative array')
2432 streams_node_keys
= list(streams_node
.keys())
2434 for key
in streams_node_keys
:
2435 stream_node
= streams_node
[key
]
2438 streams_node
[key
] = self
._process
_stream
_include
(stream_node
)
2439 except Exception as e
:
2440 raise ConfigError('cannot process includes of stream object "{}"'.format(key
), e
)
2442 return self
._process
_node
_include
(metadata_node
, 'metadata',
2443 self
._process
_metadata
_include
,
2444 process_children_include
)
2446 def _process_root_includes(self
, root
):
2447 # The following config objects support includes:
2454 # We need to process the event includes first, then the stream
2455 # includes, then the trace includes, and finally the metadata
2458 # In each object, only one of the $include and $include-replace
2459 # special properties is allowed.
2461 # We keep a stack of absolute paths to included files to detect
2463 if 'metadata' in root
:
2464 root
['metadata'] = self
._process
_metadata
_include
(root
['metadata'])
2468 def _yaml_ordered_dump(self
, node
, **kwds
):
2469 class ODumper(yaml
.Dumper
):
2472 def dict_representer(dumper
, node
):
2473 return dumper
.represent_mapping(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2476 ODumper
.add_representer(collections
.OrderedDict
, dict_representer
)
2478 return yaml
.dump(node
, Dumper
=ODumper
, **kwds
)
2480 def _yaml_ordered_load(self
, yaml_path
):
2481 class OLoader(yaml
.Loader
):
2484 def construct_mapping(loader
, node
):
2485 loader
.flatten_mapping(node
)
2487 return collections
.OrderedDict(loader
.construct_pairs(node
))
2489 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2494 with
open(yaml_path
, 'r') as f
:
2495 node
= yaml
.load(f
, OLoader
)
2496 except (OSError, IOError) as e
:
2497 raise ConfigError('cannot open file "{}"'.format(yaml_path
))
2498 except Exception as e
:
2499 raise ConfigError('unknown error while trying to load file "{}"'.format(yaml_path
), e
)
2501 # loaded node must be an associate array
2502 if not _is_assoc_array_prop(node
):
2503 raise ConfigError('root of YAML file "{}" must be an associative array'.format(yaml_path
))
2508 self
._version
= None
2509 self
._include
_stack
= []
2511 def parse(self
, yaml_path
):
2513 self
._root
_yaml
_path
= yaml_path
2516 root
= self
._yaml
_ordered
_load
(yaml_path
)
2517 except Exception as e
:
2518 raise ConfigError('cannot parse YAML file "{}"'.format(yaml_path
), e
)
2520 if not _is_assoc_array_prop(root
):
2521 raise ConfigError('configuration must be an associative array')
2523 unk_prop
= _get_first_unknown_prop(root
, [
2530 raise ConfigError('unknown configuration property: "{}"'.format(unk_prop
))
2532 # get the config version
2533 self
._version
= self
._get
_version
(root
)
2535 # process includes if supported
2536 if self
._version
>= 201:
2537 root
= self
._process
_root
_includes
(root
)
2539 # dump config if required
2540 if self
._dump
_config
:
2541 print(self
._yaml
_ordered
_dump
(root
, indent
=2,
2542 default_flow_style
=False))
2544 # get prefix and metadata
2545 prefix
= self
._get
_prefix
(root
)
2546 meta
= self
._create
_metadata
(root
)
2548 return Config(self
._version
, prefix
, meta
)
2551 def from_yaml_file(path
, include_dirs
, ignore_include_not_found
, dump_config
):
2553 parser
= _YamlConfigParser(include_dirs
, ignore_include_not_found
,
2555 cfg
= parser
.parse(path
)
2558 except Exception as e
:
2559 raise ConfigError('cannot create configuration from YAML file "{}"'.format(path
), e
)