README.md: add PPA instructions for Ubuntu
[barectf.git] / barectf / config.py
index 9c1a7bd5f21330808db0d123d726f56f7e43ff3b..eb952b6729c5951335c9ef011c357577be90c262 100644 (file)
@@ -1,6 +1,6 @@
 # The MIT License (MIT)
 #
-# Copyright (c) 2015 Philippe Proulx <pproulx@efficios.com>
+# Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -66,15 +66,18 @@ class Config:
             raise ConfigError('barectf metadata error', e)
 
     def _augment_metadata_env(self, meta):
-        env = meta.env
-
-        env['domain'] = 'bare'
-        env['tracer_name'] = 'barectf'
         version_tuple = barectf.get_version_tuple()
-        env['tracer_major'] = version_tuple[0]
-        env['tracer_minor'] = version_tuple[1]
-        env['tracer_patch'] = version_tuple[2]
-        env['barectf_gen_date'] = str(datetime.datetime.now().isoformat())
+        base_env = {
+            'domain': 'bare',
+            'tracer_name': 'barectf'        ,
+            'tracer_major': version_tuple[0],
+            'tracer_minor': version_tuple[1],
+            'tracer_patch': version_tuple[2],
+            'barectf_gen_date': str(datetime.datetime.now().isoformat()),
+        }
+
+        base_env.update(meta.env)
+        meta.env = base_env
 
     @property
     def version(self):
@@ -100,7 +103,7 @@ class Config:
 
     @prefix.setter
     def prefix(self, value):
-        if not is_valid_identifier(value):
+        if not _is_valid_identifier(value):
             raise ConfigError('prefix must be a valid C identifier')
 
         self._prefix = value
@@ -170,7 +173,7 @@ _ctf_keywords = set([
 ])
 
 
-def is_valid_identifier(iden):
+def _is_valid_identifier(iden):
     if not _re_iden.match(iden):
         return False
 
@@ -271,7 +274,7 @@ class _BarectfMetadataValidator:
             raise ConfigError('invalid trace packet header type', e)
 
         for stream_name, stream in meta.streams.items():
-            if not is_valid_identifier(stream_name):
+            if not _is_valid_identifier(stream_name):
                 raise ConfigError('stream name "{}" is not a valid C identifier'.format(stream_name))
 
             self._cur_entity = _Entity.STREAM_PACKET_CONTEXT
@@ -297,7 +300,7 @@ class _BarectfMetadataValidator:
 
             try:
                 for ev_name, ev in stream.events.items():
-                    if not is_valid_identifier(ev_name):
+                    if not _is_valid_identifier(ev_name):
                         raise ConfigError('event name "{}" is not a valid C identifier'.format(ev_name))
 
                     self._cur_entity = _Entity.EVENT_CONTEXT
@@ -309,16 +312,13 @@ class _BarectfMetadataValidator:
 
                     self._cur_entity = _Entity.EVENT_PAYLOAD
 
-                    if ev.payload_type is None:
-                        raise ConfigError('missing payload type in event "{}"'.format(ev_name), e)
-
                     try:
                         self._validate_entity(ev.payload_type)
                     except Exception as e:
                         raise ConfigError('invalid payload type in event "{}"'.format(ev_name), e)
 
-                    if not ev.payload_type.fields:
-                        raise ConfigError('empty payload type in event "{}"'.format(ev_name), e)
+                    if stream.is_event_empty(ev):
+                        raise ConfigError('event "{}" is empty'.format(ev_name))
             except Exception as e:
                 raise ConfigError('invalid stream "{}"'.format(stream_name), e)
 
@@ -335,13 +335,13 @@ class _MetadataSpecialFieldsValidator:
         if len(self._meta.streams) > 1:
             # yes
             if t is None:
-                raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is missing')
+                raise ConfigError('need "stream_id" field in trace packet header type (more than one stream), but trace packet header type is missing')
 
             if type(t) is not metadata.Struct:
-                raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is not a structure type')
+                raise ConfigError('need "stream_id" field in trace packet header type (more than one stream), but trace packet header type is not a structure type')
 
             if 'stream_id' not in t.fields:
-                raise ConfigError('need "stream_id" field in trace packet header type')
+                raise ConfigError('need "stream_id" field in trace packet header type (more than one stream)')
 
         # validate "magic" and "stream_id" types
         if type(t) is not metadata.Struct:
@@ -363,6 +363,10 @@ class _MetadataSpecialFieldsValidator:
 
                 if field_type.signed:
                     raise ConfigError('"stream_id" field in trace packet header type must be an unsigned integer type')
+
+                # "id" size can fit all event IDs
+                if len(self._meta.streams) > (1 << field_type.size):
+                    raise ConfigError('"stream_id" field\' size in trace packet header type is too small for the number of trace streams')
             elif field_name == 'uuid':
                 if self._meta.trace.uuid is None:
                     raise ConfigError('"uuid" field in trace packet header type specified, but no trace UUID provided')
@@ -394,13 +398,15 @@ class _MetadataSpecialFieldsValidator:
         t = stream.packet_context_type
 
         if type(t) is None:
-            return
+            raise ConfigError('missing "packet-context-type" property in stream object')
 
         if type(t) is not metadata.Struct:
-            return
+            raise ConfigError('"packet-context-type": expecting a structure type')
 
         # "timestamp_begin", if exists, is an unsigned integer type,
         # mapped to a clock
+        ts_begin = None
+
         if 'timestamp_begin' in t.fields:
             ts_begin = t.fields['timestamp_begin']
 
@@ -415,6 +421,8 @@ class _MetadataSpecialFieldsValidator:
 
         # "timestamp_end", if exists, is an unsigned integer type,
         # mapped to a clock
+        ts_end = None
+
         if 'timestamp_end' in t.fields:
             ts_end = t.fields['timestamp_end']
 
@@ -431,6 +439,11 @@ class _MetadataSpecialFieldsValidator:
         if (('timestamp_begin' in t.fields) ^ ('timestamp_end' in t.fields)):
             raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
 
+        # "timestamp_begin" and "timestamp_end" are mapped to the same clock
+        if ts_begin is not None and ts_end is not None:
+            if ts_begin.property_mappings[0].object.name != ts_end.property_mappings[0].object.name:
+                raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be mapped to the same clock object in stream packet context type')
+
         # "events_discarded", if exists, is an unsigned integer type
         if 'events_discarded' in t.fields:
             events_discarded = t.fields['events_discarded']
@@ -467,6 +480,10 @@ class _MetadataSpecialFieldsValidator:
         if content_size.signed:
             raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
 
+        # "packet_size" size should be greater than or equal to "content_size" size
+        if content_size.size > packet_size.size:
+            raise ConfigError('"content_size" field size must be lesser than or equal to "packet_size" field size')
+
     def _validate_stream_event_header(self, stream):
         t = stream.event_header_type
 
@@ -474,13 +491,13 @@ class _MetadataSpecialFieldsValidator:
         if len(stream.events) > 1:
             # yes
             if t is None:
-                raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
+                raise ConfigError('need "id" field in stream event header type (more than one event), but stream event header type is missing')
 
             if type(t) is not metadata.Struct:
-                raise ConfigError('need "id" field in stream event header type, but stream event header type is not a structure type')
+                raise ConfigError('need "id" field in stream event header type (more than one event), but stream event header type is not a structure type')
 
             if 'id' not in t.fields:
-                raise ConfigError('need "id" field in stream event header type')
+                raise ConfigError('need "id" field in stream event header type (more than one event)')
 
         # validate "id" and "timestamp" types
         if type(t) is not metadata.Struct:
@@ -492,24 +509,28 @@ class _MetadataSpecialFieldsValidator:
             ts = t.fields['timestamp']
 
             if type(ts) is not metadata.Integer:
-                raise ConfigError('"ts" field in stream event header type must be an integer type')
+                raise ConfigError('"timestamp" field in stream event header type must be an integer type')
 
             if ts.signed:
-                raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
+                raise ConfigError('"timestamp" field in stream event header type must be an unsigned integer type')
 
             if not ts.property_mappings:
-                raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
+                raise ConfigError('"timestamp" field in stream event header type must be mapped to a clock')
 
-        # "id" is an unsigned integer type
         if 'id' in t.fields:
             eid = t.fields['id']
 
+            # "id" is an unsigned integer type
             if type(eid) is not metadata.Integer:
                 raise ConfigError('"id" field in stream event header type must be an integer type')
 
             if eid.signed:
                 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
 
+            # "id" size can fit all event IDs
+            if len(stream.events) > (1 << eid.size):
+                raise ConfigError('"id" field\' size in stream event header type is too small for the number of stream events')
+
     def _validate_stream(self, stream):
         self._validate_stream_packet_context(stream)
         self._validate_stream_event_header(stream)
@@ -813,7 +834,7 @@ class _MetadataDynamicTypesValidator:
         self._stack_pop()
 
     def _visit_array_type(self, t):
-        if not t.is_static:
+        if t.is_variable_length:
             # find length type
             try:
                 length_type = self._lookup_path(t.length, t)
@@ -954,7 +975,8 @@ class _MetadataDynamicTypesValidator:
 
 # Since type inheritance allows types to be only partially defined at
 # any place in the configuration, this validator validates that actual
-# trace, stream, and event types are all complete and valid.
+# trace, stream, and event types are all complete and valid. Therefore
+# an invalid, but unusued type alias is accepted.
 class _MetadataTypesHistologyValidator:
     def __init__(self):
         self._type_to_validate_type_histology_func = {
@@ -988,20 +1010,36 @@ class _MetadataTypesHistologyValidator:
     def _validate_enum_histology(self, t):
         # integer type is set
         if t.value_type is None:
-            raise ConfigError('missing enumeration type\'s integer type')
+            raise ConfigError('missing enumeration type\'s value type')
 
         # there's at least one member
         if not t.members:
             raise ConfigError('enumeration type needs at least one member')
 
-        # no overlapping values
+        # no overlapping values and all values are valid considering
+        # the value type
         ranges = []
 
+        if t.value_type.signed:
+            value_min = -(1 << t.value_type.size - 1)
+            value_max = (1 << (t.value_type.size - 1)) - 1
+        else:
+            value_min = 0
+            value_max = (1 << t.value_type.size) - 1
+
         for label, value in t.members.items():
             for rg in ranges:
                 if value[0] <= rg[1] and rg[0] <= value[1]:
                     raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label))
 
+            fmt = 'enumeration type\'s member "{}": value {} is outside the value type range [{}, {}]'
+
+            if value[0] < value_min or value[0] > value_max:
+                raise ConfigError(fmt.format(label, value[0], value_min, value_max))
+
+            if value[1] < value_min or value[1] > value_max:
+                raise ConfigError(fmt.format(label, value[1], value_min, value_max))
+
             ranges.append(value)
 
     def _validate_string_histology(self, t):
@@ -1057,9 +1095,8 @@ class _MetadataTypesHistologyValidator:
         if t is None:
             return
 
-        # entity cannot be an array
-        if type(t) is metadata.Array:
-            raise ConfigError('cannot use an array here')
+        if type(t) is not metadata.Struct:
+            raise ConfigError('expecting a structure type')
 
         self._validate_type_histology(t)
 
@@ -1073,14 +1110,6 @@ class _MetadataTypesHistologyValidator:
             raise ConfigError('invalid event context type for event "{}"'.format(ev_name), e)
 
         # validate event payload type
-        if ev.payload_type is None:
-            raise ConfigError('event payload type must exist in event "{}"'.format(ev_name))
-
-        # TODO: also check arrays, sequences, and variants
-        if type(ev.payload_type) is metadata.Struct:
-            if not ev.payload_type.fields:
-                raise ConfigError('event payload type must have at least one field for event "{}"'.format(ev_name))
-
         try:
             self._validate_entity_type_histology(ev.payload_type)
         except Exception as e:
@@ -1154,7 +1183,6 @@ class _YamlConfigParser:
             metadata.Variant: self._create_variant,
         }
         self._include_dirs = include_dirs
-        self._include_dirs.append(os.getcwd())
         self._ignore_include_not_found = ignore_include_not_found
         self._dump_config = dump_config
 
@@ -1256,101 +1284,117 @@ class _YamlConfigParser:
         if 'align' in node:
             align = node['align']
 
-            if not _is_int_prop(align):
-                raise ConfigError('"align" property of integer type object must be an integer')
+            if align is None:
+                obj.set_default_align()
+            else:
+                if not _is_int_prop(align):
+                    raise ConfigError('"align" property of integer type object must be an integer')
 
-            if not _is_valid_alignment(align):
-                raise ConfigError('invalid alignment: {}'.format(align))
+                if not _is_valid_alignment(align):
+                    raise ConfigError('invalid alignment: {}'.format(align))
 
-            obj.align = align
+                obj.align = align
 
         # signed
         if 'signed' in node:
             signed = node['signed']
 
-            if not _is_bool_prop(signed):
-                raise ConfigError('"signed" property of integer type object must be a boolean')
+            if signed is None:
+                obj.set_default_signed()
+            else:
+                if not _is_bool_prop(signed):
+                    raise ConfigError('"signed" property of integer type object must be a boolean')
 
-            obj.signed = signed
+                obj.signed = signed
 
         # byte order
         if 'byte-order' in node:
             byte_order = node['byte-order']
 
-            if not _is_str_prop(byte_order):
-                raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
+            if byte_order is None:
+                obj.byte_order = self._bo
+            else:
+                if not _is_str_prop(byte_order):
+                    raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
+
+                byte_order = _byte_order_str_to_bo(byte_order)
 
-            byte_order = _byte_order_str_to_bo(byte_order)
+                if byte_order is None:
+                    raise ConfigError('invalid "byte-order" property in integer type object')
 
-            if byte_order is None:
-                raise ConfigError('invalid "byte-order" property in integer type object')
+                obj.byte_order = byte_order
         else:
-            byte_order = self._bo
-
-        obj.byte_order = byte_order
+            obj.byte_order = self._bo
 
         # base
         if 'base' in node:
             base = node['base']
 
-            if not _is_str_prop(base):
-                raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
-
-            if base == 'bin':
-                base = 2
-            elif base == 'oct':
-                base = 8
-            elif base == 'dec':
-                base = 10
-            elif base == 'hex':
-                base = 16
+            if base is None:
+                obj.set_default_base()
             else:
-                raise ConfigError('unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base))
+                if not _is_str_prop(base):
+                    raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
+
+                if base == 'bin':
+                    base = 2
+                elif base == 'oct':
+                    base = 8
+                elif base == 'dec':
+                    base = 10
+                elif base == 'hex':
+                    base = 16
+                else:
+                    raise ConfigError('unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base))
 
-            obj.base = base
+                obj.base = base
 
         # encoding
         if 'encoding' in node:
             encoding = node['encoding']
 
-            if not _is_str_prop(encoding):
-                raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
+            if encoding is None:
+                obj.set_default_encoding()
+            else:
+                if not _is_str_prop(encoding):
+                    raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
 
-            encoding = _encoding_str_to_encoding(encoding)
+                encoding = _encoding_str_to_encoding(encoding)
 
-            if encoding is None:
-                raise ConfigError('invalid "encoding" property in integer type object')
+                if encoding is None:
+                    raise ConfigError('invalid "encoding" property in integer type object')
 
-            obj.encoding = encoding
+                obj.encoding = encoding
 
         # property mappings
         if 'property-mappings' in node:
             prop_mappings = node['property-mappings']
 
-            if not _is_array_prop(prop_mappings):
-                raise ConfigError('"property-mappings" property of integer type object must be an array')
-
-            if len(prop_mappings) > 1:
-                raise ConfigError('length of "property-mappings" array in integer type object must be 1')
+            if prop_mappings is None:
+                obj.set_default_property_mappings()
+            else:
+                if not _is_array_prop(prop_mappings):
+                    raise ConfigError('"property-mappings" property of integer type object must be an array')
 
-            del obj.property_mappings[:]
+                if len(prop_mappings) > 1:
+                    raise ConfigError('length of "property-mappings" array in integer type object must be 1')
 
-            for index, prop_mapping in enumerate(prop_mappings):
-                if not _is_assoc_array_prop(prop_mapping):
-                    raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
+                for index, prop_mapping in enumerate(prop_mappings):
+                    if not _is_assoc_array_prop(prop_mapping):
+                        raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
 
-                if 'type' not in prop_mapping:
-                    raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index))
+                    if 'type' not in prop_mapping:
+                        raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index))
 
-                prop_type = prop_mapping['type']
+                    prop_type = prop_mapping['type']
 
-                if not _is_str_prop(prop_type):
-                    raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index))
+                    if not _is_str_prop(prop_type):
+                        raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index))
 
-                if prop_type == 'clock':
-                    self._set_int_clock_prop_mapping(obj, prop_mapping)
-                else:
-                    raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type, index))
+                    if prop_type == 'clock':
+                        self._set_int_clock_prop_mapping(obj, prop_mapping)
+                    else:
+                        raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type, index))
 
         return obj
 
@@ -1406,29 +1450,33 @@ class _YamlConfigParser:
         if 'align' in node:
             align = node['align']
 
-            if not _is_int_prop(align):
-                raise ConfigError('"align" property of floating point number type object must be an integer')
+            if align is None:
+                obj.set_default_align()
+            else:
+                if not _is_int_prop(align):
+                    raise ConfigError('"align" property of floating point number type object must be an integer')
 
-            if not _is_valid_alignment(align):
-                raise ConfigError('invalid alignment: {}'.format(align))
+                if not _is_valid_alignment(align):
+                    raise ConfigError('invalid alignment: {}'.format(align))
 
-            obj.align = align
+                obj.align = align
 
         # byte order
         if 'byte-order' in node:
             byte_order = node['byte-order']
 
-            if not _is_str_prop(byte_order):
-                raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
+            if byte_order is None:
+                obj.byte_order = self._bo
+            else:
+                if not _is_str_prop(byte_order):
+                    raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
 
-            byte_order = _byte_order_str_to_bo(byte_order)
+                byte_order = _byte_order_str_to_bo(byte_order)
 
-            if byte_order is None:
-                raise ConfigError('invalid "byte-order" property in floating point number type object')
+                if byte_order is None:
+                    raise ConfigError('invalid "byte-order" property in floating point number type object')
         else:
-            byte_order = self._bo
-
-        obj.byte_order = byte_order
+            obj.byte_order = self._bo
 
         return obj
 
@@ -1447,8 +1495,10 @@ class _YamlConfigParser:
 
         # value type
         if 'value-type' in node:
+            value_type_node = node['value-type']
+
             try:
-                obj.value_type = self._create_type(node['value-type'])
+                obj.value_type = self._create_type(value_type_node)
             except Exception as e:
                 raise ConfigError('cannot create enumeration type\'s integer type', e)
 
@@ -1460,6 +1510,12 @@ class _YamlConfigParser:
                 raise ConfigError('"members" property of enumeration type object must be an array')
 
             cur = 0
+            last_value = obj.last_value
+
+            if last_value is None:
+                cur = 0
+            else:
+                cur = last_value + 1
 
             for index, m_node in enumerate(members_node):
                 if not _is_str_prop(m_node) and not _is_assoc_array_prop(m_node):
@@ -1470,6 +1526,14 @@ class _YamlConfigParser:
                     value = (cur, cur)
                     cur += 1
                 else:
+                    unk_prop = _get_first_unknown_prop(m_node, [
+                        'label',
+                        'value',
+                    ])
+
+                    if unk_prop:
+                        raise ConfigError('unknown enumeration type member object property: "{}"'.format(unk_prop))
+
                     if 'label' not in m_node:
                         raise ConfigError('missing "label" property in enumeration member #{}'.format(index))
 
@@ -1522,15 +1586,18 @@ class _YamlConfigParser:
         if 'encoding' in node:
             encoding = node['encoding']
 
-            if not _is_str_prop(encoding):
-                raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
+            if encoding is None:
+                obj.set_default_encoding()
+            else:
+                if not _is_str_prop(encoding):
+                    raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
 
-            encoding = _encoding_str_to_encoding(encoding)
+                encoding = _encoding_str_to_encoding(encoding)
 
-            if encoding is None:
-                raise ConfigError('invalid "encoding" property in string type object')
+                if encoding is None:
+                    raise ConfigError('invalid "encoding" property in string type object')
 
-            obj.encoding = encoding
+                obj.encoding = encoding
 
         return obj
 
@@ -1551,29 +1618,35 @@ class _YamlConfigParser:
         if 'min-align' in node:
             min_align = node['min-align']
 
-            if not _is_int_prop(min_align):
-                raise ConfigError('"min-align" property of structure type object must be an integer')
+            if min_align is None:
+                obj.set_default_min_align()
+            else:
+                if not _is_int_prop(min_align):
+                    raise ConfigError('"min-align" property of structure type object must be an integer')
 
-            if not _is_valid_alignment(min_align):
-                raise ConfigError('invalid minimum alignment: {}'.format(min_align))
+                if not _is_valid_alignment(min_align):
+                    raise ConfigError('invalid minimum alignment: {}'.format(min_align))
 
-            obj.min_align = min_align
+                obj.min_align = min_align
 
         # fields
         if 'fields' in node:
             fields = node['fields']
 
-            if not _is_assoc_array_prop(fields):
-                raise ConfigError('"fields" property of structure type object must be an associative array')
+            if fields is None:
+                obj.set_default_fields()
+            else:
+                if not _is_assoc_array_prop(fields):
+                    raise ConfigError('"fields" property of structure type object must be an associative array')
 
-            for field_name, field_node in fields.items():
-                if not is_valid_identifier(field_name):
-                    raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name))
+                for field_name, field_node in fields.items():
+                    if not _is_valid_identifier(field_name):
+                        raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name))
 
-                try:
-                    obj.fields[field_name] = self._create_type(field_node)
-                except Exception as e:
-                    raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name), e)
+                    try:
+                        obj.fields[field_name] = self._create_type(field_node)
+                    except Exception as e:
+                        raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name), e)
 
         return obj
 
@@ -1604,6 +1677,8 @@ class _YamlConfigParser:
 
         # element type
         if 'element-type' in node:
+            element_type_node = node['element-type']
+
             try:
                 obj.element_type = self._create_type(node['element-type'])
             except Exception as e:
@@ -1645,7 +1720,7 @@ class _YamlConfigParser:
             # do not validate type names for the moment; will be done in a
             # second phase
             for type_name, type_node in types.items():
-                if not is_valid_identifier(type_name):
+                if not _is_valid_identifier(type_name):
                     raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name))
 
                 try:
@@ -1665,7 +1740,7 @@ class _YamlConfigParser:
             return t
 
         if not _is_assoc_array_prop(type_node):
-            raise ConfigError('type objects must be associative arrays')
+            raise ConfigError('type objects must be associative arrays or strings (type alias name)')
 
         # inherit:
         #   v2.0:  "inherit"
@@ -1723,6 +1798,9 @@ class _YamlConfigParser:
 
         ta_node = metadata_node['type-aliases']
 
+        if ta_node is None:
+            return
+
         if not _is_assoc_array_prop(ta_node):
             raise ConfigError('"type-aliases" property (metadata) must be an associative array')
 
@@ -1766,93 +1844,118 @@ class _YamlConfigParser:
         if 'uuid' in node:
             uuidp = node['uuid']
 
-            if not _is_str_prop(uuidp):
-                raise ConfigError('"uuid" property of clock object must be a string')
+            if uuidp is None:
+                clock.set_default_uuid()
+            else:
+                if not _is_str_prop(uuidp):
+                    raise ConfigError('"uuid" property of clock object must be a string')
 
-            try:
-                uuidp = uuid.UUID(uuidp)
-            except:
-                raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp))
+                try:
+                    uuidp = uuid.UUID(uuidp)
+                except:
+                    raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp))
 
-            clock.uuid = uuidp
+                clock.uuid = uuidp
 
         # description
         if 'description' in node:
             desc = node['description']
 
-            if not _is_str_prop(desc):
-                raise ConfigError('"description" property of clock object must be a string')
+            if desc is None:
+                clock.set_default_description()
+            else:
+                if not _is_str_prop(desc):
+                    raise ConfigError('"description" property of clock object must be a string')
 
-            clock.description = desc
+                clock.description = desc
 
         # frequency
         if 'freq' in node:
             freq = node['freq']
 
-            if not _is_int_prop(freq):
-                raise ConfigError('"freq" property of clock object must be an integer')
+            if freq is None:
+                clock.set_default_freq()
+            else:
+                if not _is_int_prop(freq):
+                    raise ConfigError('"freq" property of clock object must be an integer')
 
-            if freq < 1:
-                raise ConfigError('invalid clock frequency: {}'.format(freq))
+                if freq < 1:
+                    raise ConfigError('invalid clock frequency: {}'.format(freq))
 
-            clock.freq = freq
+                clock.freq = freq
 
         # error cycles
         if 'error-cycles' in node:
             error_cycles = node['error-cycles']
 
-            if not _is_int_prop(error_cycles):
-                raise ConfigError('"error-cycles" property of clock object must be an integer')
+            if error_cycles is None:
+                clock.set_default_error_cycles()
+            else:
+                if not _is_int_prop(error_cycles):
+                    raise ConfigError('"error-cycles" property of clock object must be an integer')
 
-            if error_cycles < 0:
-                raise ConfigError('invalid clock error cycles: {}'.format(error_cycles))
+                if error_cycles < 0:
+                    raise ConfigError('invalid clock error cycles: {}'.format(error_cycles))
 
-            clock.error_cycles = error_cycles
+                clock.error_cycles = error_cycles
 
         # offset
         if 'offset' in node:
             offset = node['offset']
 
-            if not _is_assoc_array_prop(offset):
-                raise ConfigError('"offset" property of clock object must be an associative array')
+            if offset is None:
+                clock.set_default_offset_seconds()
+                clock.set_default_offset_cycles()
+            else:
+                if not _is_assoc_array_prop(offset):
+                    raise ConfigError('"offset" property of clock object must be an associative array')
 
-            unk_prop = _get_first_unknown_prop(offset, ['cycles', 'seconds'])
+                unk_prop = _get_first_unknown_prop(offset, ['cycles', 'seconds'])
 
-            if unk_prop:
-                raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop))
+                if unk_prop:
+                    raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop))
 
-            # cycles
-            if 'cycles' in offset:
-                offset_cycles = offset['cycles']
+                # cycles
+                if 'cycles' in offset:
+                    offset_cycles = offset['cycles']
 
-                if not _is_int_prop(offset_cycles):
-                    raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
+                    if offset_cycles is None:
+                        clock.set_default_offset_cycles()
+                    else:
+                        if not _is_int_prop(offset_cycles):
+                            raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
 
-                if offset_cycles < 0:
-                    raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles))
+                        if offset_cycles < 0:
+                            raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles))
 
-                clock.offset_cycles = offset_cycles
+                        clock.offset_cycles = offset_cycles
 
-            # seconds
-            if 'seconds' in offset:
-                offset_seconds = offset['seconds']
+                # seconds
+                if 'seconds' in offset:
+                    offset_seconds = offset['seconds']
 
-                if not _is_int_prop(offset_seconds):
-                    raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
+                    if offset_seconds is None:
+                        clock.set_default_offset_seconds()
+                    else:
+                        if not _is_int_prop(offset_seconds):
+                            raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
 
-                if offset_seconds < 0:
-                    raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds))
+                        if offset_seconds < 0:
+                            raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds))
 
-                clock.offset_seconds = offset_seconds
+                        clock.offset_seconds = offset_seconds
 
         # absolute
         if 'absolute' in node:
             absolute = node['absolute']
 
-            if not _is_bool_prop(absolute):
-                raise ConfigError('"absolute" property of clock object must be a boolean')
+            if absolute is None:
+                clock.set_default_absolute()
+            else:
+                if not _is_bool_prop(absolute):
+                    raise ConfigError('"absolute" property of clock object must be a boolean')
 
-            clock.absolute = absolute
+                clock.absolute = absolute
 
         # return C type:
         #   v2.0:  "return-ctype"
@@ -1873,10 +1976,13 @@ class _YamlConfigParser:
                 return_ctype_node = node[return_ctype_prop]
 
         if return_ctype_node is not None:
-            if not _is_str_prop(return_ctype_node):
-                raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop))
+            if return_ctype_node is None:
+                clock.set_default_return_ctype()
+            else:
+                if not _is_str_prop(return_ctype_node):
+                    raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop))
 
-            clock.return_ctype = return_ctype_node
+                clock.return_ctype = return_ctype_node
 
         return clock
 
@@ -1888,11 +1994,14 @@ class _YamlConfigParser:
 
         clocks_node = metadata_node['clocks']
 
+        if clocks_node is None:
+            return
+
         if not _is_assoc_array_prop(clocks_node):
             raise ConfigError('"clocks" property (metadata) must be an associative array')
 
         for clock_name, clock_node in clocks_node.items():
-            if not is_valid_identifier(clock_name):
+            if not _is_valid_identifier(clock_name):
                 raise ConfigError('invalid clock name: "{}"'.format(clock_name))
 
             if clock_name in self._clocks:
@@ -1914,6 +2023,9 @@ class _YamlConfigParser:
 
         env_node = metadata_node['env']
 
+        if env_node is None:
+            return env
+
         if not _is_assoc_array_prop(env_node):
             raise ConfigError('"env" property (metadata) must be an associative array')
 
@@ -1921,7 +2033,7 @@ class _YamlConfigParser:
             if env_name in env:
                 raise ConfigError('duplicate environment variable "{}"'.format(env_name))
 
-            if not is_valid_identifier(env_name):
+            if not _is_valid_identifier(env_name):
                 raise ConfigError('invalid environment variable name: "{}"'.format(env_name))
 
             if not _is_int_prop(env_value) and not _is_str_prop(env_value):
@@ -1995,7 +2107,7 @@ class _YamlConfigParser:
         trace.byte_order = self._bo
 
         # UUID
-        if 'uuid' in trace_node:
+        if 'uuid' in trace_node and trace_node['uuid'] is not None:
             uuidp = trace_node['uuid']
 
             if not _is_str_prop(uuidp):
@@ -2012,7 +2124,7 @@ class _YamlConfigParser:
             trace.uuid = uuidp
 
         # packet header type
-        if 'packet-header-type' in trace_node:
+        if 'packet-header-type' in trace_node and trace_node['packet-header-type'] is not None:
             try:
                 ph_type = self._create_type(trace_node['packet-header-type'])
             except Exception as e:
@@ -2043,25 +2155,29 @@ class _YamlConfigParser:
         if unk_prop:
             raise ConfigError('unknown event object property: "{}"'.format(unk_prop))
 
-        if 'log-level' in event_node:
+        if 'log-level' in event_node and event_node['log-level'] is not None:
             ll_node = event_node['log-level']
 
             if _is_str_prop(ll_node):
-                ll = self._lookup_log_level(event_node['log-level'])
+                ll_value = self._lookup_log_level(event_node['log-level'])
 
-                if ll is None:
+                if ll_value is None:
                     raise ConfigError('cannot find log level "{}"'.format(ll_node))
+
+                ll = metadata.LogLevel(event_node['log-level'], ll_value)
             elif _is_int_prop(ll_node):
                 if ll_node < 0:
                     raise ConfigError('invalid log level value {}: value must be positive'.format(ll_node))
 
-                ll = ll_node
+                ll = metadata.LogLevel(None, ll_node)
             else:
                 raise ConfigError('"log-level" property must be either a string or an integer')
 
             event.log_level = ll
 
-        if 'context-type' in event_node:
+        if 'context-type' in event_node and event_node['context-type'] is not None:
+            ctx_type_node = event_node['context-type']
+
             try:
                 t = self._create_type(event_node['context-type'])
             except Exception as e:
@@ -2069,15 +2185,13 @@ class _YamlConfigParser:
 
             event.context_type = t
 
-        if 'payload-type' not in event_node:
-            raise ConfigError('missing "payload-type" property in event object')
-
-        try:
-            t = self._create_type(event_node['payload-type'])
-        except Exception as e:
-            raise ConfigError('cannot create event\'s payload type object', e)
+        if 'payload-type' in event_node and event_node['payload-type'] is not None:
+            try:
+                t = self._create_type(event_node['payload-type'])
+            except Exception as e:
+                raise ConfigError('cannot create event\'s payload type object', e)
 
-        event.payload_type = t
+            event.payload_type = t
 
         return event
 
@@ -2097,7 +2211,7 @@ class _YamlConfigParser:
         if unk_prop:
             raise ConfigError('unknown stream object property: "{}"'.format(unk_prop))
 
-        if 'packet-context-type' in stream_node:
+        if 'packet-context-type' in stream_node and stream_node['packet-context-type'] is not None:
             try:
                 t = self._create_type(stream_node['packet-context-type'])
             except Exception as e:
@@ -2105,7 +2219,7 @@ class _YamlConfigParser:
 
             stream.packet_context_type = t
 
-        if 'event-header-type' in stream_node:
+        if 'event-header-type' in stream_node and stream_node['event-header-type'] is not None:
             try:
                 t = self._create_type(stream_node['event-header-type'])
             except Exception as e:
@@ -2113,7 +2227,7 @@ class _YamlConfigParser:
 
             stream.event_header_type = t
 
-        if 'event-context-type' in stream_node:
+        if 'event-context-type' in stream_node and stream_node['event-context-type'] is not None:
             try:
                 t = self._create_type(stream_node['event-context-type'])
             except Exception as e:
@@ -2126,24 +2240,25 @@ class _YamlConfigParser:
 
         events = stream_node['events']
 
-        if not _is_assoc_array_prop(events):
-            raise ConfigError('"events" property of stream object must be an associative array')
+        if events is not None:
+            if not _is_assoc_array_prop(events):
+                raise ConfigError('"events" property of stream object must be an associative array')
 
-        if not events:
-            raise ConfigError('at least one event is needed within a stream object')
+            if not events:
+                raise ConfigError('at least one event is needed within a stream object')
 
-        cur_id = 0
+            cur_id = 0
 
-        for ev_name, ev_node in events.items():
-            try:
-                ev = self._create_event(ev_node)
-            except Exception as e:
-                raise ConfigError('cannot create event "{}"'.format(ev_name), e)
+            for ev_name, ev_node in events.items():
+                try:
+                    ev = self._create_event(ev_node)
+                except Exception as e:
+                    raise ConfigError('cannot create event "{}"'.format(ev_name), e)
 
-            ev.id = cur_id
-            ev.name = ev_name
-            stream.events[ev_name] = ev
-            cur_id += 1
+                ev.id = cur_id
+                ev.name = ev_name
+                stream.events[ev_name] = ev
+                cur_id += 1
 
         return stream
 
@@ -2241,15 +2356,20 @@ class _YamlConfigParser:
         return version
 
     def _get_prefix(self, root):
+        def_prefix = 'barectf_'
+
         if 'prefix' not in root:
-            return 'barectf_'
+            return def_prefix
 
         prefix_node = root['prefix']
 
+        if prefix_node is None:
+            return def_prefix
+
         if not _is_str_prop(prefix_node):
             raise ConfigError('"prefix" property (configuration) must be a string')
 
-        if not is_valid_identifier(prefix_node):
+        if not _is_valid_identifier(prefix_node):
             raise ConfigError('"prefix" property (configuration) must be a valid C identifier')
 
         return prefix_node
@@ -2292,9 +2412,13 @@ class _YamlConfigParser:
         return None
 
     def _get_include_paths(self, include_node):
+        if include_node is None:
+            return []
+
         if _is_str_prop(include_node):
             return [include_node]
-        elif _is_array_prop(include_node):
+
+        if _is_array_prop(include_node):
             for include_path in include_node:
                 if not _is_str_prop(include_path):
                     raise ConfigError('invalid include property: expecting array of strings')
This page took 0.039838 seconds and 4 git commands to generate.