1. 程式人生 > >Python命令行解析庫——argarse、docopt、click、invoke

Python命令行解析庫——argarse、docopt、click、invoke

argarse、docopt、click

命令行示例:

基本用法

$ python [file].py hello Kyle
Hello, Kyle!
$ python [file].py goodbye Kyle
Goodbye, Kyle!

W/選項用法(標誌)

$ python [file].py hello --greeting=Wazzup Kyle
Whazzup, Kyle!

$ python [file].py goodbye --greeting=Later Kyle
Later, Kyle!

$ python [file].py hello --caps Kyle
HELLO, KYLE!

$ python [file].py hello --greeting=Wazzup --caps Kyle
WAZZUP, KYLE!

Argarse:標準庫

def greet(args):
    output = '{0}, {1}!'.format(args.greeting, args.name)
    if args.caps:
        output = output.upper()
    print(output)

parser = argparse.ArgumentParser()
parser.add_argument('--version', action='version', version='1.0.0')
subparsers = parser.add_subparsers()

hello_parser = subparsers.add_parser('hello')
hello_parser.add_argument('name', help='name of the person to greet')
hello_parser.add_argument('--greeting', default='Hello', help='word to use for the greeting')
hello_parser.add_argument('--caps', action='store_true', help='uppercase the output')
hello_parser.set_defaults(func=greet)

goodbye_parser = subparsers.add_parser('goodbye')
goodbye_parser.add_argument('name', help='name of the person to greet')
goodbye_parser.add_argument('--greeting', default='Hello', help='word to use for the greeting')
goodbye_parser.add_argument('--caps', action='store_true', help='uppercase the output')
goodbye_parser.set_defaults(func=greet)

if __name__ == '__main__':
    args = parser.parse_args()
    args.func(args)

Docopt

"""usage: greet [--help] <command> [<args>...]

options:
  -h --help         Show this screen.
  --version         Show the version.

commands:
   hello       Say hello
   goodbye     Say goodbye

"""

from docopt import docopt
from schema import Schema, SchemaError, Optional

HELLO = """usage: basic.py hello [options] [<name>]

  -h --help         Show this screen.
  --caps            Uppercase the output.
  --greeting=<str>  Greeting to use [default: Hello].
"""

GOODBYE = """usage: basic.py goodbye [options] [<name>]

  -h --help         Show this screen.
  --caps            Uppercase the output.
  --greeting=<str>  Greeting to use [default: Goodbye].
"""


def greet(args):
    output = '{0}, {1}!'.format(args['--greeting'],
                                args['<name>'])
    if args['--caps']:
        output = output.upper()
    print(output)


if __name__ == '__main__':
    arguments = docopt(__doc__, options_first=True, version='1.0.0')

    schema = Schema({
        Optional('hello'): bool,
        Optional('goodbye'): bool,
        '<name>': str,
        Optional('--caps'): bool,
        Optional('--help'): bool,
        Optional('--greeting'): str
    })

    def validate(args):
        try:
            args = schema.validate(args)
            return args
        except SchemaError as e:
            exit(e)

    if arguments['<command>'] == 'hello':
        greet(validate(docopt(HELLO)))
    elif arguments['<command>'] == 'goodbye':
        greet(validate(docopt(GOODBYE)))
    else:
        exit("{0} is not a command. See 'options.py --help'.".format(arguments['<command>']))

Click

import click

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])


def greeter(**kwargs):
    output = '{0}, {1}!'.format(kwargs['greeting'],
                                kwargs['name'])
    if kwargs['caps']:
        output = output.upper()
    print(output)


@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(version='1.0.0')
def greet():
    pass


@greet.command()
@click.argument('name')
@click.option('--greeting', default='Hello', help='word to use for the greeting')
@click.option('--caps', is_flag=True, help='uppercase the output')
def hello(**kwargs):
    greeter(**kwargs)


@greet.command()
@click.argument('name')
@click.option('--greeting', default='Goodbye', help='word to use for the greeting')
@click.option('--caps', is_flag=True, help='uppercase the output')
def goodbye(**kwargs):
    greeter(**kwargs)

if __name__ == '__main__':
    greet()

Invoke

from invoke import task


def greet(name, greeting, caps):
    output = '{0}, {1}!'.format(greeting, name)
    if caps:
        output = output.upper()
    print(output)


HELP = {
    'name': 'name of the person to greet',
    'greeting': 'word to use for the greeting',
    'caps': 'uppercase the output'
}


def print_version():
    print('1.0.0')
    exit(0)


@task(help=HELP)
def hello(name='', greeting='Hello', caps=False, version=False):
    """
    Say hello.
    """
    if version:
        print_version()
    greet(name, greeting, caps)


@task(help=HELP)
def goodbye(name='', greeting='Goodbye', caps=False, version=False):
    """
    Say goodbye.
    """
    if version:
        print_version()
    greet(name, greeting, caps)

錯誤處理一般有三種情況:

1、參數不足。

2、無效的選項/標誌。

3、需要標誌給一個值。

Argparse

$ python argparse/final.py hello
usage: final.py hello [-h] [--greeting GREETING] [--caps] name
final.py hello: error: the following arguments are required: name

$ python argparse/final.py --badoption hello Kyle
usage: final.py [-h] [--version] {hello,goodbye} ...
final.py: error: unrecognized arguments: --badoption

$ python argparse/final.py hello --caps=notanoption Kyle
usage: final.py hello [-h] [--greeting GREETING] [--caps] name
final.py hello: error: argument --caps: ignored explicit argument 'notanoption'

Docopt,需借助於scheme

...
from schema import Schema, SchemaError, Optional
...
    schema = Schema({
        Optional('hello'): bool,
        Optional('goodbye'): bool,
        '<name>': str,
        Optional('--caps'): bool,
        Optional('--help'): bool,
        Optional('--greeting'): str
    })

    def validate(args):
        try:
            args = schema.validate(args)
            return args
        except SchemaError as e:
            exit(e)

    if arguments['<command>'] == 'hello':
        greet(validate(docopt(HELLO)))
    elif arguments['<command>'] == 'goodbye':
        greet(validate(docopt(GOODBYE)))
...

$ python docopt/validation.py hello
None should be instance of <class 'str'>

$ python docopt/validation.py hello --greeting Kyle
None should be instance of <class 'str'>

$ python docopt/validation.py hello --caps=notanoption Kyle
--caps must not have an argument
usage: basic.py hello [options] [<name>]

Click

$ python click/final.py hello
Usage: final.py hello [OPTIONS] NAME

Error: Missing argument "name".

$ python click/final.py hello --badoption Kyle
Error: no such option: --badoption

$ python click/final.py hello --caps=notanoption Kyle
Error: --caps option does not take a value

打包

entry_point本質上是一個到代碼單一函數的映射,系統PATH將給出一個命令。entry_point的形式是command = package.module:function

包裝click命令

greeter/
├── greet
│   ├── __init__.py
│   └── cli.py       <-- the same as our final.py
└── setup.py
entry_points={
    'console_scripts': [
        'greet=greet.cli:greet',  # command=package.module:function
    ],
},

當用戶安裝entry_point創建的包時,setuptools將創建以下可執行腳本(調用greet)並放置它在用戶系統的PATH。

#!/usr/bin/python
if __name__ == '__main__':
    import sys
    from greet.cli import greet

    sys.exit(greet())

包裝Argparse命令

代碼:

if __name__ == '__main__':
    args = parser.parse_args()
    args.func(args)

變成:

def greet():
    args = parser.parse_args()
    args.func(args)
if __name__ == '__main__':
    greet()

針對entry_point,現在我們可以使用click中定義的相同的模式了。


包裝Docopt命令

代碼:

if __name__ == '__main__':
    arguments = docopt(__doc__, options_first=True, version='1.0.0')

    if arguments['<command>'] == 'hello':
        greet(docopt(HELLO))
    elif arguments['<command>'] == 'goodbye':
        greet(docopt(GOODBYE))
    else:
        exit("{0} is not a command. See 'options.py --help'.".format(arguments['<command>']))

變成:

def greet():
    arguments = docopt(__doc__, options_first=True, version='1.0.0')

    if arguments['<command>'] == 'hello':
        greet(docopt(HELLO))
    elif arguments['<command>'] == 'goodbye':
        greet(docopt(GOODBYE))
    else:
        exit("{0} is not a command. See 'options.py --help'.".format(arguments['<command>']))
if __name__ == '__main__':
    greet()


Python命令行解析庫——argarse、docopt、click、invoke