From 0c7a803a196cae5ce4bcf68039eded3f8b7860c7 Mon Sep 17 00:00:00 2001 From: David Alfonso Date: Wed, 15 May 2019 19:23:21 +0200 Subject: [PATCH] Make output format options mutually exclusive - Add support in MutuallyExclusiveOption for options with an internal name different from the ones permitted in the command line. - New hidden option --plain to handle the default output format in report, aggregate and log commands. --- watson/cli.py | 101 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/watson/cli.py b/watson/cli.py index 08cfe180..2310217f 100644 --- a/watson/cli.py +++ b/watson/cli.py @@ -39,19 +39,26 @@ def __init__(self, *args, **kwargs): super(MutuallyExclusiveOption, self).__init__(*args, **kwargs) def handle_parse_result(self, ctx, opts, args): - if self.mutually_exclusive.intersection(opts) and self.name in opts: - raise click.UsageError( - '`--{name}` is mutually exclusive with the following options: ' - '{options}'.format(name=self.name.replace('_', ''), - options=', ' - .join(['`--{}`'.format(_) for _ in - self.mutually_exclusive])) - ) - + if self.name in opts: + if self.mutually_exclusive.intersection(opts): + self._raise_exclusive_error() + if self.multiple and len(set(opts[self.name])) > 1: + self._raise_exclusive_error() return super(MutuallyExclusiveOption, self).handle_parse_result( ctx, opts, args ) + def _raise_exclusive_error(self): + # Use self.opts[-1] instead of self.name to handle options with a + # different internal name. + self.mutually_exclusive.add(self.opts[-1].strip('-')) + raise click.UsageError( + 'The following options are mutually exclusive: ' + '{options}'.format(options=', ' + .join(['`--{}`'.format(_) for _ in + self.mutually_exclusive])) + ) + class WatsonCliError(click.ClickException): def format_message(self): @@ -439,15 +446,23 @@ def status(watson, project, tags, elapsed): help="Reports activity only for frames containing the given " "tag. You can add several tags by using this option multiple " "times") -@click.option('-j', '--json', 'format_json', is_flag=True, - help="Format the report in JSON instead of plain text") -@click.option('-s', '--csv', 'format_csv', is_flag=True, - help="Format the report in JSON instead of plain text") +@click.option('-j', '--json', 'output_format', cls=MutuallyExclusiveOption, + flag_value='json', mutually_exclusive=['csv'], + multiple=True, + help="Format output in JSON instead of plain text") +@click.option('-s', '--csv', 'output_format', cls=MutuallyExclusiveOption, + flag_value='csv', mutually_exclusive=['json'], + multiple=True, + help="Format output in JSON instead of plain text") +@click.option('--plain', 'output_format', cls=MutuallyExclusiveOption, + flag_value='plain', mutually_exclusive=['json', 'csv'], + multiple=True, default=True, hidden=True, + help="Format output in plain text (default)") @click.option('-g/-G', '--pager/--no-pager', 'pager', default=None, help="(Don't) view output through a pager.") @click.pass_obj def report(watson, current, from_, to, projects, tags, year, month, - week, day, luna, all, format_json, format_csv, pager, + week, day, luna, all, output_format, pager, aggregated=False): """ Display a report of the time spent on each project. @@ -558,13 +573,13 @@ def report(watson, current, from_, to, projects, tags, year, month, except watson.WatsonError as e: raise click.ClickException(e) - if format_json and not aggregated: + if 'json' in output_format and not aggregated: click.echo(json.dumps(report, indent=4, sort_keys=True)) return - elif format_csv and not aggregated: + elif 'csv' in output_format and not aggregated: click.echo(build_csv(flatten_report(report))) return - elif (format_json or format_csv) and aggregated: + elif 'plain' not in output_format and aggregated: return report lines = [] @@ -676,16 +691,24 @@ def _final_print(lines): help="Reports activity only for frames containing the given " "tag. You can add several tags by using this option multiple " "times") -@click.option('-j', '--json', 'format_json', is_flag=True, - help="Format the report in JSON instead of plain text") -@click.option('-s', '--csv', 'format_csv', is_flag=True, - help="Format the report in JSON instead of plain text") +@click.option('-j', '--json', 'output_format', cls=MutuallyExclusiveOption, + flag_value='json', mutually_exclusive=['csv'], + multiple=True, + help="Format output in JSON instead of plain text") +@click.option('-s', '--csv', 'output_format', cls=MutuallyExclusiveOption, + flag_value='csv', mutually_exclusive=['json'], + multiple=True, + help="Format output in JSON instead of plain text") +@click.option('--plain', 'output_format', cls=MutuallyExclusiveOption, + flag_value='plain', mutually_exclusive=['json', 'csv'], + multiple=True, default=True, hidden=True, + help="Format output in plain text (default)") @click.option('-g/-G', '--pager/--no-pager', 'pager', default=None, help="(Don't) view output through a pager.") @click.pass_obj @click.pass_context -def aggregate(ctx, watson, current, from_, to, projects, tags, - format_json, format_csv, pager): +def aggregate(ctx, watson, current, from_, to, projects, tags, output_format, + pager): """ Display a report of the time spent on each project aggregated by day. @@ -746,12 +769,12 @@ def aggregate(ctx, watson, current, from_, to, projects, tags, from_offset = from_ + offset output = ctx.invoke(report, current=current, from_=from_offset, to=from_offset, projects=projects, tags=tags, - format_json=format_json, format_csv=format_csv, + output_format=output_format, pager=pager, aggregated=True) - if format_json: + if 'json' in output_format: lines.append(output) - elif format_csv: + elif 'csv' in output_format: lines.extend(flatten_report(output)) else: # if there is no activity for the day, append a newline @@ -761,9 +784,9 @@ def aggregate(ctx, watson, current, from_, to, projects, tags, lines.append(u'\n'.join(output)) - if format_json: + if 'json' in output_format: click.echo(json.dumps(lines, indent=4, sort_keys=True)) - elif format_csv: + elif 'csv' in output_format: click.echo(build_csv(lines)) elif pager or (pager is None and watson.config.getboolean('options', 'pager', True)): @@ -813,15 +836,23 @@ def aggregate(ctx, watson, current, from_, to, projects, tags, help="Logs activity only for frames containing the given " "tag. You can add several tags by using this option multiple " "times") -@click.option('-j', '--json', 'format_json', is_flag=True, - help="Format the log in JSON instead of plain text") -@click.option('-s', '--csv', 'format_csv', is_flag=True, - help="Format the log in JSON instead of plain text") +@click.option('-j', '--json', 'output_format', cls=MutuallyExclusiveOption, + flag_value='json', mutually_exclusive=['csv'], + multiple=True, + help="Format output in JSON instead of plain text") +@click.option('-s', '--csv', 'output_format', cls=MutuallyExclusiveOption, + flag_value='csv', mutually_exclusive=['json'], + multiple=True, + help="Format output in JSON instead of plain text") +@click.option('--plain', 'output_format', cls=MutuallyExclusiveOption, + flag_value='plain', mutually_exclusive=['json', 'csv'], + multiple=True, default=True, hidden=True, + help="Format output in plain text (default)") @click.option('-g/-G', '--pager/--no-pager', 'pager', default=None, help="(Don't) view output through a pager.") @click.pass_obj def log(watson, current, from_, to, projects, tags, year, month, week, day, - luna, all, format_json, format_csv, pager): + luna, all, output_format, pager): """ Display each recorded session during the given timespan. @@ -891,11 +922,11 @@ def log(watson, current, from_, to, projects, tags, year, month, week, day, projects=projects or None, tags=tags or None, span=span ) - if format_json: + if 'json' in output_format: click.echo(frames_to_json(filtered_frames)) return - if format_csv: + if 'csv' in output_format: click.echo(frames_to_csv(filtered_frames)) return