1

I have a command line interface build with click which implements multiple commands.

Now I want to pass unspecified named options into one command which is here named command1 e.g. the number of options and their names should be able to vary flexibly.

import click
@click.group(chain=True)
@click.pass_context
def cli(ctx, **kwargs):
 return True
@cli.command()
@click.option('--command1-option1', type=str)
@click.option('--command1-option2', type=str)
@click.pass_context
def command1(ctx, **kwargs):
 """Add command1."""
 ctx.obj['command1_args'] = {}
 for k, v in kwargs.items():
 ctx.obj['command1_args'][k] = v
 return True
@cli.command()
@click.argument('command2-argument1', type=str)
@click.pass_context
def command2(ctx, **kwargs):
 """Add command2."""
 print(ctx.obj)
 print(kwargs)
 return True
if __name__ == '__main__':
 cli(obj={})

I have already looked into forwarding unknown options like in this question but the problem is that I have to call (chanin) other commands after the first one which have to be asserted e.g. this call has to work but with arbitrary options for command1:

$python cli.py command1 --command1-option1 foo --command1-option2 bar command2 'hello'

So how can I add unspecified named options to a single command and call (chain) another one at the same time (after it)?

Stephen Rauch
50.1k32 gold badges119 silver badges143 bronze badges
asked Jan 7, 2019 at 11:42
0

1 Answer 1

1

The custom class found here, can be adapted to your case.

Using the Custom Class:

To use the custom class, just use the cls parameter to the click.command() decorator like:

@cli.command(cls=AcceptAllCommand)
@click.pass_context
def command1(ctx, **kwargs):
 """Add command1."""
 ...

Test Code:

import click
class AcceptAllCommand(click.Command):
 def make_parser(self, ctx):
 """Hook 'make_parser' and allow the opt dict to find any option"""
 parser = super(AcceptAllCommand, self).make_parser(ctx)
 command = self
 class AcceptAllDict(dict):
 def __contains__(self, item):
 """If the parser does no know this option, add it"""
 if not super(AcceptAllDict, self).__contains__(item):
 # create an option name
 name = item.lstrip('-')
 # add the option to our command
 click.option(item)(command)
 # get the option instance from the command
 option = command.params[-1]
 # add the option instance to the parser
 parser.add_option(
 [item], name.replace('-', '_'), obj=option)
 return True
 # set the parser options to our dict
 parser._short_opt = AcceptAllDict(parser._short_opt)
 parser._long_opt = AcceptAllDict(parser._long_opt)
 return parser
@click.group(chain=True)
@click.pass_context
def cli(ctx, **kwargs):
 """"""
@cli.command(cls=AcceptAllCommand)
@click.pass_context
def command1(ctx, **kwargs):
 """Add command1."""
 ctx.obj['command1_args'] = {}
 for k, v in kwargs.items():
 ctx.obj['command1_args'][k] = v
@cli.command()
@click.argument('command2-argument1', type=str)
@click.pass_context
def command2(ctx, **kwargs):
 """Add command2."""
 print(ctx.obj)
 print(kwargs)
if __name__ == "__main__":
 commands = (
 "command1 --cmd1-opt1 foo --cmd1-opt2 bar command2 hello",
 '--help',
 )
 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(), obj={})
 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)]
-----------
> command1 --cmd1-opt1 foo --cmd1-opt2 bar command2 hello
{'command1_args': {'cmd1_opt1': 'foo', 'cmd1_opt2': 'bar'}}
{'command2_argument1': 'hello'}
-----------
> --help
Usage: test.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
Options:
 --help Show this message and exit.
Commands:
 command1 Add command1.
 command2 Add command2.
answered Jan 7, 2019 at 15:53
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect. Thanks a lot!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.