Ejemplo n.º 1
0
 def test_result_type(self):
     """
     Test that the expressions return the right value type.
     """
     for computable_expr, expr_val_list in self.execute():
         for expr_val in expr_val_list:
             TestResult.fail_if(
                 not isinstance(expr_val.value, Final),
                 'Wrong value type: expected {} but got {}'.format(
                     utils.get_name(Final),
                     utils.get_name(type(expr_val.value))),
                 [computable_expr])
Ejemplo n.º 2
0
 def handle_cycle(path):
     error('Cyclic dependency detected: {path}'.format(
         path=' -> '.join(
             utils.get_name(callable_)
             for callable_ in path
         )
     ))
Ejemplo n.º 3
0
 def handle_non_produced(cls_name, consumer_name, param_name, callable_path):
     info('Nothing can produce instances of {cls} needed for {consumer} (parameter "{param}", along path {path})'.format(
         cls=cls_name,
         consumer=consumer_name,
         param=param_name,
         path=' -> '.join(utils.get_name(callable_) for callable_ in callable_path)
     ))
Ejemplo n.º 4
0
    def test_excep(self):
        for computable_expr, expr_val_list in self.execute(check_excep=False):
            for expr_val in expr_val_list:
                failed_parent_list = expr_val.get_excep()
                TestResult.fail_if(not failed_parent_list,
                                   'No exception detected', [computable_expr])

                for failed_froz_val in failed_parent_list:
                    excep = failed_froz_val.excep
                    TestResult.fail_if(
                        not isinstance(excep, ValueError),
                        'Wrong exception type: expected {} but got {}'.format(
                            utils.get_name(ValueError),
                            repr(excep),
                        ), [computable_expr])
Ejemplo n.º 5
0
 def format_result(self, expr_val):
     """
     Format an :class:`exekall.engine.ExprVal` that is the result of an
     expression. It should return a (short) string that will be displayed at
     the end of the computation.
     """
     val = expr_val.value
     if val is NoValue or val is None:
         for failed_parent in expr_val.get_excep():
             excep = failed_parent.excep
             return 'EXCEPTION ({type}): {msg}'.format(type=get_name(
                 type(excep), full_qual=False),
                                                       msg=excep)
         return 'No value computed'
     else:
         return str(val)
Ejemplo n.º 6
0
def build_patch_map(sweep_spec_list, param_spec_list, op_set):
    sweep_spec_list = copy.copy(sweep_spec_list)
    # Make a sweep spec from a simpler param spec
    sweep_spec_list.extend(
        (callable_pattern, param, value, value, 1)
        for callable_pattern, param, value in param_spec_list)

    patch_map = dict()
    for callable_pattern, param, start, stop, step in sweep_spec_list:
        for op in op_set:
            callable_ = op.callable_
            callable_name = utils.get_name(callable_, full_qual=True)
            if not utils.match_name(callable_name, [callable_pattern]):
                continue
            patch_map.setdefault(op, dict()).setdefault(param, []).extend(
                utils.sweep_param(callable_, param, start, stop, step))
    return patch_map
Ejemplo n.º 7
0
    def from_expr_data(cls, data: ExprData, consumer: Consumer) -> 'ExekallArtifactPath':
        """
        Factory used when running under `exekall`
        """
        artifact_dir = Path(data['expr_artifact_dir']).resolve()
        consumer_name = get_name(consumer)

        # Find a non-used directory
        for i in itertools.count(1):
            artifact_dir_ = Path(artifact_dir, consumer_name, str(i))
            if not artifact_dir_.exists():
                artifact_dir = artifact_dir_
                break

        cls.get_logger().info(f'Creating {consumer_name} artifact storage: {artifact_dir}')
        artifact_dir.mkdir(parents=True)
        # Get canonical absolute paths
        artifact_dir = artifact_dir.resolve()
        root = data['artifact_dir']
        relative = artifact_dir.relative_to(root)
        return cls(root, relative)
Ejemplo n.º 8
0
def exec_expr_list(iteration_expr_list, adaptor, artifact_dir,
                   testsession_uuid, hidden_callable_set,
                   only_template_scripts, adaptor_cls, verbose, save_db,
                   use_pdb):

    if not only_template_scripts:
        with (artifact_dir / 'UUID').open('wt') as f:
            f.write(testsession_uuid + '\n')

        (artifact_dir / 'BY_UUID').mkdir()

    out('\nArtifacts dir: {}\n'.format(artifact_dir))

    for expr in utils.flatten_seq(iteration_expr_list):
        expr_short_id = expr.get_id(
            hidden_callable_set=hidden_callable_set,
            with_tags=False,
            full_qual=False,
            qual=False,
        )

        data = expr.data
        data['id'] = expr_short_id
        data['uuid'] = expr.uuid

        expr_artifact_dir = pathlib.Path(artifact_dir, expr_short_id,
                                         expr.uuid)
        expr_artifact_dir.mkdir(parents=True)
        expr_artifact_dir = expr_artifact_dir.resolve()
        data['artifact_dir'] = artifact_dir
        data['expr_artifact_dir'] = expr_artifact_dir

        with (expr_artifact_dir / 'UUID').open('wt') as f:
            f.write(expr.uuid + '\n')

        with (expr_artifact_dir / 'ID').open('wt') as f:
            f.write(expr_short_id + '\n')

        with (expr_artifact_dir / 'STRUCTURE').open('wt') as f:
            f.write(
                expr.get_id(
                    hidden_callable_set=hidden_callable_set,
                    with_tags=False,
                    full_qual=True,
                ) + '\n\n')
            f.write(expr.format_structure() + '\n')

        is_svg, dot_output = utils.render_graphviz(expr)
        graphviz_path = expr_artifact_dir / 'STRUCTURE.{}'.format(
            'svg' if is_svg else 'dot')
        with graphviz_path.open('wt', encoding='utf-8') as f:
            f.write(dot_output)

        with (expr_artifact_dir / 'EXPRESSION_TEMPLATE.py').open(
                'wt', encoding='utf-8') as f:
            f.write(
                expr.get_script(
                    prefix='expr',
                    db_path=os.path.join('..', utils.DB_FILENAME),
                    db_relative_to='__file__',
                )[1] + '\n', )

    if only_template_scripts:
        return 0

    # Preserve the execution order, so the summary is displayed in the same
    # order
    result_map = collections.OrderedDict()
    for i, expr_list in enumerate(iteration_expr_list):
        i += 1
        info('Iteration #{}\n'.format(i))

        for expr in expr_list:
            exec_start_msg = 'Executing: {short_id}\n\nID: {full_id}\nArtifacts: {folder}\nUUID: {uuid_}'.format(
                short_id=expr.get_id(
                    hidden_callable_set=hidden_callable_set,
                    full_qual=False,
                    qual=False,
                ),
                full_id=expr.get_id(
                    hidden_callable_set=hidden_callable_set
                    if not verbose else None,
                    full_qual=True,
                ),
                folder=expr.data['expr_artifact_dir'],
                uuid_=expr.uuid).replace('\n', '\n# ')

            delim = '#' * (len(exec_start_msg.splitlines()[0]) + 2)
            out(delim + '\n# ' + exec_start_msg + '\n' + delim)

            result_list = list()
            result_map[expr] = result_list

            def pre_line():
                out('-' * 40)

            # Make sure that all the output of the expression is flushed to ensure
            # there won't be any buffered stderr output being displayed after the
            # "official" end of the Expression's execution.

            def flush_std_streams():
                sys.stdout.flush()
                sys.stderr.flush()

            def get_uuid_str(expr_val):
                return 'UUID={}'.format(expr_val.uuid)

            computed_expr_val_set = set()
            reused_expr_val_set = set()

            def log_expr_val(expr_val, reused):
                # Consider that PrebuiltOperator reuse values instead of
                # actually computing them.
                if isinstance(expr_val.expr.op, engine.PrebuiltOperator):
                    reused = True

                if reused:
                    msg = 'Reusing already computed {id} {uuid}'
                    reused_expr_val_set.add(expr_val)
                else:
                    msg = 'Computed {id} {uuid}'
                    computed_expr_val_set.add(expr_val)

                op = expr_val.expr.op
                if (op.callable_ not in hidden_callable_set and
                        not issubclass(op.value_type, engine.ForcedParamType)):
                    log_f = info
                else:
                    log_f = debug

                log_f(
                    msg.format(
                        id=expr_val.get_id(
                            full_qual=False,
                            with_tags=True,
                            hidden_callable_set=hidden_callable_set,
                        ),
                        uuid=get_uuid_str(expr_val),
                    ))

                # Drop into the debugger if we got an exception
                excep = expr_val.excep
                if use_pdb and excep is not NoValue:
                    error(utils.format_exception(excep))
                    pdb.post_mortem(excep.__traceback__)

            def get_duration_str(expr_val):
                if expr_val.duration is None:
                    duration = ''
                else:
                    duration = '{:.2f}s'.format(expr_val.duration)

                cumulative = expr_val.cumulative_duration
                cumulative = ' (cumulative: {:.2f}s)'.format(
                    cumulative) if cumulative else ''

                return '{}{}'.format(duration, cumulative)

            # This returns an iterator
            executor = expr.execute(log_expr_val)

            out('')
            for result in utils.iterate_cb(executor, pre_line,
                                           flush_std_streams):
                for excep_val in result.get_excep():
                    excep = excep_val.excep
                    tb = utils.format_exception(excep)
                    error(
                        '{e_name}: {e}\nID: {id}\n{tb}'.format(
                            id=excep_val.get_id(),
                            e_name=utils.get_name(type(excep)),
                            e=excep,
                            tb=tb,
                        ), )

                prefix = 'Finished {uuid} in {duration} '.format(
                    uuid=get_uuid_str(result),
                    duration=get_duration_str(result),
                )
                out('{prefix}{id}'.format(
                    id=result.get_id(
                        full_qual=False,
                        qual=False,
                        mark_excep=True,
                        with_tags=True,
                        hidden_callable_set=hidden_callable_set,
                    ).strip().replace('\n', '\n' + len(prefix) * ' '),
                    prefix=prefix,
                ))

                out(adaptor.format_result(result))
                result_list.append(result)

            out('')
            expr_artifact_dir = expr.data['expr_artifact_dir']

            # Finalize the computation
            adaptor.finalize_expr(expr)

            # Dump the reproducer script
            with (expr_artifact_dir / 'EXPRESSION.py').open(
                    'wt', encoding='utf-8') as f:
                f.write(
                    expr.get_script(
                        prefix='expr',
                        db_path=os.path.join('..', '..', utils.DB_FILENAME),
                        db_relative_to='__file__',
                    )[1] + '\n', )

            def format_uuid(expr_val_list):
                uuid_list = sorted(
                    {expr_val.uuid
                     for expr_val in expr_val_list})
                return '\n'.join(uuid_list)

            def write_uuid(path, *args):
                with path.open('wt') as f:
                    f.write(format_uuid(*args) + '\n')

            write_uuid(expr_artifact_dir / 'VALUES_UUID', result_list)
            write_uuid(expr_artifact_dir / 'REUSED_VALUES_UUID',
                       reused_expr_val_set)
            write_uuid(expr_artifact_dir / 'COMPUTED_VALUES_UUID',
                       computed_expr_val_set)

            # From there, use a relative path for symlinks
            expr_artifact_dir = pathlib.Path(
                '..', expr_artifact_dir.relative_to(artifact_dir))
            computed_uuid_set = {
                expr_val.uuid
                for expr_val in computed_expr_val_set
            }
            computed_uuid_set.add(expr.uuid)
            for uuid_ in computed_uuid_set:
                (artifact_dir / 'BY_UUID' /
                 uuid_).symlink_to(expr_artifact_dir)

    if save_db:
        db = engine.ValueDB(
            engine.FrozenExprValSeq.from_expr_list(
                utils.flatten_seq(iteration_expr_list),
                hidden_callable_set=hidden_callable_set,
            ),
            adaptor_cls=adaptor_cls,
        )

        db_path = artifact_dir / utils.DB_FILENAME
        db.to_path(db_path)
        relative_db_path = db_path.relative_to(artifact_dir)
    else:
        relative_db_path = None
        db = None

    out('#' * 80)
    info('Artifacts dir: {}'.format(artifact_dir))
    info('Result summary:')

    # Display the results summary
    summary = adaptor.get_summary(result_map)
    out(summary)
    with (artifact_dir / 'SUMMARY').open('wt', encoding='utf-8') as f:
        f.write(summary + '\n')

    # Output the merged script with all subscripts
    script_path = artifact_dir / 'ALL_SCRIPTS.py'
    result_name_map, all_scripts = engine.Expression.get_all_script(
        utils.flatten_seq(iteration_expr_list),
        prefix='expr',
        db_path=relative_db_path,
        db_relative_to='__file__',
        db=db,
        adaptor_cls=adaptor_cls,
    )

    with script_path.open('wt', encoding='utf-8') as f:
        f.write(all_scripts + '\n')

    return adaptor.get_run_exit_code(result_map)
Ejemplo n.º 9
0
    def show_db(self, db):
        parse_uuid_attr = self._parse_uuid_attr

        def indent(s):
            idt = ' ' * 4
            return idt + s.replace('\n', '\n' + idt)

        def get_uuid(uuid):
            try:
                froz_val = db.get_by_uuid(uuid)
            except KeyError:
                raise KeyError('UUID={} not found in the database'.format(uuid))
            else:
                return froz_val

        def get_attr_key(obj, attr_key):
            # parse "attr[key1][key2][...]"
            attr = attr_key.split('[', 1)[0]
            keys = re.findall(r'\[(.*?)\]', attr_key)
            if attr:
                obj = getattr(obj, attr)
            return get_nested_key(obj, keys)

        def resolve_attr(obj, attr_key):
            if attr_key is None:
                return obj

            try:
                attr_key, remainder = attr_key.split('.', 1)
            except ValueError:
                return get_attr_key(obj, attr_key)
            else:
                obj = get_attr_key(obj, attr_key)
                return resolve_attr(obj, remainder)

        args = self.args
        if not (args.show or args.show_yaml):
            super().show_db(db)

        attr_map = {}
        for uuid, attr in args.show:
            attr_map.setdefault(uuid, set()).add(attr)

        if len(args.show) == 1:
            show_format = '{val}'
        else:
            show_format = 'UUID={uuid} {type}{attr}{eq}{val}'

        serialize_spec_list = args.serialize
        yaml_show_spec_list = args.show_yaml

        for uuid, attr_set in attr_map.items():
            attr_list = sorted(attr_set)
            froz_val = get_uuid(uuid)
            value = froz_val.value
            for attr in attr_list:
                attr_value = resolve_attr(value, attr)

                attr_str = str(attr_value)
                if '\n' in attr_str:
                    attr_str = '\n' + indent(attr_str)
                    eq = ':'
                else:
                    eq = '='

                print(show_format.format(
                    uuid=froz_val.uuid,
                    type=get_name(type(value)),
                    attr='.' + attr if attr else '',
                    val=attr_str,
                    eq=eq,
                ))


        if len(yaml_show_spec_list) == 1:
            yaml_show_format = '{yaml}'
            yaml_indent = lambda x: x
        else:
            yaml_show_format = 'UUID={uuid} {type}:\n\n{yaml}'
            yaml_indent = indent

        for uuid, attr in yaml_show_spec_list:
            froz_val = get_uuid(uuid)
            value = froz_val.value
            value = resolve_attr(value, attr)

            if isinstance(value, Serializable):
                yaml_str = value.to_yaml()
            else:
                yaml_str = Serializable._to_yaml(value)

            print(yaml_show_format.format(
                uuid=uuid,
                type=get_name(type(value)),
                yaml=yaml_indent(yaml_str),
            ))

        for uuid_attr, path in serialize_spec_list:
            uuid, attr = parse_uuid_attr(uuid_attr)
            froz_val = get_uuid(uuid)
            value = froz_val.value
            value = resolve_attr(value, attr)

            if isinstance(value, Serializable):
                value.to_path(path)
            else:
                Serializable._to_path(value, path, fmt='yaml')

        return 0