0

Say I have a config file cfg.yaml

# cfg.yaml
opt1: "Bailey is the cutest doggo ever!!!"
opt2: 42

I would like to dynamically build a cli interface without knowing ahead of time what items are in cfg.yaml. Where the values in the cfg.yaml are the default values of the options. Something like:

python cli.py config.yaml run --help

Usage: cli.py run [OPTIONS]

Options:
  --opt1 TEXT
  --opt2 INTEGER

Then I could run:

python cli.py config.yaml run --opt1 my_str --opt2 314159
Joshua Patterson
  • 439
  • 5
  • 12

1 Answers1

0

Ok, it ain't pretty, but I have a hack to keep moving.

With some help from here regarding the programmatic building of the cli and good old sys.argv :-)

import click
import yaml
import sys

cfg_path = sys.argv[1]
if cfg_path.endswith('.yaml'):
    with open(cfg_path, 'r') as f:
        cfg = yaml.load(f, Loader=yaml.SafeLoader)
    sys.argv.remove(cfg_path)
else:
    cfg = {}

def options_from_pipeline_def(cfg):
    def decorator(f):
        for k,v in cfg.items():
            param_decls = [
                f'--{k}',
            ]

            attrs = dict(
                required=False,
                default=v,
                type=type(v)
                )
            click.option(*param_decls, **attrs)(f)
        return f
    return decorator 


@click.group()
def cli():
    pass

@cli.command("run")
@options_from_pipeline_def(cfg)
def run(**kwargs):
    print(f"kwargs: {kwargs}")


if __name__ == '__main__':
    cli()

Examples

$ python build-cli.py --help
---

Usage: build-cli.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  run

########################################

$ python build-cli.py cfg.yaml run --help
---

Usage: build-cli.py run [OPTIONS]

Options:
  --opt2 INTEGER
  --opt1 TEXT
  --help          Show this message and exit.

########################################

$ python z-build-cli.py cfg.yaml run
---

kwargs: {'opt2': 42, 'opt1': 'Bailey is the cutest doggo ever!!!'}

########################################

$ python build-cli.py cfg.yaml run --opt1 "I cannot stress how cute Bailey is!" --opt2 314159
---

kwargs: {'opt1': 'I cannot stress how cute Bailey is!', 'opt2': 314159}

Joshua Patterson
  • 439
  • 5
  • 12