8

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!

2 Answers 2

7

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()
Sign up to request clarification or add additional context in comments.

Comments

4

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.