예제 #1
0
파일: tools.py 프로젝트: qiime2/q2cli
def peek(path):
    import qiime2.sdk
    from q2cli.core.config import CONFIG

    metadata = qiime2.sdk.Result.peek(path)

    click.echo(CONFIG.cfg_style('type', "UUID")+":        ", nl=False)
    click.echo(metadata.uuid)
    click.echo(CONFIG.cfg_style('type', "Type")+":        ", nl=False)
    click.echo(metadata.type)
    if metadata.format is not None:
        click.echo(CONFIG.cfg_style('type', "Data format")+": ", nl=False)
        click.echo(metadata.format)
예제 #2
0
    def parse_args(self, ctx, args):
        from q2cli.core.config import CONFIG
        if isinstance(self, click.MultiCommand):
            return super().parse_args(ctx, args)

        errors = []
        parser = self.make_parser(ctx)
        skip_rest = False
        for _ in range(10):  # surely this is enough attempts
            try:
                opts, args, param_order = parser.parse_args(args=args)
                break
            except click.ClickException as e:
                errors.append(e)
                skip_rest = True

        if not skip_rest:
            for param in click.core.iter_params_for_processing(
                    param_order, self.get_params(ctx)):
                try:
                    value, args = param.handle_parse_result(ctx, opts, args)
                except click.ClickException as e:
                    errors.append(e)

            if args and not ctx.allow_extra_args and not ctx.resilient_parsing:
                errors.append(
                    click.UsageError('Got unexpected extra argument%s (%s)' %
                                     (len(args) != 1 and 's' or '', ' '.join(
                                         map(click.core.make_str, args)))))
        if errors:
            click.echo(ctx.get_help() + "\n", err=True)
            if len(errors) > 1:
                problems = 'There were some problems with the command:'
            else:
                problems = 'There was a problem with the command:'
            click.echo(CONFIG.cfg_style('problem', problems.center(78, ' ')),
                       err=True)
            for idx, e in enumerate(errors, 1):
                msg = click.formatting.wrap_text(
                    e.format_message(),
                    initial_indent=' (%d/%d%s) ' %
                    (idx, len(errors), '?' if skip_rest else ''),
                    subsequent_indent='  ')
                click.echo(CONFIG.cfg_style('error', msg), err=True)
            ctx.exit(1)

        ctx.args = args
        return args
예제 #3
0
파일: util.py 프로젝트: qiime2/q2cli
def exit_with_error(e,
                    header='An error has been encountered:',
                    traceback='stderr',
                    status=1):
    import sys
    import traceback as tb
    import textwrap
    import click
    from q2cli.core.config import CONFIG

    footer = []  # footer only exists if traceback is set
    tb_file = None
    if traceback == 'stderr':
        tb_file = sys.stderr
        footer = ['See above for debug info.']
    elif traceback is not None:
        tb_file = traceback
        footer = ['Debug info has been saved to %s' % tb_file.name]

    error = textwrap.indent(str(e), '  ')
    segments = [header, error] + footer

    if traceback is not None:
        tb.print_exception(type(e), e, e.__traceback__, file=tb_file)

        tb_file.write('\n')

    click.echo(CONFIG.cfg_style('error', '\n\n'.join(segments)), err=True)

    if not footer:
        click.echo(err=True)  # extra newline to look normal

    click.get_current_context().exit(status)
예제 #4
0
파일: tools.py 프로젝트: danteese/q2cli
def validate(path, level):
    import qiime2.sdk
    from q2cli.core.config import CONFIG

    try:
        result = qiime2.sdk.Result.load(path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 Result:' % path
        q2cli.util.exit_with_error(e, header=header)

    try:
        result.validate(level)
    except qiime2.plugin.ValidationError as e:
        header = 'Result %s does not appear to be valid at level=%s:' % (path,
                                                                         level)
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    except Exception as e:
        header = ('An unexpected error has occurred while attempting to '
                  'validate result %s:' % path)
        q2cli.util.exit_with_error(e, header=header)
    else:
        click.echo(
            CONFIG.cfg_style(
                'success', f'Result {path} appears to be '
                f'valid at level={level}.'))
예제 #5
0
파일: tools.py 프로젝트: qiime2/q2cli
def view(visualization_path, index_extension):
    # Guard headless envs from having to import anything large
    import sys
    from q2cli.core.config import CONFIG
    if not os.getenv("DISPLAY") and sys.platform != "darwin":
        raise click.UsageError(
            'Visualization viewing is currently not supported in headless '
            'environments. You can view Visualizations (and Artifacts) at '
            'https://view.qiime2.org, or move the Visualization to an '
            'environment with a display and view it with `qiime tools view`.')

    import zipfile
    import qiime2.sdk

    if index_extension.startswith('.'):
        index_extension = index_extension[1:]
    try:
        visualization = qiime2.sdk.Visualization.load(visualization_path)
    # TODO: currently a KeyError is raised if a zipped file that is not a
    # QIIME 2 result is passed. This should be handled better by the framework.
    except (zipfile.BadZipFile, KeyError, TypeError):
        raise click.BadParameter(
            '%s is not a QIIME 2 Visualization. Only QIIME 2 Visualizations '
            'can be viewed.' % visualization_path)

    index_paths = visualization.get_index_paths(relative=False)

    if index_extension not in index_paths:
        raise click.BadParameter(
            'No index %s file is present in the archive. Available index '
            'extensions are: %s' % (index_extension,
                                    ', '.join(index_paths.keys())))
    else:
        index_path = index_paths[index_extension]
        launch_status = click.launch(index_path)
        if launch_status != 0:
            click.echo(CONFIG.cfg_style('error', 'Viewing visualization '
                                        'failed while attempting to open '
                                        f'{index_path}'), err=True)
        else:
            while True:
                click.echo(
                    "Press the 'q' key, Control-C, or Control-D to quit. This "
                    "view may no longer be accessible or work correctly after "
                    "quitting.", nl=False)
                # There is currently a bug in click.getchar where translation
                # of Control-C and Control-D into KeyboardInterrupt and
                # EOFError (respectively) does not work on Python 3. The code
                # here should continue to work as expected when the bug is
                # fixed in Click.
                #
                # https://github.com/pallets/click/issues/583
                try:
                    char = click.getchar()
                    click.echo()
                    if char in {'q', '\x03', '\x04'}:
                        break
                except (KeyboardInterrupt, EOFError):
                    break
예제 #6
0
def export_default_theme(output_path):
    import configparser
    from q2cli.core.config import CONFIG

    parser = configparser.ConfigParser()
    parser.read_dict(CONFIG.get_default_styles())
    with open(output_path, 'w') as fh:
        parser.write(fh)
예제 #7
0
def import_theme(theme):
    import os
    import shutil
    from configparser import Error

    import q2cli.util
    from q2cli.core.config import CONFIG

    try:
        CONFIG.parse_file(theme)
    except Error as e:
        # If they tried to change [error] in a valid manner before we hit our
        # parsing error, we don't want to use their imported error settings
        CONFIG.styles = CONFIG.get_default_styles()
        header = 'Something went wrong while parsing your theme: '
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    shutil.copy(theme,
                os.path.join(q2cli.util.get_app_dir(), 'cli-colors.theme'))
예제 #8
0
    def format_options(self, ctx, formatter, COL_MAX=23, COL_MIN=10):
        from q2cli.core.config import CONFIG
        # write options
        opt_groups = {}
        records = []
        for group, options in self.get_opt_groups(ctx).items():
            opt_records = []
            for o in options:
                record = o.get_help_record(ctx)
                if record is None:
                    continue
                opt_records.append((o, record))
                records.append(record)
            opt_groups[group] = opt_records
        first_columns = (r[0] for r in records)
        border = min(
            COL_MAX,
            max(COL_MIN,
                *(len(col) for col in first_columns if len(col) < COL_MAX)))

        for opt_group, opt_records in opt_groups.items():
            if not opt_records:
                continue
            formatter.write_heading(click.style(opt_group, bold=True))
            formatter.indent()
            padded_border = border + formatter.current_indent
            for opt, record in opt_records:
                self.write_option(ctx, formatter, opt, record, padded_border)
            formatter.dedent()

        # Modified from original:
        # https://github.com/pallets/click/blob
        # /c6042bf2607c5be22b1efef2e42a94ffd281434c/click/core.py#L1056
        # Copyright (c) 2014 by the Pallets team.
        commands = []
        for subcommand in self.list_commands(ctx):
            cmd = self.get_command(ctx, subcommand)
            # What is this, the tool lied about a command.  Ignore it
            if cmd is None:
                continue
            if cmd.hidden:
                continue

            commands.append((subcommand, cmd))

        # allow for 3 times the default spacing
        if len(commands):
            limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands)

            rows = []
            for subcommand, cmd in commands:
                help = cmd.get_short_help_str(limit)
                rows.append((CONFIG.cfg_style('command', subcommand), help))

            if rows:
                with formatter.section(click.style('Commands', bold=True)):
                    formatter.write_dl(rows)
예제 #9
0
파일: commands.py 프로젝트: qiime2/q2cli
    def __init__(self, *args, **kwargs):
        import re
        import sys

        unicodes = ["\u2018", "\u2019", "\u201C", "\u201D", "\u2014", "\u2013"]
        category_regex = re.compile(r'--m-(\S+)-category')

        invalid_chars = []
        categories = []
        for command in sys.argv:
            if any(x in command for x in unicodes):
                invalid_chars.append(command)

            match = category_regex.fullmatch(command)
            if match is not None:
                param_name, = match.groups()
                # Maps old-style option name to new name.
                categories.append((command, '--m-%s-column' % param_name))

        if invalid_chars or categories:
            if invalid_chars:
                msg = ("Error: Detected invalid character in: %s\nVerify the "
                       "correct quotes or dashes (ASCII) are being used." %
                       ', '.join(invalid_chars))
                click.echo(CONFIG.cfg_style('error', msg), err=True)
            if categories:
                old_to_new_names = '\n'.join('Instead of %s, trying using %s' %
                                             (old, new)
                                             for old, new in categories)
                msg = ("Error: The following options no longer exist because "
                       "metadata *categories* are now called metadata "
                       "*columns* in QIIME 2.\n\n%s" % old_to_new_names)
                click.echo(CONFIG.cfg_style('error', msg), err=True)
            sys.exit(-1)

        super().__init__(*args, **kwargs)

        # Plugin state for current deployment that will be loaded from cache.
        # Used to construct the dynamic CLI.
        self._plugins = None
예제 #10
0
파일: tools.py 프로젝트: qiime2/q2cli
def extract(input_path, output_path):
    import zipfile
    import qiime2.sdk
    from q2cli.core.config import CONFIG

    try:
        extracted_dir = qiime2.sdk.Result.extract(input_path, output_path)
    except (zipfile.BadZipFile, ValueError):
        raise click.BadParameter(
            '%s is not a valid QIIME 2 Result. Only QIIME 2 Artifacts and '
            'Visualizations can be extracted.' % input_path)
    else:
        success = 'Extracted %s to directory %s' % (input_path, extracted_dir)
        click.echo(CONFIG.cfg_style('success', success))
예제 #11
0
파일: tools.py 프로젝트: qiime2/q2cli
def export_data(input_path, output_path, output_format):
    import qiime2.util
    import qiime2.sdk
    import distutils
    from q2cli.core.config import CONFIG
    result = qiime2.sdk.Result.load(input_path)
    if output_format is None:
        if isinstance(result, qiime2.sdk.Artifact):
            output_format = result.format.__name__
        else:
            output_format = 'Visualization'
        result.export_data(output_path)
    else:
        if isinstance(result, qiime2.sdk.Visualization):
            error = '--output-format cannot be used with visualizations'
            click.echo(CONFIG.cfg_style('error', error), err=True)
            click.get_current_context().exit(1)
        else:
            source = result.view(qiime2.sdk.parse_format(output_format))
            if os.path.isfile(str(source)):
                if os.path.isfile(output_path):
                    os.remove(output_path)
                elif os.path.dirname(output_path) == '':
                    # This allows the user to pass a filename as a path if they
                    # want their output in the current working directory
                    output_path = os.path.join('.', output_path)
                if os.path.dirname(output_path) != '':
                    # create directory (recursively) if it doesn't exist yet
                    os.makedirs(os.path.dirname(output_path), exist_ok=True)
                qiime2.util.duplicate(str(source), output_path)
            else:
                distutils.dir_util.copy_tree(str(source), output_path)

    output_type = 'file' if os.path.isfile(output_path) else 'directory'
    success = 'Exported %s as %s to %s %s' % (input_path, output_format,
                                              output_type, output_path)
    click.echo(CONFIG.cfg_style('success', success))
예제 #12
0
    def _color_important(self, tokens, ctx):
        import re
        from q2cli.core.config import CONFIG

        for t in tokens:
            if '_' in t:
                names = self.get_option_names(ctx)
                if re.sub(r'[^\w]', '', t) in names:
                    m = re.search(r'(\w+)', t)
                    word = t[m.start():m.end()]
                    word = CONFIG.cfg_style('emphasis', word.replace('_', '-'))
                    token = t[:m.start()] + word + t[m.end():]
                    yield token
                    continue
            yield t
예제 #13
0
파일: dev.py 프로젝트: qiime2/q2cli
def assert_result_data(input_path, zip_data_path, expression):
    import re
    import q2cli.util
    import qiime2.sdk
    from q2cli.core.config import CONFIG

    q2cli.util.get_plugin_manager()

    try:
        result = qiime2.sdk.Result.load(input_path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 result:' % \
                input_path
        q2cli.util.exit_with_error(e, header=header)

    try:
        hits = sorted(result._archiver.data_dir.glob(zip_data_path))
        if len(hits) != 1:
            data_dir = result._archiver.data_dir
            all_fps = sorted(data_dir.glob('**/*'))
            all_fps = [x.relative_to(data_dir).name for x in all_fps]
            raise ValueError('Value provided for zip_data_path (%s) did not '
                             'produce exactly one match.\nMatches: %s\n'
                             'Paths observed: %s' %
                             (zip_data_path, hits, all_fps))
    except Exception as e:
        header = 'There was a problem locating the zip_data_path (%s)' % \
                zip_data_path
        q2cli.util.exit_with_error(e, header=header)

    try:
        target = hits[0].read_text()
        match = re.search(expression, target, flags=re.MULTILINE)
        if match is None:
            raise AssertionError('Expression %r not found in %s.' %
                                 (expression, hits[0]))
    except Exception as e:
        header = 'There was a problem finding the expression.'
        q2cli.util.exit_with_error(e, header=header)

    msg = '"%s" was found in %s' % (str(expression), str(zip_data_path))
    click.echo(CONFIG.cfg_style('success', msg))
예제 #14
0
파일: tools.py 프로젝트: qiime2/q2cli
def citations(path):
    import qiime2.sdk
    import io
    from q2cli.core.config import CONFIG
    ctx = click.get_current_context()

    try:
        result = qiime2.sdk.Result.load(path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 result:' % path
        q2cli.util.exit_with_error(e, header=header)

    if result.citations:
        with io.StringIO() as fh:
            result.citations.save(fh)
            click.echo(fh.getvalue(), nl=False)
        ctx.exit(0)
    else:
        click.echo(CONFIG.cfg_style('problem', 'No citations found.'),
                   err=True)
        ctx.exit(1)
예제 #15
0
파일: tools.py 프로젝트: qiime2/q2cli
def import_data(type, input_path, output_path, input_format):
    import qiime2.sdk
    import qiime2.plugin
    from q2cli.core.config import CONFIG
    try:
        artifact = qiime2.sdk.Artifact.import_data(type, input_path,
                                                   view_type=input_format)
    except qiime2.plugin.ValidationError as e:
        header = 'There was a problem importing %s:' % input_path
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    except Exception as e:
        header = 'An unexpected error has occurred:'
        q2cli.util.exit_with_error(e, header=header)
    artifact.save(output_path)
    if input_format is None:
        input_format = artifact.format.__name__

    success = 'Imported %s as %s to %s' % (input_path,
                                           input_format,
                                           output_path)
    click.echo(CONFIG.cfg_style('success', success))
예제 #16
0
파일: commands.py 프로젝트: qiime2/q2cli
    def get_command(self, ctx, name):
        try:
            action = self._action_lookup[name]
        except KeyError:
            from q2cli.util import get_close_matches

            possibilities = get_close_matches(name, self._action_lookup)
            if len(possibilities) == 1:
                hint = '  Did you mean %r?' % possibilities[0]
            elif possibilities:
                hint = '  (Possible commands: %s)' % ', '.join(possibilities)
            else:
                hint = ''

            click.echo(CONFIG.cfg_style(
                'error', "Error: QIIME 2 plugin %r has no "
                "action %r." % (self._plugin['name'], name) + hint),
                       err=True)
            ctx.exit(2)  # Match exit code of `return None`

        return ActionCommand(name, self._plugin, action)
예제 #17
0
파일: util.py 프로젝트: qiime2/q2cli
    def callback(ctx, param, value):
        if not value or ctx.resilient_parsing:
            return

        records = get_citation_records()
        if records:
            import io
            import qiime2.sdk

            citations = qiime2.sdk.Citations([('key%d' % i, r)
                                              for i, r in enumerate(records)])
            with io.StringIO() as fh:
                fh.write('% use `qiime tools citations` on a QIIME 2 result'
                         ' for complete list\n\n')
                citations.save(fh)
                click.echo(fh.getvalue(), nl=False)
            ctx.exit()
        else:
            click.echo(CONFIG.cfg_style('problem', 'No citations found.'),
                       err=True)
            ctx.exit(1)
예제 #18
0
파일: commands.py 프로젝트: qiime2/q2cli
    def __init__(self, name, plugin, action):
        import q2cli.util
        import q2cli.click.type

        self.plugin = plugin
        self.action = action

        self._inputs, self._params, self._outputs = \
            self._build_generated_options()

        self._misc = [
            click.Option(['--output-dir'],
                         type=q2cli.click.type.OutDirType(),
                         help='Output unspecified results to a directory'),
            click.Option(['--verbose / --quiet'],
                         default=None,
                         required=False,
                         help='Display verbose output to stdout and/or stderr '
                         'during execution of this action. Or silence '
                         'output if execution is successful (silence is '
                         'golden).'),
            q2cli.util.example_data_option(self._get_plugin,
                                           self.action['id']),
            q2cli.util.citations_option(self._get_citation_records)
        ]

        options = [*self._inputs, *self._params, *self._outputs, *self._misc]
        help_ = [action['description']]
        if self.action['deprecated']:
            help_.append(
                CONFIG.cfg_style(
                    'warning',
                    'WARNING:\n\nThis command is deprecated and will '
                    'be removed in a future version of this plugin.'))
        super().__init__(name,
                         params=options,
                         callback=self,
                         short_help=action['name'],
                         help='\n\n'.join(help_))
예제 #19
0
파일: commands.py 프로젝트: qiime2/q2cli
    def get_command(self, ctx, name):
        if name in self._builtin_commands:
            return self._builtin_commands[name]

        try:
            plugin = self._plugin_lookup[name]
        except KeyError:
            from q2cli.util import get_close_matches

            possibilities = get_close_matches(name, self._plugin_lookup)
            if len(possibilities) == 1:
                hint = '  Did you mean %r?' % possibilities[0]
            elif possibilities:
                hint = '  (Possible commands: %s)' % ', '.join(possibilities)
            else:
                hint = ''

            click.echo(CONFIG.cfg_style(
                'error', "Error: QIIME 2 has no "
                "plugin/command named %r." % name + hint),
                       err=True)
            ctx.exit(2)  # Match exit code of `return None`

        return PluginCommand(plugin, name)
예제 #20
0
파일: dev.py 프로젝트: qiime2/q2cli
def assert_result_type(input_path, qiime_type):
    import q2cli.util
    import qiime2.sdk
    from q2cli.core.config import CONFIG

    q2cli.util.get_plugin_manager()
    try:
        result = qiime2.sdk.Result.load(input_path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 Result:' % \
            input_path
        q2cli.util.exit_with_error(e, header=header)

    if str(result.type) != qiime_type:
        try:
            msg = 'Expected %s, observed %s' % (qiime_type, result.type)
            raise AssertionError(msg)
        except Exception as e:
            header = 'There was a problem asserting the type:'
            q2cli.util.exit_with_error(e, header=header)
    else:
        msg = 'The input file (%s) type and the expected type (%s)' \
              ' match' % (input_path, qiime_type)
        click.echo(CONFIG.cfg_style('success', msg))
예제 #21
0
    def write_option(self, ctx, formatter, opt, record, border, COL_SPACING=2):
        import itertools
        from q2cli.core.config import CONFIG
        full_width = formatter.width - formatter.current_indent
        indent_text = ' ' * formatter.current_indent
        opt_text, help_text = record
        opt_text_secondary = None
        if type(opt_text) is tuple:
            opt_text, opt_text_secondary = opt_text
        help_text, requirements = self._clean_help(help_text)
        type_placement = None
        type_repr = None
        type_indent = 2 * indent_text

        if hasattr(opt.type, 'get_type_repr'):
            type_repr = opt.type.get_type_repr(opt)
            if type_repr is not None:
                if len(type_repr) <= border - len(type_indent):
                    type_placement = 'under'
                else:
                    type_placement = 'beside'

        if len(opt_text) > border:
            lines = simple_wrap(opt_text, full_width)
        else:
            lines = [opt_text.split(' ')]
        if opt_text_secondary is not None:
            lines.append(opt_text_secondary.split(' '))

        to_write = []
        for tokens in lines:
            dangling_edge = formatter.current_indent
            styled = []
            for token in tokens:
                dangling_edge += len(token) + 1
                if token.startswith('--'):
                    token = CONFIG.cfg_style('option',
                                             token,
                                             required=opt.required)
                styled.append(token)
            line = indent_text + ' '.join(styled)
            to_write.append(line)
        formatter.write('\n'.join(to_write))
        dangling_edge -= 1

        if type_placement == 'beside':
            lines = simple_wrap(type_repr,
                                formatter.width - len(type_indent),
                                start_col=dangling_edge - 1)
            to_write = []
            first_iter = True
            for tokens in lines:
                line = ' '.join(tokens)
                if first_iter:
                    dangling_edge += 1 + len(line)
                    line = " " + CONFIG.cfg_style('type', line)
                    first_iter = False
                else:
                    dangling_edge = len(type_indent) + len(line)
                    line = type_indent + CONFIG.cfg_style('type', line)
                to_write.append(line)
            formatter.write('\n'.join(to_write))

        if dangling_edge + 1 > border + COL_SPACING:
            formatter.write('\n')
            left_col = []
        else:
            padding = ' ' * (border + COL_SPACING - dangling_edge)
            formatter.write(padding)
            dangling_edge += len(padding)
            left_col = ['']  # jagged start

        if type_placement == 'under':
            padding = ' ' * (border + COL_SPACING - len(type_repr) -
                             len(type_indent))
            line = ''.join(
                [type_indent,
                 CONFIG.cfg_style('type', type_repr), padding])
            left_col.append(line)

        if hasattr(opt, 'meta_help') and opt.meta_help is not None:
            meta_help = simple_wrap(opt.meta_help,
                                    border - len(type_indent) - 1)
            for idx, line in enumerate([' '.join(t) for t in meta_help]):
                if idx == 0:
                    line = type_indent + '(' + line
                else:
                    line = type_indent + ' ' + line
                if idx == len(meta_help) - 1:
                    line += ')'
                line += ' ' * (border - len(line) + COL_SPACING)
                left_col.append(line)

        right_col = simple_wrap(help_text,
                                formatter.width - border - COL_SPACING)
        right_col = [
            ' '.join(self._color_important(tokens, ctx))
            for tokens in right_col
        ]

        to_write = []
        for left, right in itertools.zip_longest(left_col,
                                                 right_col,
                                                 fillvalue=' ' *
                                                 (border + COL_SPACING)):
            to_write.append(left)
            if right.strip():
                to_write[-1] += right

        formatter.write('\n'.join(to_write))

        if requirements is None:
            formatter.write('\n')
        else:
            if to_write:
                if len(to_write) > 1 or ((not left_col) or left_col[0] != ''):
                    dangling_edge = 0
                dangling_edge += click.formatting.term_len(to_write[-1])
            else:
                pass  # dangling_edge is still correct

            if dangling_edge + 1 + len(requirements) > formatter.width:
                formatter.write('\n')
                pad = formatter.width - len(requirements)
            else:
                pad = formatter.width - len(requirements) - dangling_edge

            formatter.write((' ' * pad) +
                            CONFIG.cfg_style('default_arg', requirements) +
                            '\n')
예제 #22
0
 def format_usage(self, ctx, formatter):
     from q2cli.core.config import CONFIG
     """Writes the usage line into the formatter."""
     pieces = self.collect_usage_pieces(ctx)
     formatter.write_usage(CONFIG.cfg_style('command', ctx.command_path),
                           ' '.join(pieces))
예제 #23
0
파일: commands.py 프로젝트: qiime2/q2cli
    def __call__(self, **kwargs):
        """Called when user hits return, **kwargs are Dict[click_names, Obj]"""
        import os
        import qiime2.util

        output_dir = kwargs.pop('output_dir')
        verbose = kwargs.pop('verbose')
        if verbose is None:
            verbose = False
            quiet = False
        elif verbose:
            quiet = False
        else:
            quiet = True

        arguments = {}
        init_outputs = {}
        for key, value in kwargs.items():
            prefix, *parts = key.split('_')
            key = '_'.join(parts)

            if prefix == 'o':
                if value is None:
                    value = os.path.join(output_dir, key)
                init_outputs[key] = value
            elif prefix == 'm':
                arguments[key[:-len('_file')]] = value
            else:
                arguments[key] = value

        outputs = self._order_outputs(init_outputs)
        action = self._get_action()
        # `qiime2.util.redirected_stdio` defaults to stdout/stderr when
        # supplied `None`.
        log = None

        if not verbose:
            import tempfile
            log = tempfile.NamedTemporaryFile(prefix='qiime2-q2cli-err-',
                                              suffix='.log',
                                              delete=False,
                                              mode='w')
        if action.deprecated:
            # We don't need to worry about redirecting this, since it should a)
            # always be shown to the user and b) the framework-originated
            # FutureWarning will wind up in the log file in quiet mode.

            msg = ('Plugin warning from %s:\n\n%s is deprecated and '
                   'will be removed in a future version of this plugin.' %
                   (q2cli.util.to_cli_name(self.plugin['name']), self.name))
            click.echo(CONFIG.cfg_style('warning', msg))

        cleanup_logfile = False
        try:
            with qiime2.util.redirected_stdio(stdout=log, stderr=log):
                results = action(**arguments)
        except Exception as e:
            header = ('Plugin error from %s:' %
                      q2cli.util.to_cli_name(self.plugin['name']))
            if verbose:
                # log is not a file
                log = 'stderr'
            q2cli.util.exit_with_error(e, header=header, traceback=log)
        else:
            cleanup_logfile = True
        finally:
            # OS X will reap temporary files that haven't been touched in
            # 36 hours, double check that the log is still on the filesystem
            # before trying to delete. Otherwise this will fail and the
            # output won't be written.
            if log and cleanup_logfile and os.path.exists(log.name):
                log.close()
                os.remove(log.name)

        if output_dir is not None:
            os.makedirs(output_dir)

        for result, output in zip(results, outputs):
            path = result.save(output)
            if not quiet:
                click.echo(
                    CONFIG.cfg_style('success',
                                     'Saved %s to: %s' % (result.type, path)))