Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating command line application in python using Click

I am creating a command line application in Python using the Click library that accepts a name as input but if no name is entered it returns the default value.

Here is the code I have so far.

hello.py

import click

@click.version_option(1.0)

@click.command()
@click.argument('string', default='World')
@click.option('-r', '--repeat', default=1, help='How many times should be greeted.')

def cli(string,repeat):
    '''This string greets you.'''
    for i in xrange(repeat): 
        click.echo('Hello %s!' % string)

if __name__ == '__main__':
    cli()

When I run it.

$ hello

Hello World!

$ hello Bob

Hello Bob!

$ hello Bob -r 3

Hello Bob!
Hello Bob!
Hello Bob!

This is exactly what I want.

Now, I would like to be able to accept input from stdin like the following examples.

$ echo John | hello

Hello John!

$ echo John | hello -r 3

Hello John!
Hello John!
Hello John!
like image 212
Ishan Avatar asked Oct 18 '15 02:10

Ishan


People also ask

How do you make a click option in Python?

Python click option names Click derives the name of the option from the long name, if both are used. In the example, we create an option with both short and long names. The name of the variable passed to the function is string , derived from the longer option name. We run the program using both option names.

What is click command Python?

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.


Video Answer


2 Answers

Well this is quite an old question but I will try to answer it anyway.

I'm sorta new to Click so, I reckon, my solution can be improved immensely. Anyway it does exactly what you want. Here it is:

import click


def get_name(ctx, param, value):
    if not value and not click.get_text_stream('stdin').isatty():
        return click.get_text_stream('stdin').read().strip()
    else:
        return value


@click.command()
@click.argument('name', callback=get_name, required=False)
@click.option('--repeat', '-r', default=1)
def say_hello(name, repeat):
    for i in range(repeat):
        click.echo('Hello {}'.format(name or 'World'))



if __name__ == "__main__":
    say_hello()
like image 99
BigBear Avatar answered Oct 19 '22 09:10

BigBear


The problem is that the command result before piping will be consumed inside your application and not as an argument for it. Since your application doesn't use any input inside itself, everything you pipe to it will be discarded.

If you want you application to be 'pipeable' just insert a raw_input inside it since this function reads from stdin.

To make your program to looks like cat you can do:

@click.command()
@click.argument('string', required=False)
@click.option('-r', '--repeat', default=1, help='How many times should be greeted.')
def cli(string, repeat):
    '''This string greets you.'''
    if not string:
        string = raw_input()
    for i in xrange(repeat):
        click.echo('Hello %s!' % string)

Another option is to transform string in an option and set prompt to True:

@click.command()
@click.option('--string', prompt=True)
@click.option('-r', '--repeat', default=1, help='How many times should be greeted.')
def cli(string, repeat):
    '''This string greets you.'''
    for i in xrange(repeat):
        click.echo('Hello %s!' % string)

In this way, if the user does not provide a string he will be prompted for input, which makes your application pipeable too. The only problem is that it will print to stdout STRING: which sometimes is not acceptable (you can define an empty string to display with prompt='', but, since I know, there is no chance to get rid of :).

By the way, to achieve the same thing, using the your code the way it is, you can do:

python hello.py `echo bob`

echo bob will be evaluated first and its result will compose the arguments for hello.py

like image 38
ppalacios Avatar answered Oct 19 '22 10:10

ppalacios