Source code for otf2.definitions

import re
from itertools import chain
from collections import namedtuple
from six import with_metaclass, integer_types, string_types
from numbers import Number
from builtins import int

import _otf2

from .attribute_value import AttributeValue
from .enums import RegionRole, Paradigm, RegionFlag, LocationType, LocationGroupType, MetricType, \
    MetricMode, MetricScope, MetricOccurrence, RecorderKind, GroupType, GroupFlag, Type, Base, \
    Undefined, ParameterType, SystemTreeDomain, CartPeriodicity, InterruptGeneratorMode, \
    ParadigmClass, IoParadigmClass, IoParadigmFlag, IoParadigmProperty, \
    IoHandleFlag, IoAccessMode, IoStatusFlag, CommFlag, RmaWinFlag
from . import enums  # Needed for namespace issue with Paradigm and ParadigmProperty
from .error import TraceReaderError, TraceWriterError


class DefinitionField(namedtuple("DefinitionField", ['name', 'type', 'private_name', 'default'])):
    def check_value(self, value):
        if value is None:
            return None

        expected_type = self.type

        # Special case: Strings are supposed to be created as native strings
        # instead of String definitions
        if expected_type is String:
            expected_type = string_types

        if isinstance(value, expected_type):
            return value

        if expected_type is tuple:
            return tuple(value)

        raise TypeError("Wrong type for {}, expected {}, actual {}"
                        .format(self.name, expected_type, type(value)))


def _generate_key(value):
    if isinstance(value, tuple):
        return tuple(_generate_key(e) for e in value)
    if isinstance(value, _RefDefinition):
        return type(value), value._ref
    return value


no_default = object()
self_type = object()


def _to_snake(name):
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()

def _pluralize(name):
    if name[-1] == 's' and not name[-2] == 's':
        return name
    if name[-2:] == 'ss':
        return name + 'es'
    if name[-1] == 'y':
        return name[:-1] + 'ies'
    else:
        return name + 's'

def _make_property(field):
    """
    Create properties to access the private name
    """
    def getter(self):
        return getattr(self, field.private_name)

    def setter(self, value):
        self._set_field(field, value)

    return property(getter, setter)


definitions = []


def _generate_doc(name, field_descriptions):
    doc = ""
    for field in field_descriptions:
        if isinstance(field.type, tuple):
            field_type = field.type[0]
        else:
            field_type = field.type

        type_name = field_type.__name__
        if type_name == "String":
            module = ""
            type_name = "str"
        elif field_type.__module__ == "__builtin__":
            module = ""
        elif field_type.__module__ == "_otf2.GeneralDefinitions":
            module = "otf2."
        elif field_type.__module__ == "_otf2.Definitions":
            module = "otf2."
        elif type_name == "AttributeValue":
            module = ""
            type_name = "value_type"
        else:
            module = "{}.".format(field_type.__module__)

        doc += ":param {module}{type_name} {field.name}: ".format(field=field, module=module,
                                                                  type_name=type_name)
        if field_type == Type and field.default is None:
            doc += "The type is inferred from the current value and should " \
                   "not be provided by the user."
        elif field.default is no_default:
            doc += "required argument"
        else:
            doc += "default value {!r}".format(field.default)
        doc += "\n"

    return doc


class _DefinitionMeta(type):
    def __new__(mcls, name, bases, namespace):
        fields = namespace.get('_fields', ())
        namespace['__slots__'] = \
            tuple("_" + field[0] for field in fields) \
            + namespace.get('__slots__', ())

        namespace.setdefault('_registry_name', '_' + _pluralize(_to_snake(name)))

        clazz = type.__new__(mcls, name, bases, namespace)

        field_descriptions = []
        field_names = []
        for field in fields:
            try:
                field_name, default, field_type = field
            except ValueError:
                field_name, default = field
                # Heuristic to guess a good type.
                field_type = type(default)
                # String is special
                if field_type is str:
                    field_type = String
                elif isinstance(default, int):
                    field_type = int

            if field_type is self_type:
                field_type = clazz
            # Avoid stupid bugs
            assert isinstance(field_type, type) or all(isinstance(t, type) for t in field_type)
            assert not isinstance(default, type)

            private_name = "_" + field_name
            description = DefinitionField(field_name, field_type, private_name, default)
            field_descriptions.append(description)
            field_names.append(field_name)
            if not hasattr(clazz, field_name):
                setattr(clazz, field_name, _make_property(description))

        for base in bases:
            field_descriptions = list(getattr(base, '_fields', ())) + field_descriptions

        clazz._fields = tuple(field_descriptions)
        doc = _generate_doc(name, field_descriptions)
        if clazz.__doc__:
            doc = clazz.__doc__ + "\n" + doc
        clazz.__doc__ = doc
        if '_abstract' not in namespace and not name == "NewBase":
            definitions.append(clazz)
        return clazz


class _RefDefinitionMeta(_DefinitionMeta):
    def __new__(mcls, name, bases, namespace):
        if '_abstract' not in namespace and not name == "NewBase":
            namespace.setdefault('_ref_name', _to_snake(name))
            namespace.setdefault('_registry_name', '_' + _pluralize(namespace['_ref_name']))
            namespace.setdefault('undefined_ref', getattr(Undefined, namespace['_ref_name'].upper()))
            try:
                namespace.setdefault('type_id', getattr(Type, namespace['_ref_name'].upper()))
            except:
                pass

        return _DefinitionMeta.__new__(mcls, name, bases, namespace)


class _Definition(with_metaclass(_DefinitionMeta, object)):
    """
    Any definition. It is attached to a registry.
    """
    __slots__ = '_registry',
    _abstract = True

    def __init__(self, registry, *args, **kwargs):
        self._registry = registry

        if len(self._fields) < len(args) + len(kwargs):
            raise TypeError("Too many arguments.")

        for idx, field in enumerate(self._fields):
            try:
                value = args[idx]
            except IndexError:
                if field.name in kwargs:
                    value = kwargs.pop(field.name)
                elif field.default is not no_default:
                    value = field.default
                    # Avoid mutating the default itself...
                    if type(value) is tuple:
                        value = value.copy()
                else:
                    raise TypeError("Required parameter '{}' missing.".format(field.name))

            value = field.check_value(value)
            setattr(self, field.private_name, value)
        if kwargs:
            raise TypeError("Unused argument.")

    @staticmethod
    def _construct_arg(registry, def_type, val):
        if isinstance(def_type, type) and issubclass(def_type, _RefDefinition):
            return registry._get(def_type, val)
        return val

    @classmethod
    def _construct(cls, registry, *args):
        """ Construct a definition based on the parameters of a callback """
        conv_args = [cls._construct_arg(registry, field.type, arg)
                     for arg, field in zip(args, cls._fields)]
        return cls(registry._registry_for_type(cls), *conv_args)

    def _set_field(self, field, value):
        if self._registry is None:
            raise AttributeError("This object is immutable")
        old_key = self._key
        value = field.check_value(value)
        setattr(self, field.private_name, value)
        self._registry._update(old_key, self)

    @classmethod
    def _new_key(cls, registry, *args, **kwargs):
        # Create fake object without registry and ref just to get a key
        return cls(registry, None, *args, **kwargs)._key

    @property
    def _key(self):
        """
        A key identifies the actual values of a definition.
        It shall be valid for hashing and comparison.
        """
        # Possible optimization: Cache key!
        field_keys = [_generate_key(getattr(self, field.private_name)) for field in self._fields]
        # Put the class first when mixing different definition classes
        return tuple(chain((type(self),), field_keys))

    @staticmethod
    def _write_arg(writer, definition_registry, obj_type, obj):
        if obj is None:
            return obj_type.undefined_ref
        if obj_type is String:
            return definition_registry.strings.get_ref(obj, writer)
        if isinstance(obj_type, type) and issubclass(obj_type, _RefDefinition):
            return obj._ref
        if obj_type is AttributeValue:
            return obj.as_otf2(definition_registry, writer)
        return obj

    def write(self, writer, def_registry):
        if def_registry._registry_for_type(type(self)) is not self._registry:
            raise ValueError("Trying to write a definition with the wrong writer.")
        args = [self._write_arg(writer, def_registry, field.type,
                                getattr(self, field.private_name))
                for field in self._fields]
        writer.write(self, *args)

    def _str_fields(self):
        return ", ".join("'{}': {!r}".format(field.name, getattr(self, field.name))
                         for field in self._fields)

    def __str__(self):
        return "{}: {}".format(type(self).__name__, self._str_fields())


class _RefDefinition(with_metaclass(_RefDefinitionMeta, _Definition)):
    """
    A Definition with a ref (id) and a corresponding IdRegistry that manages this definition.
    """
    __slots__ = '_ref', '_properties'
    _abstract = True

    def __init__(self, registry, ref, *args, **kwargs):
        """ The constructor of a Definition is called by a registry"""
        self._ref = ref
        self._properties = {}
        super(_RefDefinition, self).__init__(registry, *args, **kwargs)

    @classmethod
    def _construct(cls, registry, ref, *args):
        """ Construct a definition based on the parameters of a callback """
        conv_args = [cls._construct_arg(registry, field.type, arg)
                     for arg, field in zip(args, cls._fields)]
        return cls(registry._registry_for_type(cls), ref, *conv_args)

    def write(self, writer, def_registry):
        if def_registry._registry_for_type(type(self)) is not self._registry:
            raise ValueError("Trying to write a definition with the wrong writer.")
        args = [self._write_arg(writer, def_registry, field.type,
                                getattr(self, field.private_name))
                for field in self._fields]
        writer.write(self, self._ref, *args)

    def __getitem__(self, key):
        return self._properties[key].value

    def __setitem__(self, key, value):
        self._properties[key].value = value

    def _add_property(self, key, property):
        assert(key not in self._properties)
        self._properties[key] = property

    def __str__(self):
        return "{} [{}]: {}".format(type(self).__name__, self._ref, self._str_fields())

    def __repr__(self):
        return "{} [{}]".format(type(self).__name__, self._ref)


[docs]class String(_RefDefinition): _fields = ('name', no_default, string_types),
class _NamedRefDefinition(_RefDefinition): """ A definition with a name field as the first argument which is enforced to be of type str. """ _fields = ('name', no_default, String), _abstract = True def __init__(self, registry, ref, name=None, *args, **kwargs): if name is None: name = "{} #{}".format(self.__class__.__name__, ref) super(_NamedRefDefinition, self).__init__(registry, ref, name, *args, **kwargs) def __repr__(self): return "{} [{}] '{}'".format(type(self).__name__, self._ref, self.name) class _PropertyDefinition(_Definition): """ Common base class for property definitions. All derived classes must have _fields[2] = 'type', _fields[3] = 'value' """ _abstract = True def __init__(self, registry, arg0, arg1, type_id=None, value=None): if value is None: raise TypeError("Missing argument 'value' for PropertyDefinition.") if type_id is None: type_id = get_type_id(value) if not isinstance(value, AttributeValue): value = AttributeValue(value, type_id) if type_id != value.type_id: raise TypeError("Conflicting AttributeValue type ids: {} != {}".format(type_id, value.type_id)) super(_PropertyDefinition, self).__init__(registry, arg0, arg1, type_id, value) if isinstance(arg0, _RefDefinition): if isinstance(arg1, Parameter): # Basically any _NamedRefDefinition would be ok, but there's no use case currently key = arg1.name elif isinstance(arg1, string_types): key = arg1 else: raise TypeError("Invalid type for name (can be string or Parameter)") # Adds property to referenced definitons 'arg0' under name 'arg1' arg0._add_property(key, self) @classmethod def _construct(cls, registry, arg0, arg1, type_id, value): value = AttributeValue._construct(registry, value, type_id) return super(_PropertyDefinition, cls)._construct(registry, arg0, arg1, type_id, value) @property def type(self): return self._value.type_id @property def value(self): return self._value.value @value.setter def value(self, new_value): self._value.set(new_value) def write(self, writer, def_registry): self._type = self._value.type_id super(_PropertyDefinition, self).write(writer, def_registry)
[docs]class ClockProperties(_Definition): # We don't want numbers.Number here. _fields = (('timer_resolution', no_default, integer_types), ('global_offset', 0, integer_types), ('trace_length', 0, integer_types), ('realtime_timestamp', Undefined.TIMESTAMP.value, integer_types))
[docs]class Attribute(_NamedRefDefinition): type_id = Type.ATTRIBUTE _fields = (('description', ''), ('type', Type.STRING))
[docs]class SystemTreeNode(_NamedRefDefinition): """ .. Note:: The root of the tree should have the parent `None` """ _fields = (('class_name', ''), ('parent', None, self_type))
[docs]class LocationGroup(_NamedRefDefinition): _fields = (('location_group_type', LocationGroupType.PROCESS), ('system_tree_parent', no_default, SystemTreeNode), ('creating_location_group', None, self_type))
[docs]class Location(_NamedRefDefinition): """ .. Note:: The number of events is managed automatically when writing traces. Do not use the attribute when writing. """ _fields = (('type', LocationType.CPU_THREAD), ('number_of_events', 0), ('group', no_default, LocationGroup)) __slots__ = '_number_of_events_written', def write(self, writer, def_registry): try: self._number_of_events = self._number_of_events_written except AttributeError: # If no writer was opened for this definition, the attribute won't be assigned. pass super(Location, self).write(writer, def_registry)
[docs]class Region(_NamedRefDefinition): _fields = (('canonical_name', None, String), ('description', ''), ('region_role', RegionRole.FUNCTION), ('paradigm', enums.Paradigm.NONE), ('region_flags', RegionFlag.NONE), ('source_file', None, String), ('begin_line_number', 0), ('end_line_number', 0)) def __init__(self, registry, ref, name=None, canonical_name=None, *args, **kwargs): if name is None: name = "{} #{}".format(self.__class__.__name__, ref) if canonical_name is None: canonical_name = name super(Region, self).__init__(registry, ref, name, canonical_name, *args, **kwargs)
[docs]class Callsite(_RefDefinition): _fields = (('source_file', None, String), ('line_number', 0), ('entered_region', no_default, Region), ('left_region', no_default, Region))
[docs]class Callpath(_RefDefinition): """ .. Note:: Use `None` as parent to indicate that this is the topmost region in the Callpath. """ _fields = (('parent', no_default, self_type), ('region', no_default, Region))
[docs]class Group(_NamedRefDefinition): """ Groups of type COMM_GROUP may be constructed from a sequence of either ranks or location objects as members argument. All other group types must be constructed using a sequence of their respective Definition objects. The members property is always a sequence of Definition objects, even when the Group is constructed using a sequence of ranks. """ _fields = (('group_type', no_default, GroupType), ('paradigm', enums.Paradigm.NONE), ('group_flags', GroupFlag.NONE), ('members', no_default, tuple)) def __init__(self, registry, ref, name=None, group_type=no_default, paradigm=enums.Paradigm.NONE, group_flags=GroupFlag.NONE, members=no_default): if group_type is no_default: raise TypeError("Required parameter 'group_type' missing.") if members is no_default: raise TypeError("Required parameter 'members' missing.") if group_type == GroupType.COMM_GROUP and len(members) > 0: if isinstance(members[0], Location): if not all(isinstance(l, Location) for l in members): raise TypeError("members for a COMM_GROUP must be either all Locations or " "all ranks") else: parent_group = None for group in registry: if group.paradigm == paradigm and group.group_type == GroupType.COMM_LOCATIONS: parent_group = group if parent_group is None: raise TraceReaderError( "Missing referenced group for sup-group: '{}' #{}".format(name, ref)) members = tuple(parent_group.location(mref) for mref in members) super(Group, self).__init__(registry, ref, name, group_type, paradigm, group_flags, members) # Group is special: # It converts a a _field between list of Definitions and list of references @classmethod def _construct(cls, registry, ref, name, group_type, paradigm, group_flags, member_refs): if group_type == GroupType.LOCATIONS: member_reg = registry.locations elif group_type == GroupType.REGIONS: member_reg = registry.regions elif group_type == GroupType.METRIC: member_reg = registry.metrics elif group_type == GroupType.COMM_LOCATIONS: member_reg = registry.locations elif group_type == GroupType.COMM_GROUP: # Defer rank->location conversion to normal constructor return cls(registry._registry_for_type(cls), ref, registry.strings[name], group_type, paradigm, group_flags, member_refs) elif group_type == GroupType.COMM_SELF: member_reg = None else: raise TypeError("Unknown group_type: {}".format(group_type)) members = [member_reg[mref] for mref in member_refs] return cls(registry._registry_for_type(cls), ref, registry.strings[name], group_type, paradigm, group_flags, members) def write(self, writer, def_registry): if def_registry._registry_for_type(type(self)) is not self._registry: raise ValueError("Trying to write a definition with the wrong writer.") args = [self._write_arg(writer, def_registry, field.type, getattr(self, field.private_name)) for field in self._fields] if self._group_type == GroupType.COMM_GROUP: super_group_members = None for group in def_registry.groups: if group.paradigm == self._paradigm and group.group_type == GroupType.COMM_LOCATIONS: super_group_members = group.members if super_group_members is None: raise TraceWriterError( "Missing referenced group for sup-group: '{}' #{}".format(self._name, self._ref)) args[-1] = [super_group_members.index(member) for member in self._members] writer.write(self, self._ref, *args) @staticmethod def _write_arg(writer, definition_registry, obj_type, obj): if obj_type is tuple: return tuple(m._ref for m in obj) return _NamedRefDefinition._write_arg(writer, definition_registry, obj_type, obj)
[docs] def rank(self, location): """ Get the rank / index of a given :py:class:`Location` or any other contained Definition with respect to this group. """ # TODO optimize with bidirectional map return self.members.index(location)
[docs] def location(self, rank): """ Get the :py:class:`Location` or other Definition object for a given rank within this group """ return self.members[rank]
[docs]class MetricMember(_NamedRefDefinition): _fields = (('description', ''), ('metric_type', MetricType.OTHER), ('metric_mode', MetricMode.ABSOLUTE_POINT), ('value_type', Type.DOUBLE), ('base', Base.DECIMAL), ('exponent', 0), ('unit', '')) def _convert_value(self, value): union_value = _otf2.MetricValue() if self.value_type == Type.UINT64: union_value.unsigned_int = int(value) elif self.value_type == Type.INT64: union_value.signed_int = int(value) elif self.value_type == Type.DOUBLE: union_value.floating_point = float(value) else: raise TypeError("Cannot convert to metric value type.") return union_value def _convert_union(self, union_value): """ :param union_value: from low level, is actually a ctypes.Union :return: nice native python value of type corresponding to our value_type """ if self.value_type == Type.UINT64: return union_value.unsigned_int elif self.value_type == Type.INT64: return union_value.signed_int elif self.value_type == Type.DOUBLE: return union_value.floating_point else: raise TypeError("Cannot convert to metric value type.")
[docs]class Metric(_RefDefinition): """ Abstract base class for :py:class:`MetricClass` and :py:class:`MetricInstance` definitions. """ _ref_name = 'metric'
[docs]class MetricClass(Metric): _ref_name = 'metric' _fields = (('members', no_default, tuple), ('occurrence', MetricOccurrence.ASYNCHRONOUS), ('recorder_kind', RecorderKind.UNKNOWN)) # MetricClass is special: # It converts a a _field between tuple of Definitions and list of references @classmethod def _construct(cls, registry, ref, member_refs, occurence, recorder_kind): members = tuple(registry.metric_members[member_ref] for member_ref in member_refs) return super(MetricClass, cls)._construct(registry, ref, members, occurence, recorder_kind) @staticmethod def _write_arg(writer, definition_registry, obj_type, obj): if obj_type is tuple: return [m._ref for m in obj] return _RefDefinition._write_arg(writer, definition_registry, obj_type, obj) @property def _type_ids(self): return [member.value_type for member in self.members] def _convert_values(self, values): return [member._convert_value(value) for value, member in zip(values, self.members)] def _convert_unions(self, unions): return [member._convert_union(union) for union, member in zip(unions, self.members)] @property def member(self): if len(self.members) != 1: raise AttributeError("Trying to read single member from MetricClass with {} " "members".format(len(self.members))) return self.members[0]
[docs]class MetricInstance(Metric): """ .. Note:: The metric_scope is always computed based on the type of the current scope. Do not explicitly provide that argument. """ _ref_name = 'metric' _fields = (('metric_class', no_default, MetricClass), ('recorder', no_default, Location), ('metric_scope', None, MetricScope), ('scope', no_default, _RefDefinition)) def __init__(self, registry, ref, metric_class, recorder, metric_scope=None, scope=None): if scope is None: raise TypeError("Missing argument 'scope' for MetricInstance.") if metric_scope is None: if isinstance(scope, Location): metric_scope = MetricScope.LOCATION elif isinstance(scope, LocationGroup): metric_scope = MetricScope.LOCATION_GROUP elif isinstance(scope, SystemTreeNode): metric_scope = MetricScope.SYSTEM_TREE_NODE elif isinstance(scope, Group): metric_scope = MetricScope.GROUP else: raise TypeError("Invalid scope type: {}.".format(type(scope))) super(MetricInstance, self).__init__(registry, ref, metric_class, recorder, metric_scope, scope) @property def metric_scope(self): try: return { Location: MetricScope.LOCATION, LocationGroup: MetricScope.LOCATION_GROUP, SystemTreeNode: MetricScope.SYSTEM_TREE_NODE, Group: MetricScope.GROUP, }[type(self.scope)] except KeyError: raise TypeError("Invalid scope type.") @classmethod def _construct(cls, registry, ref, metric_class, recorder, metric_scope, scope): if metric_scope == MetricScope.LOCATION: scope_reg = registry.locations elif metric_scope == MetricScope.LOCATION_GROUP: scope_reg = registry.location_groups elif metric_scope == MetricScope.SYSTEM_TREE_NODE: scope_reg = registry.system_tree_nodes elif metric_scope == MetricScope.GROUP: if registry.groups[scope].group_type == GroupType.METRIC: raise TypeError("You cannot refer to a group of type metric from a metric. " "Really, think about it, it doesn't make any sense.") scope_reg = registry.groups else: raise TypeError("Unexpected MetricScope") return cls(registry._registry_for_type(cls), ref, registry.metrics[metric_class], registry.locations[recorder], metric_scope, scope_reg[scope]) @property def _type_ids(self): return self.metric_class._type_ids def _convert_values(self, values): return self.metric_class._convert_values(values) def _convert_unions(self, unions): return self.metric_class._convert_unions(unions) @property def members(self): return self.metric_class.members @property def member(self): if len(self.members) != 1: raise AttributeError("Trying to read single member from MetricClass with {} " "members".format(len(self.members))) return self.members[0] def write(self, writer, def_registry): self._metric_scope = self.metric_scope super(MetricInstance, self).write(writer, def_registry)
[docs]class Comm(_NamedRefDefinition): _fields = (('group', no_default, Group), ('parent', None, self_type), ('flags', CommFlag.NONE))
[docs] def rank(self, location): """ Get the rank / index of a given :py:class:`Location` with respect to this communicator. """ return self.group.rank(location)
[docs] def location(self, rank): """ Get the :py:class:`Location` for a given rank within this communicator """ return self.group.location(rank)
[docs]class InterComm(Comm): _ref_name = 'comm' _fields = (('groupA', no_default, Group), ('groupB', no_default, Group), ('parent', None, self_type), ('flags', CommFlag.NONE))
# def rank(self, location): # """ # Get the rank / index of a given :py:class:`Location` with respect to this communicator. # """ # return self.group.rank(location) # def location(self, rank): # """ # Get the :py:class:`Location` for a given rank within this communicator # """ # return self.group.location(rank)
[docs]class Parameter(_NamedRefDefinition): _fields = (('parameter_type', ParameterType.STRING),)
[docs]class RmaWin(_NamedRefDefinition): _fields = (('comm', no_default, Comm), ('flags', RmaWinFlag.NONE))
[docs] def rank(self, location): """ Get the rank / index of a given :py:class:`Location` with respect to the communicator of this RMA window. """ return self.comm.rank(location)
[docs] def location(self, rank): """ Get the :py:class:`Location` for a given rank within the communicator of this RMA window. """ return self.comm.location(rank)
[docs]class MetricClassRecorder(_Definition): _fields = (('metric_class', no_default, MetricClass), ('recorder', no_default, Location))
[docs]class SystemTreeNodeProperty(_PropertyDefinition): _fields = (('system_tree_node', no_default, SystemTreeNode), ('name', no_default, String), ('type', None, Type), ('value', no_default, AttributeValue))
[docs]class SystemTreeNodeDomain(_Definition): _fields = (('system_tree_node', no_default, SystemTreeNode), ('system_tree_node_domain', no_default, SystemTreeDomain))
[docs]class LocationGroupProperty(_PropertyDefinition): _fields = (('location_group', no_default, LocationGroup), ('name', no_default, String), ('type', None, Type), ('value', no_default, AttributeValue))
[docs]class LocationProperty(_PropertyDefinition): _fields = (('location', no_default, Location), ('name', no_default, String), ('type', None, Type), ('value', no_default, AttributeValue))
[docs]class CartDimension(_NamedRefDefinition): _fields = (("size", no_default, int), ("cart_periodicity", CartPeriodicity.FALSE))
[docs]class CartTopology(_NamedRefDefinition): """ .. Note:: Dimensions is a tuple of :class:`otf2.definitions.CartDimension`. """ _fields = (("communicator", no_default, Comm), ("dimensions", no_default, tuple)) @classmethod def _construct(cls, registry, ref, name, communicator, dimension_refs): dimensions = tuple(registry.cart_dimensions[dimension_ref] for dimension_ref in dimension_refs) return cls(registry._registry_for_type(cls), ref, registry.strings[name], registry.comms[communicator], dimensions) @staticmethod def _write_arg(writer, definition_registry, obj_type, obj): if obj_type is tuple: return [m._ref for m in obj] return _NamedRefDefinition._write_arg(writer, definition_registry, obj_type, obj)
[docs]class CartCoordinate(_Definition): """ .. Note:: Coordinates is a tuple of integers. """ _fields = (("topology", no_default, CartTopology), ("rank", no_default, int), ("coordinates", no_default, tuple))
[docs]class SourceCodeLocation(_RefDefinition): _fields = (('file', no_default, String), ('line_number', 0))
[docs]class CallingContext(_RefDefinition): """ .. Note:: Use `None` as parent to indicate that this is the topmost CallingContext """ _fields = (("region", no_default, Region), ("source_code_location", None, SourceCodeLocation), ("parent", no_default, self_type))
[docs]class CallingContextProperty(_PropertyDefinition): _fields = (('calling_context', no_default, CallingContext), ('name', no_default, String), ('type', None, Type), ('value', no_default, AttributeValue))
[docs]class InterruptGenerator(_NamedRefDefinition): _fields = (("interrupt_generator_mode", InterruptGeneratorMode.TIME), ("base", Base.DECIMAL), ("exponent", 0), ("period", no_default, int))
[docs]class Paradigm(_Definition): """ .. Note:: This definition has no reference. It describes an enum. """ _fields = (("paradigm", no_default, enums.Paradigm), ("name", no_default, String), ("paradigm_class", no_default, ParadigmClass))
[docs]class ParadigmProperty(_PropertyDefinition): """ .. Warning:: Does **not** refer to a :class:`otf2.definitions.Paradigm`, but :class:`otf2.Paradigm`! """ _fields = (("paradigm", no_default, enums.Paradigm), ("property", no_default, enums.ParadigmProperty), ("type", None, Type), ("value", no_default, AttributeValue))
[docs]class IoFile(_NamedRefDefinition): """ Abstract base class for :py:class:`IoRegularFile` and :py:class:`IoDirectory` definitions. """ _ref_name = 'io_file'
[docs]class IoRegularFile(IoFile): _ref_name = 'io_file' _fields = (("scope", no_default, SystemTreeNode),)
[docs]class IoDirectory(IoFile): _ref_name = 'io_file' _fields = (("scope", no_default, SystemTreeNode),)
[docs]class IoFileProperty(_PropertyDefinition): _fields = (("io_file", no_default, IoFile), ('name', no_default, String), ('type', None, Type), ('value', no_default, AttributeValue))
[docs]class IoParadigm(_RefDefinition): _fields = (("identification", no_default, String), ("name", no_default, String), ("io_paradigm_class", IoParadigmClass.SERIAL), ("io_paradigm_flags", IoParadigmFlag.NONE)) # Reusing _properties from _RefDefinitions # They don't factor into the key def __init__(self, registry, ref, identification, name, io_paradigm_class, io_paradigm_flags, properties=None): if properties is None: properties = {} for property, value in properties.items(): if not isinstance(property, IoParadigmProperty): raise TypeError("property_values for IoParadigm must be a dict with keys of type " "IoParadigmProperty. Violating key: {}".format(property)) if not isinstance(value, AttributeValue): properties[property] = AttributeValue(value, get_type_id(value)) super(IoParadigm, self).__init__(registry, ref, identification, name, io_paradigm_class, io_paradigm_flags) self._properties = properties def __setitem__(self, property, value): # We can set the properties directly here, even if no AttributeValue is set yet if isinstance(value, AttributeValue): self._properties[property] = value else: assert property in self._properties self._properties[property].value = value @classmethod def _construct(cls, registry, ref, identification, name, io_paradigm_class, io_paradigm_flags, properties, types, values): property_attribute_values = {property: AttributeValue._construct(registry, value, type_id) for property, type_id, value in zip(properties, types, values)} return super(IoParadigm, cls)._construct(registry, ref, identification, name, io_paradigm_class, io_paradigm_flags, property_attribute_values) def write(self, writer, def_registry): if def_registry._registry_for_type(type(self)) is not self._registry: raise ValueError("Trying to write a definition with the wrong writer.") args = [self._write_arg(writer, def_registry, field.type, getattr(self, field.private_name)) for field in self._fields] properties_list = list(self._properties.keys()) types_list = [av.type_id for av in self._properties.values()] values_list = [av.as_otf2(def_registry, writer) for av in self._properties.values()] writer.write(self, self._ref, *(args + [properties_list, types_list, values_list]))
[docs]class IoHandle(_NamedRefDefinition): _fields = (("file", no_default, IoFile), ("io_paradigm", no_default, IoParadigm), ("io_handle_flags", IoHandleFlag.NONE), ("comm", None, Comm), ("parent", None, self_type))
[docs]class IoPreCreatedHandleState(_Definition): _fields = (('io_handle', no_default, IoHandle), ('io_access_mode', no_default, IoAccessMode), ('io_status_flags', IoStatusFlag.NONE))
[docs]class CallpathParameter(_PropertyDefinition): _fields = (('callpath', no_default, Callpath), ('parameter', no_default, Parameter), ('type', None, Type), ('value', no_default, AttributeValue))
def get_type_id(value): if isinstance(value, AttributeValue): return value.type_id elif isinstance(value, int): return Type.INT64 elif isinstance(value, Number): return Type.DOUBLE elif isinstance(value, str): return Type.STRING elif isinstance(value, _RefDefinition) and hasattr(value, 'type_id'): return value.type_id else: raise TypeError("Encountered value with unexpected type for an attribute value: " "'{} ({})'".format(value, type(value))) __all__ = [definition.__name__ for definition in definitions] __all__ += ['get_type_id']