27

I would like to add unspecified options to the cli command using python-click library. So my cli function could look like the following

$ my-cmd --option1 value1 --options2 value2 --unknown_var value3

My current code:

import click

@click.option('--option1')
@click.option('--option2')
@click.command(name='my-cmd')
def cli(option1, option2):
  click.echo("my test")

I would like to see something like the following:

import click

@click.option('--option1')
@click.option('--option2')
@click.command(name='my-cmd')
def cli(option1, option2, **kwargs):
  click.echo("my test")
  # Manually manage **kwargs
aitorhh
  • 2,331
  • 1
  • 23
  • 35
  • 2
    If you are willing to accept `my-cmd --option1 value1 -- --unknown_var value3 --another-unknown-var value 4` then you can use [Option Like Arguments](http://click.pocoo.org/5/arguments/#option-like-arguments) and [Variadic Arguments](http://click.pocoo.org/5/arguments/#variadic-arguments) together. – RubenLaguna Oct 05 '15 at 09:10

1 Answers1

50

You can pass context with ignore_unknown_options and allow_extra_args settings, the extra arguments will be collected in context.args list (['--unknown_var', 'value3', '--unknown_var2', 'value4']). Then you can transform it to dict.

import click

@click.command(name='my-cmd', context_settings=dict(
    ignore_unknown_options=True,
    allow_extra_args=True,
))
@click.option('--option1')
@click.option('--option2')
@click.pass_context
def cli(ctx, option1, option2):
    click.echo({ctx.args[i][2:]: ctx.args[i+1] for i in range(0, len(ctx.args), 2)})

example

python cli.py --option1 value1 --option2 value2 --unknown_var value3 --unknown_var2 value4
>> {'unknown_var2': 'value4', 'unknown_var': 'value3'}

See Forwarding Unknown Options.

SkunkSpinner
  • 11,429
  • 7
  • 40
  • 53
r-m-n
  • 14,192
  • 4
  • 69
  • 68
  • In addition, if you want to support args (starting with -) and options (starting with --) you might want to use something like `{(ctx.args[i][2:] if str(ctx.args[i]).startswith("--") else ctx.args[i][1:]): ctx.args[i+1] for i in range(0, len(ctx.args), 2)}` – omni Oct 28 '20 at 15:32
  • 1
    I would use a stripping and splitting with a list comprehension. This looks way cleaner: `data = dict([item.strip('--').split('=') for item in ctx.args])` – tupui Dec 09 '20 at 11:57
  • Make sure you account for boolean flags `params = {k.lstrip('--'): ctx.args[i + 1] if not (i + 1 >= len(ctx.args) or ctx.args[i + 1].startswith('--')) else True for i, k in enumerate(ctx.args) if k.startswith('--')}` (ugly one line so it fits in a comment) – Fraser Langton Nov 25 '22 at 02:17