On Github telemaco / python-click
            virtualenv .venv
source .venv/bin/activate
pip install click
          
                    #!/usr/bin/env python
import click
@click.command()
def hello():
    click.echo('Hello World!')
if __name__ == '__main__':
    hello()
          
                      ./hello_world.py
Hello World!
          
                    #!/usr/bin/env python
import click
@click.group()
def cli():
    pass
@cli.command()
def say_hello():
    click.echo('Hello hacker!')
@cli.command()
def say_bye():
    click.echo('Bye hacker!')
if __name__ == '__main__':
    cli()
          
                    $ anidando
Usage: anidando [OPTIONS] COMMAND [ARGS]...
Options:
  --help  Show this message and exit.
Commands:
  say_bye
  say_hello
          
                    #!/usr/bin/env python
import click
@click.command()
@click.option('--count-down', default=10, help='suprise countdown')
@click.argument('name')
def surprise(count_down, name):
    while count_down != 0:
        click.echo("%s..." % count_down)
        count_down = count_down - 1
    click.echo("Supriseeeeee!!!! %s" % name)
if __name__ == '__main__':
    surprise()
          
                    $ surprise --help
Usage: surprise [OPTIONS] NAME
Options:
  --count-down INTEGER  suprise countdown
  --help                Show this message and exit.
$ surprise --count-down 3 roberto
3...
2...
1...
Supriseeeeee!!!! roberto
          
                    from setuptools import setup
setup(
    name='yourscript',
    version='0.1',
    py_modules=['yourscript'],
    install_requires=[
        'Click',
    ],
    entry_points='''
        [console_scripts]
        yourscript=yourscript:cli
    ''',
)
          
                  @click.command()
@click.option('--n', default=1)
def dots(n):
    click.echo('.' * n)
        
                  @click.command()
@click.option('--pos', nargs=2, type=float)
def findme(pos):
    click.echo('%s / %s' % pos)
        
                  @click.command()
@click.option('--item', type=(unicode, int))
def putitem(item):
    click.echo('name=%s id=%d' % item)
        
                  @click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
    click.echo('\n'.join(message))
        
                  $ commit -m foo -m bar
foo
bar
             
                @click.command()
@click.option('-v', '--verbose', count=True)
def log(verbose):
    click.echo('Verbosity: %s' % verbose)
        
                  $ log -vvv
Verbosity: 3
            
                @click.command()
@click.option('--shout/--no-shout', default=False)
def info(shout):
    if shout:
        click.echo('Eeeeeeeyyyyy!!!!!')
    else:
        click.echo('Ey')
        
                import sys
@click.command()
@click.option('--upper', 'transformation', flag_value='upper',
              default=True)
@click.option('--lower', 'transformation', flag_value='lower')
def info(transformation):
    click.echo(getattr(sys.platform, transformation)())
        
                  $ info --upper
DARWIN
$ info --lower
darwin
$ info
DARWIN
          
                @click.command()
@click.option('--hash-type', type=click.Choice(['md5', 'sha1']))
def digest(hash_type):
    click.echo(hash_type)
        
                @click.command()
@click.option('--name', prompt=True)
def hello(name):
    click.echo('Hello %s!' % name)
        
                  $ hello --name=John
Hello John!
$ hello
Name: John
Hello John!
          
                @click.command()
@click.password_option()
def encrypt(password):
    click.echo('Encrypting password to %s' % password.encode('rot13'))
#
# Asi seria sin el decorator @click.password_option()
#
# @click.command()
# @click.option('--password', prompt=True, hide_input=True,
#              confirmation_prompt=True)
# def encrypt(password):
#    click.echo('Encrypting password to %s' % password.encode('rot13'))
        
                  $ encrypt
Password: 
Repeat for confirmation: 
Encrypting password to frperg
          
                @click.command()
@click.option('--username')
def greet(username):
    click.echo('Hello %s!' % username)
if __name__ == '__main__':
    greet(auto_envvar_prefix='GREETER')
        
                  $ export GREETER_USERNAME=john
$ greet
Hello john!
          
                @click.command()
@click.option('--username', envvar='USERNAME')
def greet(username):
    click.echo('Hello %s!' % username)
if __name__ == '__main__':
    greet()
        
                  $ export USERNAME=john
$ greet
Hello john!
          
                def validate_rolls(ctx, param, value):
    try:
        rolls, dice = map(int, value.split('d', 2))
        return (dice, rolls)
    except ValueError:
        raise click.BadParameter('rolls need to be in format NdM')
@click.command()
@click.option('--rolls', callback=validate_rolls, default='1d6')
def roll(rolls):
    click.echo('Rolling a %d-sided dice %d time(s)' % rolls)
if __name__ == '__main__':
    roll()
        
                  roll --rolls=42
Usage: roll [OPTIONS]
Error: Invalid value for "--rolls": rolls need to be in format NdM
$ roll --rolls=2d12
Rolling a 12-sided dice 2 time(s)
          
                @click.command()
@click.argument('filename')
def touch(filename):
    click.echo(filename)
        
                  $ touch foo.txt
foo.txt
          
                @click.command()
@click.argument('src', nargs=-1)
@click.argument('dst', nargs=1)
def copy(src, dst):
    for fn in src:
        click.echo('move %s to folder %s' % (fn, dst))
        
                  $ copy foo.txt bar.txt my_folder
move foo.txt to folder my_folder
move bar.txt to folder my_folder
          
                @click.command()
@click.argument('input', type=click.File('rb'))
@click.argument('output', type=click.File('wb'))
def inout(input, output):
    while True:
        chunk = input.read(1024)
        if not chunk:
            break
        output.write(chunk)
        
                  $ inout - hello.txt
hello
^D
$ inout hello.txt -
hello
          
                @click.command()
@click.argument('f', type=click.Path(exists=True))
def touch(f):
    click.echo(click.format_filename(f))
        
                  $ touch hello.txt
hello.txt
$ touch missing.txt
Usage: touch [OPTIONS] F
Error: Invalid value for "f": Path "missing.txt" does not exist.
          
                @click.command()
@click.argument('name')
def say_hello(name):
    """This script say hello to user NAME."""
    click.echo('Hello %s!' % name)
        
                  $ say_hello --help
Usage: say_hello [OPTIONS] NAME
  This script say hello to user NAME.
Options:
  --help           Show this message and exit.
          
                @click.group()
def cli():
    """A simple command line tool."""
@cli.command('init', short_help='init the repo')
def init():
    """Initializes the repository."""
@cli.command('delete', short_help='delete the repo')
def delete():
    """Deletes the repository."""
        
                  $ repo.py
Usage: repo.py [OPTIONS] COMMAND [ARGS]...
  A simple command line tool.
Options:
  --help  Show this message and exit.
Commands:
  delete  delete the repo
  init    init the repo
          
                value = click.prompt('Please enter a valid integer', type=int)
        
                  value = click.prompt('Please enter a number', default=42.0)
        
                if click.confirm('Do you want to continue?'):
    click.echo('Well done!')
        
                  click.confirm('Do you want to continue?', abort=True)
        
                import click
click.echo('Hello World!')
click.echo('Hello World!', err=True)
click.secho('Hello World!', fg='green')
click.secho('Some more text', bg='blue', fg='white')
click.secho('ATTENTION', blink=True, bold=True)
        
                @click.command()
def less():
    click.echo_via_pager('\n'.join('Line %d' % idx
                                   for idx in range(200)))
        
                import click
def get_commit_message():
    MARKER = '# Everything below is ignored\n'
    message = click.edit('\n\n' + MARKER)
    if message is not None:
        return message.split(MARKER, 1)[0].rstrip('\n')
        
                  click.edit(filename='/etc/passwd')
        
                @click.group()
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
    ctx.obj['DEBUG'] = debug
@cli.command()
@click.pass_context
def sync(ctx):
    click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
if __name__ == '__main__':
    cli(obj={})
        
                  $ cmd --debug sync
Debug is on
$ cmd sync
Debug is off
          
                import click
import os
plugin_folder = os.path.join(os.path.dirname(__file__), 'commands')
class MyCLI(click.MultiCommand):
    def list_commands(self, ctx):
        rv = []
        for filename in os.listdir(plugin_folder):
            if filename.endswith('.py'):
                rv.append(filename[:-3])
        rv.sort()
        return rv
    def get_command(self, ctx, name):
        ns = {}
        fn = os.path.join(plugin_folder, name + '.py')
        with open(fn) as f:
            code = compile(f.read(), fn, 'exec')
            eval(code, ns, ns)
        return ns['cli']
@click.command(cls=MyCLI)
def cli():
    pass
        
                import click
@click.group()
def cli1():
    pass
@cli1.command()
def cmd1():
    """Command on cli1"""
@click.group()
def cli2():
    pass
@cli2.command()
def cmd2():
    """Command on cli2"""
cli = click.CommandCollection(sources=[cli1, cli2])
if __name__ == '__main__':
    cli()
        
                @click.group(chain=True)
def cli():
    pass
@cli.command('sdist')
def sdist():
    click.echo('sdist called')
@cli.command('bdist_wheel')
def bdist_wheel():
    click.echo('bdist_wheel called')
        
                  $ setup.py sdist bdist_wheel
sdist called
bdist_wheel called
          
                import click
CONTEXT_SETTINGS = dict(
    default_map={'runserver': {'port': 5000}}
)
@click.group(context_settings=CONTEXT_SETTINGS)
def cli():
    pass
@cli.command()
@click.option('--port', default=8000)
def runserver(port):
    click.echo('Serving on http://127.0.0.1:%d/' % port)
if __name__ == '__main__':
    cli()
        
                  $ cli runserver
Serving on http://127.0.0.1:5000/
          
                cli = click.Group()
@cli.command()
@click.option('--count', default=1)
def test(count):
    click.echo('Count: %d' % count)
@cli.command()
@click.option('--count', default=1)
@click.pass_context
def dist(ctx, count):
    ctx.forward(test)
    ctx.invoke(test, count=42)
        
                  $ cli dist
Count: 1
Count: 42
          
      http://telemaco.github.io/python-click