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.

Answer from Danila Ganchar on Stack Overflow
🌐
Click Documentation
click.palletsprojects.com › en › stable › complex
Complex Applications — Click Documentation (8.5.x)
Whenever a Click command is executed, a Context object is created which holds state for this particular invocation. It remembers parsed parameters, what command created it, which resources need to be cleaned up at the end of the function, and so forth.
Discussions

How to put context in custom object?
I have a nested application like the complex cli example, where a custom object is passed through a custom decorator composed with click.make_pass_decorator and ensure=True. If I put @pass_context ... More on github.com
🌐 github.com
July 23, 2015
command line interface - How to access the main Context object of a Python Click application - Stack Overflow
(*) I know that I can generate a new Context object via click.Context(hello) but this context does not contain by itself the info_name property. Of course I can figure out the script name manually, but Click certainly creates a default context with the script name and I believe that one would ... More on stackoverflow.com
🌐 stackoverflow.com
Make `click.Context` generic for `obj`
To add some type safety to the obj attribute of the Context class, I was thinking maybe it could be made generic with respect to obj attribute. It could then be used this way (with a TypedDict but it could be any user defined object): cl... More on github.com
🌐 github.com
5
April 16, 2023
command line interface - Why doesn't python-click pass context between subcommands? - Stack Overflow
Click Version: 8.1.3 Python Version: 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)] ----------- > fn1 fn2 obj: {'fn1': 1} ... Sign up to request clarification or add additional context in comments. More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 1
7

If you can edit your click command functions, you can organize them like this:

@cli.command()
@click.argument('some_argument', type=str)
@click.pass_context
def say_something(ctx, some_argument):
    return _say_something(ctx, some_argument):

def _say_something(ctx, some_argument):
    print(some_argument)

If built like this, then you can call _say_something() function as an undecorated (normal) Python function.

If you can not edit your commands

Building on this answer you can pass the context to another click command using this function:

Code:

def call_click_command_with_ctx(cmd, ctx, *args, **kwargs):
    """ Wrapper to call a click command with a Context object

    :param cmd: click cli command function to call
    :param ctx: click context
    :param args: arguments to pass to the function
    :param kwargs: keyword arguments to pass to the function
    :return: None
    """

    # monkey patch make_context
    def make_context(*some_args, **some_kwargs):
        child_ctx = click.Context(cmd, parent=ctx)
        with child_ctx.scope(cleanup=False):
            cmd.parse_args(child_ctx, list(args))
        return child_ctx

    cmd.make_context = make_context
    prev_make_context = cmd.make_context

    # call the command
    call_click_command(cmd, *args, **kwargs)

    # restore make_context
    cmd.make_context = prev_make_context

How does this work?

This works because click is a well designed OO framework. The @click.Command object can be introspected to determine what parameters it is expecting. Then a command line can be constructed that will look like the command line that click is expecting. In addition, the make_context method of the command can be overridden to allow the command context to used by the command.

Code from Previous Answer:

def call_click_command(cmd, *args, **kwargs):
    """ Wrapper to call a click command

    :param cmd: click cli command function to call
    :param args: arguments to pass to the function
    :param kwargs: keywrod arguments to pass to the function
    :return: None
    """

    # Get positional arguments from args
    arg_values = {c.name: a for a, c in zip(args, cmd.params)}
    args_needed = {c.name: c for c in cmd.params
                   if c.name not in arg_values}

    # build and check opts list from kwargs
    opts = {a.name: a for a in cmd.params if isinstance(a, click.Option)}
    for name in kwargs:
        if name in opts:
            arg_values[name] = kwargs[name]
        else:
            if name in args_needed:
                arg_values[name] = kwargs[name]
                del args_needed[name]
            else:
                raise click.BadParameter(
                    "Unknown keyword argument '{}'".format(name))


    # check positional arguments list
    for arg in (a for a in cmd.params if isinstance(a, click.Argument)):
        if arg.name not in arg_values:
            raise click.BadParameter("Missing required positional"
                                     "parameter '{}'".format(arg.name))

    # build parameter lists
    opts_list = sum(
        [[o.opts[0], str(arg_values[n])] for n, o in opts.items()], [])
    args_list = [str(v) for n, v in arg_values.items() if n not in opts]

    # call the command
    cmd(opts_list + args_list)

Test Code:

import click

@click.group(chain=True)
@click.option('--some_common_option', type=float, default=1e-10)
@click.pass_context
def cli(ctx, some_common_option):
    # save shared params within context object for different commands
    for k, v in locals().items():
        if 'ctx' not in k:
            ctx.obj[k] = v


@cli.command()
@click.argument('some_argument', type=str)
@click.pass_context
def say_something(ctx, some_argument):
    print(some_argument)


@cli.command()
@click.argument('some_other_argument', type=str)
@click.pass_context
def say_more(ctx, some_other_argument):
    ctx.obj['text'] = some_other_argument
    call_click_command_with_ctx(say_something, ctx, ctx.obj['text'])


if __name__ == "__main__":
    commands = (
        'say_something something',
        'say_more more',
        '--help',
    )

    import sys, time

    time.sleep(1)
    print('Click Version: {}'.format(click.__version__))
    print('Python Version: {}'.format(sys.version))
    for cmd in commands:
        try:
            time.sleep(0.1)
            print('-----------')
            print('> ' + cmd)
            time.sleep(0.1)
            cli(cmd.split(), obj={})

        except BaseException as exc:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise

Results:

Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> say_something something
something
-----------
> say_more more
more
-----------
> --help
Usage: test.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  --some_common_option FLOAT
  --help                      Show this message and exit.

Commands:
  say_more
  say_something
🌐
AssemblyAI
assemblyai.com › blog › the-definitive-guide-to-python-click
The Definitive Guide to Python Click | AssemblyAI
July 28, 2021 - Let’s make a command that will use the pass_dict decorator to pass in the dictionary object that is stored. The Python Click library will search for the innermost object (in context) of the type we specified above (dict) and pass that to our function.
🌐
Digitalk
digitalk.tech › blog › python-click-or-how-to-write-professional-cli-applications
Mndwrk Blog - Python click or how to write professional CLI applications
June 17, 2022 - The value parameter will be the unprocessed option value, and the param parameter will be the option- or argument instance (either a click.Option or a click.Argument instance). Finally, the ctx parameter is the currently active click execution context (click.Context object).
🌐
GitHub
github.com › pallets › click › issues › 387
How to put context in custom object? · Issue #387 · pallets/click
July 23, 2015 - You can use click.get_current_context(), but it should be used rarely and I'm not sure if it's overkill in your case.
🌐
Readthedocs
pocoo-click.readthedocs.io › en › latest › api
API - the Click Documentation
If the cleanup is intended the context object can also be directly used as a context manager. ... New in version 5.0. ... The width of the terminal (None is autodetection). ... An optional normalization function for tokens. This is options, choices, commands etc. ... Returns the current click context.
🌐
ProgramCreek
programcreek.com › python › example › 88034 › click.Context
Python Examples of click.Context
:param click_context: click context object. :param agents: agents names. :param multithreaded: bool flag to run as multithreads. :return: None.
Find elsewhere
🌐
Readthedocs
pocoo-click.readthedocs.io › en › latest › advanced
Advanced Patterns - the Click Documentation
Starting with Click 5.0 it is possible to access the current context from anywhere within the same thread through the use of the get_current_context() function which returns it. This is primarily useful for accessing the context bound object as well as some flags that are stored on it to customize the runtime behavior.
🌐
GitHub
github.com › pallets › click › issues › 2493
Make `click.Context` generic for `obj` · Issue #2493 · pallets/click
April 16, 2023 - Make click.Context generic for obj#2493 · Copy link · Labels · typingType annotations and stubsType annotations and stubs · Viicos · opened · on Apr 16, 2023 · Issue body actions · To add some type safety to the obj attribute of the Context class, I was thinking maybe it could be made generic with respect to obj attribute. It could then be used this way (with a TypedDict but it could be any user defined object): class ContextObj(TypedDict): attr: int def subcommand( ctx: click.Context[ContextObj], ...
Author: pallets
🌐
Better Stack
betterstack.com › community › guides › scaling-python › click-explained
Creating composable CLIs with click in Python | Better Stack Community
In this example, @click.pass_context is used to pass the context object to each command, allowing them to access shared values.
🌐
Click Documentation
click.palletsprojects.com › en › stable › commands
Advanced Groups and Context — Click Documentation (8.5.x)
If the object is provided, each context will pass the object onwards to its children, but at any level a context’s object can be overridden.
🌐
Click Documentation
click.palletsprojects.com › api
API — Click Documentation (8.5.x)
Context manager. Yields a writable file-like object which can be used as an output pager. ... Added in version 8.4.0. ... color (bool | None) – controls if the pager supports ANSI colors or not. The default is autodetection. ... click.prompt(text: str, default: str | None = None, hide_input: ...
🌐
Click Documentation
click.palletsprojects.com › en › stable › advanced
Advanced Patterns — Click Documentation (8.5.x)
One common way to prevent a parameter from being passed to the callback is the expose_value argument to a parameter which hides the parameter entirely. The way this works is that the Context object has a params attribute which is a dictionary of all parameters.
🌐
GitHub
github.com › pallets › click › issues › 200
Setting context object inside function doesn't pass the new ...
August 12, 2014 - @click.group() @click.option('--debug/--no-debug', default=False) @click.pass_context def cli(ctx, debug): if ctx.obj is None: # Doesn't work as of 3.0 ctx.obj = {} ctx.obj['DEBUG'] = debug @cli.command() @click.pass_context def sync(ctx): click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off')) if __name__ == '__main__': cli()
Author: pallets
🌐
Medium
lewoudar.medium.com › click-a-beautiful-python-library-to-write-cli-applications-9c8154847066
Click: a beautiful python library to write CLI applications | by Kevin Tewouda | Medium
December 27, 2022 - In line 49, we pass the click context for this CLI and in line 52 we instantiate the RequestData class holding some state we need in the subcommands. The resulting object is passed to the obj attribute of the context.
🌐
GitHub
github.com › pallets › click › issues › 516
Accessing global context in tests · Issue #516 · pallets/click
February 5, 2016 - Is there a way for me to mock this context object in a nice way. The only approach I can think of is to patch the pass_context director before I import my CLI module into my testing module. ... @click.group() @click.option('-c', '--config', default=os.path.expanduser("~/.configfile"), help='Path to configuration file') @click.pass_context def main(ctx, config): ctx.obj = { 'config': config, 'connections': { 'redis': RedisConnection(config) } } @main.command() @click.pass_context def command(ctx): conn = ctx.obj['connections'] conn['redis'].do_thing() # here i'd like to make the RedisConnection a mock during testing
Author: pallets