From 2e759b5827fc9431e7305da0dc2d8744256cd34d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 20 Apr 2017 22:10:52 -0300 Subject: [PATCH 1/2] Pass arguments to the default command --- CHANGELOG.rst | 10 ++++++++++ requirements.txt | 1 + todoman/cli.py | 43 ++++++++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0d5d4b9d..f6a9bac0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,9 +7,19 @@ releases, in reverse chronological order. v3.2.0 ------ +New features +~~~~~~~~~~~~ + +* Allow passing arguments to the shortcut ``todo``, so ``todo --startable`` now + works as expected. * Completing recurring todos now works as expected and does not make if dissapear forever. +Packaging changes +~~~~~~~~~~~~~~~~~ + +* New runtime dependency: ``click-default-group``. + v3.1.0 ------ diff --git a/requirements.txt b/requirements.txt index c5fd4ad1..11e1504a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ atomicwrites click>=6.0 click_log>=0.1.3 +click-default-group configobj humanize icalendar diff --git a/todoman/cli.py b/todoman/cli.py index 9c588cf2..94a3c57d 100644 --- a/todoman/cli.py +++ b/todoman/cli.py @@ -8,6 +8,7 @@ import click import click_log +from click_default_group import DefaultGroup from todoman import exceptions, formatters from todoman.configuration import ConfigurationException, load_config @@ -119,12 +120,6 @@ def _sort_callback(ctx, param, val): def validate_status(ctx=None, param=None, val=None): - # The default command doesn't run callbacks as expected, so it needs to - # specify the callback'd type. When `list` is called explicitly, this - # callback *IS* run, so we need to handle that edge case: - if not isinstance(val, str): - return val - statuses = val.upper().split(',') if 'ANY' in statuses: @@ -162,10 +157,17 @@ def command_wrap(*a, **kw): class AppContext: def __init__(self): - self.config = None self.db = None self.formatter_class = None + self.init_config() + + def init_config(self): + try: + self.config = load_config() + except ConfigurationException as e: + raise click.ClickException(e.args[0]) + @cached_property def ui_formatter(self): return formatters.DefaultFormatter( @@ -191,7 +193,20 @@ def formatter(self): help='Go into interactive mode before saving the task.') -@click.group(invoke_without_command=True) +class ConfigurableDefaultGroup(DefaultGroup): + + def get_command(self, ctx, cmd_name): + if cmd_name not in self.commands: + ctx = ctx.ensure_object(AppContext) + cmd_name = ctx.config['main']['default_command'] + return super(DefaultGroup, self).get_command(ctx, cmd_name) + + +@click.group( + cls=ConfigurableDefaultGroup, + default='default', # click crashes in this is None + default_if_no_args=True, +) @click_log.init('todoman') @click_log.simple_verbosity_option() @click.option('--colour', '--color', default=None, @@ -209,10 +224,6 @@ def formatter(self): @catch_errors def cli(click_ctx, color, porcelain, humanize): ctx = click_ctx.ensure_object(AppContext) - try: - ctx.config = load_config() - except ConfigurationException as e: - raise click.ClickException(e.args[0]) if porcelain and humanize: raise click.ClickException('--porcelain and --humanize cannot be used' @@ -246,12 +257,6 @@ def cli(click_ctx, color, porcelain, humanize): # Make python actually use LC_TIME, or the user's locale settings locale.setlocale(locale.LC_TIME, "") - if not click_ctx.invoked_subcommand: - invoke_command( - click_ctx, - ctx.config['main']['default_command'], - ) - def invoke_command(click_ctx, command): name, *args = command.split(' ') @@ -483,7 +488,7 @@ def move(ctx, list, ids): callback=_validate_startable_param, help='Show only todos which ' 'should can be started today (i.e.: start time is not in the ' 'future).') -@click.option('--status', '-s', default=['NEEDS-ACTION', 'IN-PROCESS'], +@click.option('--status', '-s', default='NEEDS-ACTION,IN-PROCESS', callback=validate_status, help='Show only todos with the ' 'provided comma-separated statuses. Valid statuses are ' '"NEEDS-ACTION", "CANCELLED", "COMPLETED", "IN-PROCESS" or "ANY"' From 1dd1679836df187a33060d88108fe72b56cd5f10 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 1 May 2017 19:20:18 -0300 Subject: [PATCH 2/2] wip --- todoman/cli.py | 82 +++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/todoman/cli.py b/todoman/cli.py index 94a3c57d..b55d5b83 100644 --- a/todoman/cli.py +++ b/todoman/cli.py @@ -202,11 +202,47 @@ def get_command(self, ctx, cmd_name): return super(DefaultGroup, self).get_command(ctx, cmd_name) -@click.group( - cls=ConfigurableDefaultGroup, - default='default', # click crashes in this is None - default_if_no_args=True, -) +class Cli(ConfigurableDefaultGroup): + + def __init__(self, *a, **kw): + super().__init__(*a, **kw, default='default', default_if_no_args=True) + + def callback(click_ctx, color, porcelain, humanize): + ctx = click_ctx.ensure_object(AppContext) + + if porcelain and humanize: + raise click.ClickException('--porcelain and --humanize cannot be used' + ' at the same time.') + + if humanize is None: # False means explicitly disabled + humanize = ctx.config['main']['humanize'] + + if humanize: + ctx.formatter_class = formatters.HumanizedFormatter + elif porcelain: + ctx.formatter_class = formatters.PorcelainFormatter + else: + ctx.formatter_class = formatters.DefaultFormatter + + color = color or ctx.config['main']['color'] + if color == 'always': + click_ctx.color = True + elif color == 'never': + click_ctx.color = False + + paths = [ + path for path in glob.iglob(expanduser(ctx.config["main"]["path"])) + if isdir(path) + ] + if len(paths) == 0: + raise exceptions.NoListsFound(ctx.config["main"]["path"]) + + ctx.db = Database(paths, ctx.config['main']['cache_path']) + + # Make python actually use LC_TIME, or the user's locale settings + locale.setlocale(locale.LC_TIME, "") + + @click_log.init('todoman') @click_log.simple_verbosity_option() @click.option('--colour', '--color', default=None, @@ -222,40 +258,10 @@ def get_command(self, ctx, cmd_name): @click.pass_context @click.version_option(prog_name='todoman') @catch_errors -def cli(click_ctx, color, porcelain, humanize): - ctx = click_ctx.ensure_object(AppContext) - - if porcelain and humanize: - raise click.ClickException('--porcelain and --humanize cannot be used' - ' at the same time.') - - if humanize is None: # False means explicitly disabled - humanize = ctx.config['main']['humanize'] - - if humanize: - ctx.formatter_class = formatters.HumanizedFormatter - elif porcelain: - ctx.formatter_class = formatters.PorcelainFormatter - else: - ctx.formatter_class = formatters.DefaultFormatter - - color = color or ctx.config['main']['color'] - if color == 'always': - click_ctx.color = True - elif color == 'never': - click_ctx.color = False - - paths = [ - path for path in glob.iglob(expanduser(ctx.config["main"]["path"])) - if isdir(path) - ] - if len(paths) == 0: - raise exceptions.NoListsFound(ctx.config["main"]["path"]) - - ctx.db = Database(paths, ctx.config['main']['cache_path']) +def get_cli(): + return Cli() - # Make python actually use LC_TIME, or the user's locale settings - locale.setlocale(locale.LC_TIME, "") +cli = get_cli() def invoke_command(click_ctx, command):