Search code examples
pythoncommand-line-interfacepython-click

Multiple context objects in CLI (Click)


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.


Solution

  • 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.