Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I return a value when @click.option is used to pass a command line argument to a function?

I am trying to use click python package to pass a command line argument to a function. The example from official documentation works as explained. But nowhere in the documentation is it mentioned how to return a value back. None of the functions in the documentation returns a value, so I don't understand how to do this.

Given example at documentation:

import click

@click.command()
@click.option('--count', default=3, help='Number of greetings.')

def hello(count):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello')

if __name__ == '__main__':
    hello()

What I want to do:

import click

@click.command()
@click.option('--count', default=3, help='Number of greetings.')

def hello(count):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello')
    return "hello hi"

if __name__ == '__main__':
    print(hello())

"hello hi" is not returned as output on console. Can someone tell me how to achieve this?

like image 558
simminni Avatar asked Oct 07 '14 23:10

simminni


People also ask

How to return values from a function in Python?

The Python return statement is a special statement that you can use inside a function or method to send the function's result back to the caller. A return statement consists of the return keyword followed by an optional return value. The return value of a Python function can be any Python object.

What is on click option?

A user interface technique for Mac computers, where the Option key is depressed and held down while the mouse is clicked on an item onscreen. Depending on the context — what is clicked, which app is active, etc., — a variety of functions may be initiated.

What is click Pass_context?

This is a special attribute where commands are supposed to remember what they need to pass on to their children. In order for this to work, we need to mark our function with pass_context() , because otherwise, the context object would be entirely hidden from us.

Which of the following is parameter of the click command?

Click supports two types of parameters for scripts: options and arguments. There is generally some confusion among authors of command line scripts of when to use which, so here is a quick overview of the differences. As its name indicates, an option is optional.


3 Answers

After looking through click's source code/reference I stumbled across the main() method of BaseCommand which takes the parameter, standalone_mode, that allows you to return values if disabled (it is True by default). Hope this helps someone in the future.

Setup code:

import sys
import click

@click.group(invoke_without_command=True)
@click.option('--option1')
@click.argument('arg1')
def main(option1, arg1):
    class SpecialObject():
        def __init__(self, option, arg):
            self.option = option
            self.arg = arg
        def __repr__(self):
            return str(self.option)+str(self.arg)

    return SpecialObject(option1, arg1)

@main.command()
def subcmd():
    return [4,5,6]

Test code:

if __name__ == "__main__":
    commands = (
        ["--help"], 
        ["arg_val",],
        ["--option1","option1_val","arg1_val"],
        ["arg_val","subcmd"],
        ["arg_val","subcmd", "--help",], 
    )
    print(f'Click Version: {click.__version__}')
    print(f'Python Version: {sys.version}')
    for cmd in commands:
        print('-----------')
        print(f'Starting cmd:{cmd}')
        ret_val = main.main(cmd, standalone_mode=False)
        print(f"Returned: {type(ret_val)}, {ret_val}\n"

Output:

$ python __main__.py
Click Version: 7.1.2
Python Version: 3.9.1 (default, Dec 11 2020, 09:29:25) [MSC v.1916 64 bit (AMD64)]
-----------
Starting cmd:['--help']
Usage: __main__.py [OPTIONS] ARG1 COMMAND [ARGS]...

Options:
  --option1 TEXT
  --help          Show this message and exit.

Commands:
  subcmd
Returned: <class 'int'>, 0

-----------
Starting cmd:['arg_val']
Returned: <class '__main__.main.<locals>.SpecialObject'>, Nonearg_val

-----------
Starting cmd:['--option1', 'option1_val', 'arg1_val']
Returned: <class '__main__.main.<locals>.SpecialObject'>, option1_valarg1_val

-----------
Starting cmd:['arg_val', 'subcmd']
Returned: <class 'list'>, [4, 5, 6]

-----------
Starting cmd:['arg_val', 'subcmd', '--help']
Usage: __main__.py subcmd [OPTIONS]

Options:
  --help  Show this message and exit.
Returned: <class 'int'>, 0
like image 64
jsonV Avatar answered Sep 23 '22 08:09

jsonV


Unfortunately, what you're trying to do doesn't make sense. Command-line programs can have an exit code, but that's just a small integer; they can't return text, or arbitrary Python objects.

There's a quasi-standard for what these integers means; the simple version is 0 for success, 1 for most errors, 2 for invalid command-line arguments. click is trying to make your function into a good command-line citizen, so when you exit your function, it calls sys.exit with the appropriate number (0 if you return, 1 if you raise, and 2 if it failed to parse your arguments).

So, whatever you return has no effect, and whatever you try to do with the return value at the top level doesn't even get run.

What programs usually do when they need to "return" text is to print it to standard output, which is exactly what click.echo is for.

like image 37
abarnert Avatar answered Sep 20 '22 08:09

abarnert


You have to tell click to print to stdout using click.echo(). Here is a working example using the code in your question (without caring about exit code as others have mentioned):

import click


@click.command()
@click.option('--count', default=3, help='Number of greetings.')
def hello(count):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello')
    return click.echo("hello hi")

if __name__ == '__main__':
    hello()
like image 32
m79lkm Avatar answered Sep 22 '22 08:09

m79lkm