2

I'm having an issue accessing the default values for the other input arguments from Python's click module (Version 6.7 running on Python 3.6) during a callback on a particular parameter. The goal of my callback would be to parse a set of numbers based on all of the other inputs and return that as the input argument to the function. However, depending on how things are called from the command line, the click context is unable access the default arguments or even some of the command line inputs depending on the order in which they are used. Now, a solution to this problem may be to handle this parsing within the function, but I feel that it should be possible to do this in the callback, so I'd like to try and make it work if possible.

Consider the following minimal example:

import click


def check_number(ctx, _, input_):

    """Do some calculations to check the number"""

    p = ctx.params

    # Print defaults as I see them here.
    print('User:', p.get('user', None))
    print('Start Date:', p.get('start_date', None))
    print('End Date:', p.get('end_date', None))
    print('Number:', input_)

    # Number is typically a range of numbers that needs to be parsed.
    # Here I would revise the input and return whats left after my check
    return input_


@click.command()
@click.option('--user','user', default='Default User')
@click.option('--start','start_date', default='2000_0101')
@click.option('--end','end_date',default='2020_0101')
@click.option('--number','number', default=0, callback=check_number)
def main(user, start_date, end_date, number):

    """Do some stuff."""


if __name__ == '__main__':

    main()

Running the script without invoking any options retrieves the defaults as expected.

> ./click_context_problem.py
User: Default User
Start Date: 2000_0101
End Date: 2020_0101
Number: 0

However, if I start to add input arguments, it becomes unable to recover the default values. For instance:

> ./click_context_problem.py --number=1
User: None
Start Date: None
End Date: None
Number: 1

Now, it is able to see some inputs if I add them to the command line, but only if it is before the --number option, as seen below:

> ./click_context_problem.py --user=me --number=1
User: me
Start Date: None
End Date: None
Number: 1

> ./click_context_problem.py --number=1 --user=me
User: None
Start Date: None
End Date: None
Number: 1

I had seen a proposed solution somewhere (which I can't find anymore unfortunately) that proposed making a click.Command subclass that looks like the following:

class GatherOptions(click.Command):

    """This class makes sure all of the input arguments are gathered and passed through the context for the option callbacks."""

    def make_context(self, *args, **kwargs):
        ctx = super(GatherOptions, self).make_context(*args, *kwargs)
        return ctx

...

@click.command(cls=GatherOptions)  # Add this class here

However, I don't really see what this does as the call to the super class doesn't seem altered in any way. But perhaps the make_context method can be invoked in some other way in order to access the defaults. In any case, it doesn't change the behavior of my program whether thats there or not.

tmwilson26
  • 671
  • 4
  • 16

1 Answers1

0

Very much late, but this might help others with the same issue: Click.Command has both a params property and an (undocumented) get_params(ctx) method. The latter method also returns the help option, if one exists.

To access the default values regardless of order of execution, use the following in your callback code:

defaults = {p.name: p.default for p in ctx.command.params}
# To use param values if they exist, uncomment the following line
# defaults.update(ctx.params)

Note that as tmwilson26 correctly noticed, only params which have already been processed by Click will appear in ctx.params.