I have a case where I'd like to run a common function (check_upgrade()
) for most of my click commands, but there are a few cases where I don't want to run it. Rather than relying on developers to remember to add a decorator or function call to explicitly run this check, I'd prefer to instead run it by default and then have a decorator that one can add (e.g. @bypass_upgrade_check
) for commands where check_upgrade()
should not run.
I was hoping for something like:
class State(object):
def __init__(self):
self.bypass_upgrade_check = False
pass_state = click.make_pass_decorator(State, ensure=True)
def bypass_upgrade_check(func):
@pass_state
def wrapper(state, *args, **kwargs):
state.bypass_upgrade_check = True
func(*args, **kwargs)
return wrapper
@click.group()
@pass_state
def common(state):
if not state.bypass_upgrade_check:
check_upgrade()
@common.command()
def cmd1():
# check_upgrade() runs here
pass
@bypass_upgrade_check
@common.command()
def cmd2():
# don't run check_upgrade() here
pass
But this doesn't work. It doesn't actually ever call the bypass_upgrade_check()
function.
Is there a way to decorate a command in such a way that I can modify the state before the group code runs? Or another method altogether that accomplishes this?
To keep track of which commands bypass the upgrade check, I suggest that in the bypass marking decorator you store that state on the click.Command
object. Then if you pass the click.Context
to your group, you can then look at the command object to see if it is marked to allow skipping upgrade like:
Code:
def bypass_upgrade_check(func):
setattr(func, 'do_upgrade_check', False)
@click.group()
@click.pass_context
def cli(ctx):
sub_cmd = ctx.command.commands[ctx.invoked_subcommand]
if getattr(sub_cmd, 'do_upgrade_check', True):
check_upgrade()
Test Code:
import click
def check_upgrade():
click.echo('Checking Upgrade!')
def bypass_upgrade_check(func):
setattr(func, 'do_upgrade_check', False)
@click.group()
@click.pass_context
def cli(ctx):
sub_cmd = ctx.command.commands[ctx.invoked_subcommand]
if getattr(sub_cmd, 'do_upgrade_check', True):
check_upgrade()
@cli.command()
def cmd1():
# check_upgrade() runs here
click.echo('cmd1')
@bypass_upgrade_check
@cli.command()
def cmd2():
# don't run check_upgrade() here
click.echo('cmd2')
if __name__ == "__main__":
commands = (
'cmd1',
'cmd2',
)
import sys, time
time.sleep(1)
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
for cmd in commands:
try:
time.sleep(0.1)
print('-----------')
print('> ' + cmd)
time.sleep(0.1)
cli(cmd.split())
except BaseException as exc:
if str(exc) != '0' and \
not isinstance(exc, (click.ClickException, SystemExit)):
raise
Results:
Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> cmd1
Checking Upgrade!
cmd1
-----------
> cmd2
cmd2