Multiple context objects in CLI (Click)

5.2k Views Asked by At

I'm trying to implement a command line app using the Flask CLI infrastructure based on Click. Its interface should work like this:

app.py -c config.cfg cmd_a
app.py -c config.cfg cmd_b

I have the following code:

@click.group
@click.option('-c', 'config')
@click.pass_context
def cli(ctx, config):
  ctx.obj = ObjA(config)
  ctx.obj = ObjB(config)  # Just for illustration

@cli.command()
@click.pass_context()
def cmd_a(ctx):
  ctx.find_object(ObjA)

@cli.command()
@cli.pass_context()
def cmd_b(ctx):
  ctx.find_object(ObjB)

cli()

The problem is, I need to create two different objects based on the -c flag and make them both available to the underlying commands, which seems impossible. Is there any workaround for this?

I know I could use the meta property of the Context object, but that would mean writing a lot of boilerplate.

1

There are 1 best solutions below

1
On BEST ANSWER

find_object will search object in parent context by type. If you want to use multiple objects the easiest way is to make obj a dictionary. Here example:

@click.group
@click.option('-c', 'config')
@click.pass_context
def cli(ctx, config):
    if ctx.obj is None:
        ctx.obj = dict()
    # add any data to obj here...
    ctx.obj['a'] = 'example_A'
    ctx.obj['b'] = 'example_B'

@cli.command()
@click.pass_context()
def cmd_a(ctx):
  # check prepared data
  print(ctx.obj['a'])

@cli.command()
@cli.pass_context()
def cmd_b(ctx):
  # check prepared data
  print(ctx.obj['b'])

Slightly different way - use custom class for obj:

class Config(object):

    def __init__(self, config):
        # do something with config here ...
        self.a = 'example_A'
        self.b = 'example_B'

# in your def cli()
ctx.obj = Config(config)
# in your commands you can works with your prepared data like this:
print(ctx.obj.a)
print(ctx.obj.b)

Hope this helps.