Example #1
0
    def __init__(self, op_str, event_checkers, check=True, prefix_str=''):
        super().__init__(check=check)
        checker_list = []
        optional_checker_list = []
        for checker in event_checkers:
            # "unwrap" checkers of the same type, to avoid useless levels of
            # nesting. This is valid since the operator is known to be
            # associative. We don't use isinstance to avoid merging checkers
            # that may have different semantics.
            if type(checker) is type(self):
                checker_list.extend(checker.checkers)
            # Aggregate them separately to avoid having multiple of them
            elif isinstance(checker, OptionalTraceEventChecker):
                optional_checker_list.append(checker)
            else:
                checker_list.append(checker)

        if optional_checker_list:
            checker_list.append(
                OptionalTraceEventChecker(optional_checker_list))

        # Avoid having the same event twice at the same level
        def key(checker):
            if isinstance(checker, TraceEventChecker):
                return checker.event
            else:
                return checker

        checker_list = deduplicate(checker_list, key=key)

        self.checkers = checker_list
        self.op_str = op_str
        self.prefix_str = prefix_str
Example #2
0
    def _df_format(self, df):
        tag_cols = self._restrict_cols(self._stat_tag_cols, df)
        # Group together lines for each given tag
        df = df.sort_values(by=tag_cols, ignore_index=True)

        # Reorder columns
        cols = deduplicate(
            deduplicate(
                tag_cols + [
                    self._stat_col, self._val_col, self._unit_col,
                    self._control_var_col, self._mean_kind_col
                ],
                keep_last=True,
            ) + list(df.columns),
            keep_last=False,
        )
        return df[[col for col in cols if col in df.columns]]
Example #3
0
    def __new__(cls, name, bases, dct, *args, types=None, **kwargs):
        try:
            typeclass = bases[0]
        # That's TypeClass itself
        except IndexError:
            return super().__new__(cls, name, bases, dct, *args, **kwargs)

        # That's a typeclass being defined
        if types is None:
            dct.update(
                INSTANCES={},
                DEFAULTS={},
                REQUIRED=dict(),
            )

            superclasses = deduplicate(bases, keep_last=False)
            with contextlib.suppress(ValueError):
                superclasses.remove(TypeClass)
            dct['SUPERCLASSES'] = superclasses

            for typeclass in superclasses:
                conflicting = {
                    name
                    for name in dct['REQUIRED'].keys()
                    & typeclass.REQUIRED.keys()
                    # If required method was specified in a base typeclass that
                    # happens to be shared, there is no problem
                    if dct['REQUIRED'][name] is not typeclass.REQUIRED[name]
                }
                if conflicting:

                    def flatten(l):
                        return list(itertools.chain.from_iterable(l))

                    # DFS traversal of superclass hierarchy, removing
                    # intermediate node that are just there to merge parent
                    # nodes without adding anything else. This avoids having
                    # intermediate classes created by __and__ for example, for
                    # better error reporting.
                    def expand(superclass):
                        # If that typeclass is an empty shim that just combines other typeclasses
                        if not (superclass.__dict__.keys() -
                                _EmptyTypeClass.__dict__.keys()):
                            return flatten(map(expand,
                                               superclass.SUPERCLASSES))
                        else:
                            return [superclass]

                    superclasses = flatten(map(expand, superclasses))
                    superclasses = deduplicate(superclasses, keep_last=False)

                    def format_method(name):
                        return '{} (defined in: {} and {})'.format(
                            name,
                            dct['REQUIRED'][name].__qualname__,
                            typeclass.REQUIRED[name].__qualname__,
                        )

                    raise TypeError(
                        'Cannot merge typeclasses {} since the following methods conflict: {}'
                        .format(
                            ', '.join(
                                sorted(tp.__qualname__
                                       for tp in superclasses)),
                            ', '.join(map(format_method, sorted(conflicting))),
                        ))
                else:
                    dct['DEFAULTS'].update(typeclass.DEFAULTS)
                    dct['REQUIRED'].update(typeclass.REQUIRED)

            typeclass = super().__new__(cls, name, bases, dct, *args, **kwargs)

            typeclass.REQUIRED.update({
                name: typeclass
                for name, attr in dct.items()
                if getattr(attr, '__required__', False)
            })

            not_copied = set(dict(inspect.getmembers(_EmptyTypeClass)).keys())
            not_copied |= dct['REQUIRED'].keys() | {'__qualname__', '__name__'}
            typeclass.DEFAULTS.update({
                attr: val
                for attr, val in dct.items() if attr not in not_copied
            })
            return typeclass
        # Someone tries to inherit from the typeclass to make an instance
        else:
            if len(bases) != 1:
                raise TypeError(
                    'A typeclass instance can only implement the methods of one typeclass, but multiple typeclasses were provided: {}'
                    .format(', '.join(
                        sorted(base.__qualname__ for base in bases))))

            missing = typeclass.REQUIRED.keys() - dct.keys()
            if missing:
                raise NotImplementedError(
                    'Following methods are missing in {} instance and must be defined for instances of the {} typeclass: {}'
                    .format(
                        name,
                        typeclass.__name__,
                        ', '.join(sorted(missing)),
                    ))

            # Merge-in the typeclass default implementations before using it,
            # so each instance contains all the methods of the typeclass
            dct = {**typeclass.DEFAULTS, **dct}

            types = types if isinstance(types, Iterable) else [types]
            for type_ in types:
                # Create an instance for each type, with the type as base class.
                bases = (type_, )
                try:
                    instance = type(name, bases, dct, *args, **kwargs)
                # Some classes like bool cannot be subclassed. Work around by
                # listing all their attributes and making a new class that has
                # all of them.
                except TypeError:
                    total_dct = {**dict(inspect.getmembers(type_)), **dct}
                    instance = type(name, tuple(), total_dct, *args, **kwargs)

                typeclass.INSTANCES[type_] = (instance, dct)

                # Monkey patch the types so that the typeclass methods can be
                # called "natively" on them if wanted
                get_top_package = lambda mod: mod.split('.')[0]

                # Only add the attribute if it does not exist already on the
                # target class
                def update_attr(attr, val):
                    if not hasattr(type_, attr):
                        setattr(type_, attr, val)

                # If the instance is declared in the same top-level package,
                # update the type itself. This prevents foreign packages from
                # monkey patching types but allows instances anywhere in a
                # given package
                if get_top_package(type_.__module__) == dct['__module__']:
                    # Then the attributes defined in the instance
                    for attr, val in dct.items():
                        update_attr(attr, val)

            # We scavanged all what we needed, the class has just been used to
            # as a vehicle to create a scope but will not be used directly. It
            # will still live a secrete life internally for casting though.
            #
            # Instead, return a class that is equivalent to the typeclass but
            # with the docstring of the instance. This allows Sphinx to pick up
            # the instance's docstring.
            dct = {**dct, **{'__doc__': dct.get('__doc__')}}
            return type(name, (typeclass, ), dct)