29

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
1
  • 3
    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 and Variadic Arguments together. Commented Oct 5, 2015 at 9:10

1 Answer 1

55

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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)}
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])
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)

Your Answer

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.