I have a function which is wrapped as a command using click. So it looks like this:
@click.command() @click.option('-w', '--width', type=int, help="Some helping message", default=0) [... some other options ...] def app(width, [... some other option arguments...]): [... function code...]
I have different use cases for this function. Sometimes, calling it through the command line is fine, but sometime I would also like to call directly the function
from file_name import app width = 45 app(45, [... other arguments ...])
How can we do that? How can we call a function that has been wrapped as a command using click? I found this related post, but it is not clear to me how to adapt it to my case (i.e., build a Context class from scratch and use it outside of a click command function).
EDIT: I should have mentioned: I cannot (easily) modify the package that contains the function to call. So the solution I am looking for is how to deal with it from the caller side.
You can also call a Click function using the callback member function under some conditions. As per this GitHub issue: Assuming you know the given command is a direct wrapper for a function you wrote (and not a group or other type of command), you can get at the function with command. callback.
Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the “Command Line Interface Creation Kit”. It's highly configurable but comes with sensible defaults out of the box.
Click's echo() function will automatically strip ANSI color codes if the stream is not connected to a terminal. the echo() function will transparently connect to the terminal on Windows and translate ANSI codes to terminal API calls.
You can call a click
command function from regular code by reconstructing the command line from parameters. Using your example it could look somthing like this:
call_click_command(app, width, [... other arguments ...])
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)
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.
import click @click.command() @click.option('-w', '--width', type=int, default=0) @click.option('--option2') @click.argument('argument') def app(width, option2, argument): click.echo("params: {} {} {}".format(width, option2, argument)) assert width == 3 assert option2 == '4' assert argument == 'arg' width = 3 option2 = 4 argument = 'arg' if __name__ == "__main__": commands = ( (width, option2, argument, {}), (width, option2, dict(argument=argument)), (width, dict(option2=option2, argument=argument)), (dict(width=width, option2=option2, argument=argument),), ) 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('> {}'.format(cmd)) time.sleep(0.1) call_click_command(app, *cmd[:-1], **cmd[-1]) except BaseException as exc: if str(exc) != '0' and \ not isinstance(exc, (click.ClickException, SystemExit)): raise
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)] ----------- > (3, 4, 'arg', {}) params: 3 4 arg ----------- > (3, 4, {'argument': 'arg'}) params: 3 4 arg ----------- > (3, {'option2': 4, 'argument': 'arg'}) params: 3 4 arg ----------- > ({'width': 3, 'option2': 4, 'argument': 'arg'},) params: 3 4 arg
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With