Reputation: 13
I am using Python 3.9 and Click to build a small command line interface utility, but I am getting strange errors, specifically when I attempt to call one function decorated as a @click.command()
from another function that is also decorated the same way.
I have distilled my program down to the bare minimum to explain what I mean.
This is my program
import click
@click.group()
def cli():
pass
@click.command()
@click.argument('id')
def outer(id):
print('outer id',id,type(id))
inner(id) # run inner() from within outer(), pass argument unchanged
@click.command() # *
@click.argument('id') # *
def inner(id):
print('inner id',id,type(id))
cli.add_command(outer)
cli.add_command(inner) # *
if __name__ == '__main__':
cli()
This is the CLI result when I run my script using both the inner
and outer
commands:
% python3 test.py inner 123
inner id 123 <class 'str'>
% python3 test.py outer 123
outer id 123 <class 'str'>
Usage: test.py [OPTIONS] ID
Try 'test.py --help' for help.
Error: Got unexpected extra arguments (2 3)
%
Interestingly, it works when I use a single character argument:
% python3 test.py outer 1
outer id 1 <class 'str'>
inner id 1 <class 'str'>
%
If I comment out the three lines marked with # *
, running the outer
command behaves as expected, passing on the id
argument to inner()
without changes or issues, no matter the length of the argument:
% python3 test.py outer 123
outer id 123 <class 'str'>
inner id 123 <class 'str'>
%
Clearly, the decorators are somehow messing with the arguments. Can anybody explain why this is, and how I can achieve the desired behavior of passing on the arguments unchanged? Maybe I am missing something super obvious?
Thanks in advance!
Upvotes: 1
Views: 816
Reputation: 781129
Use the context operations to invoke other commands
import click
@click.group()
def cli():
pass
@click.command()
@click.argument('id')
@click.pass_context
def outer(ctx, id):
print('outer id',id,type(id))
ctx.forward(inner) # run inner(), pass argument unchanged
ctx.invoke(inner, id=id+1) # run inner(), pass modified argument
@click.command() # *
@click.argument('id') # *
def inner(id):
print('inner id',id,type(id))
cli.add_command(outer)
cli.add_command(inner) # *
if __name__ == '__main__':
cli()
Upvotes: 2