So here's my issue:
Assume that I have built a CLI using Python Click for which I have created custom classes of groups and commands that wrap invoking to catch exceptions:
logger = logging.getLogger()
class CLICommandInvoker(click.Command):
def invoke(self, ctx):
command = ctx.command.name
try:
logger.info("Running {command} command".format(command=command))
ret = super(CLICommandInvoker, self).invoke(ctx)
logger.info("Completed {command} command".format(command=command))
return ret
except Exception as exc:
logger.error(
'Command {command} failed with exception: {exc}'.format(command=command, exc=exc)
)
""" In case command invoked from another command """
raise Exception("Failed to invoke {command} command".format(command=command))
class CLIGroupInvoker(click.Group):
def invoke(self, ctx):
group = ctx.command.name
try:
ret = super(CLIGroupInvoker, self).invoke(ctx)
group_subcommand = ctx.invoked_subcommand
logger.info(
'Command "{group}-{subcommand}" completed successfully'.format(group = group, subcommand = group_subcommand)
)
return ret
except Exception:
group_subcommand = ctx.invoked_subcommand
logger.error(
'Command "{group}-{subcommand}" failed'.format(group=group, subcommand=group_subcommand)
)
Now, for example I have two commands in a certain group:
@click.group(cls=CLIGroupInvoker)
def g():
pass
@g.command(cls=CLICommandInvoker)
def c1():
print("C1")
@g.command(cls=CLICommandInvoker)
@click.pass_context
def c2(ctx):
ctx.invoke(c1)
print("C2")
So, the code runs fine, but the invoke method of the context in c2 does not run the custom invoke in my CLICommandInvoker, but goes straight to the c1 function instead. I don't see the Running c1 command or other logs that are in the custom invoke regarding c1, only those about c2.
So, what am I doing wrong here? How can I have the command invocation use the custom class when invoking commands from another command? Or is that not possible?
I know there is a solution to simply refactor the code to extract the implementation itself and simply have the commands "wrap" the actual logic, but let's say that for the moment it's not possible.