9

tldnr:関数が与えられた場合、その署名からArgumentParserを自動的に作成する方法はありますか?

コマンドラインに公開したい関数がたくさんあります。つまり、基本的に、モジュールは次のとおりです。

 def copy(foo, bar, baz):
    ...
 def move(from, to):
    ...
 def unlink(parrot, nomore=True):
    ...

 if __name__ == '__main__':
     argparse stuff

これは、次のようにコマンドラインから呼び出すことができます。

 python commands.py move spam ham
 python commands.py unlink --parrot Polly

これは実装が非常に簡単ですが、多くの配線が必要です。

parser = argparse.ArgumentParser(...)
subparsers = parser.add_subparsers()
...
c = subparsers.add_parser('unlink', description='Unlink a parrot')
c.add_argument('--parrot', help='parrots name', required=True)
c.add_argument('--nomore', help='this parrot is no more', action='store_true')
...
c = subparsers.add_parser('move', description='Move stuff')
...

など、関数ごとに。最悪の場合、関数の引数が変更された場合(そして変更された場合)、argparseのものを手動で同期する必要があります。

関数が自分自身にargparseのものを提供できれば、メインコードは次のようになります。

parser = argparse.ArgumentParser(...)
subparsers = parser.add_subparsers()

copy.register(subparsers)
move.register(subparsers)
unlink.register(subparsers)
...

私はこれらの線に沿って何かを考えました:

@args(
    description='Unlink a parrot',
    parrot={'required':True, 'help':'parrots name'},
    nomore={'action': 'store_true', 'help': 'this parrot is no more'}
)
def unlink(parrot, nomore=True):
    ...

私の質問:

  • このようなことをするライブラリはありますか?
  • いいえの場合、そのようなデコレータを作成することは可能ですか?
  • 私が欲しいものを実装する他の/より良い方法はありますか?

更新:

placが解決策のようです。これが私がplacでやりたいことをする方法です:

コマンドモジュール:cmds.py:

import plac

@plac.annotations(
    foo=('the foo thing'),
    bar=('the bar thing'),
    fast=('do a fast copy', 'flag')
)
def copy(foo, bar, fast=False):
    """Copy some foo to bar."""
    pass
        
@plac.annotations(
    parrots=('parrots names'),
    nomore=('these parrots are no more', 'flag'),
    repeat=('repeat n times', 'option', 'r', int)
)
def unlink(nomore=False, repeat=1, *parrots):
    """Unlink some parrots."""
    pass

#more commands...

# export commands so that plac knows about them
commands = 'copy', 'unlink'

メインモジュールは次のとおりです。

import plac
import cmds

plac.call(cmds)

あなたが私に尋ねればかなりきちんとしています。

4

5 に答える 5

3

placを試しましたか?

docsの例:

# dbcli.py
import plac
from sqlalchemy.ext.sqlsoup import SqlSoup

@plac.annotations(
    db=plac.Annotation("Connection string", type=SqlSoup),
    header=plac.Annotation("Header", 'flag', 'H'),
    sqlcmd=plac.Annotation("SQL command", 'option', 'c', str, metavar="SQL"),
    delimiter=plac.Annotation("Column separator", 'option', 'd'),
    scripts=plac.Annotation("SQL scripts"),
    )
def main(db, header, sqlcmd, delimiter="|", *scripts):
    "A script to run queries and SQL scripts on a database"
    yield 'Working on %s' % db.bind.url

    if sqlcmd:
        result = db.bind.execute(sqlcmd)
        if header: # print the header
            yield delimiter.join(result.keys())
        for row in result: # print the rows
            yield delimiter.join(map(str, row))

    for script in scripts:
        db.bind.execute(open(script).read())
        yield 'executed %s' % script

if __name__ == '__main__':
    for output in plac.call(main):
        print(output)

出力:

usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]

A script to run queries and SQL scripts on a database

positional arguments:
  db                    Connection string
  scripts               SQL scripts

optional arguments:
  -h, --help            show this help message and exit
  -H, --header          Header
  -c SQL, --sqlcmd SQL  SQL command
  -d |, --delimiter |   Column separator
于 2012-11-06T10:06:39.290 に答える
3

に似た機能がarghplacによって提供されます。これは、特にサブパーサー (またはに見られるものなど) の簡単な作成を特徴としています。gitdjango-admin.py

そのドキュメントからの例:

from argh import *

def dump(args):
    return db.find()

@command
def load(path, format='json'):
    print loaders[format].load(path)

p = ArghParser()
p.add_commands([load, dump])

if __name__ == '__main__':
    p.dispatch()

次の--help応答を生成します。

usage: prog.py [-h] {load,dump} ...

positional arguments:
  {load,dump}
    load
    dump

optional arguments:
  -h, --help   show this help message and exit

および次のload --help:

usage: prog.py load [-h] [-f FORMAT] path

positional arguments:
  path

optional arguments:
  -h, --help            show this help message and exit
  -f FORMAT, --format FORMAT

引数には注釈を付けることができます:

@arg('path')
@arg('--format', choices=['yaml','json'], default='json')
@arg('--dry-run', default=False)
@arg('-v', '--verbosity', choices=range(0,3), default=1)
def load(args, LOADERS={'json': json.load, 'yaml': yaml.load}):
    loader = loaders[args.format]
    data = loader(open(args.path))
    ...

を使用すると@plain_signatureargs引数 toloadがキーワード引数に展開されます。

@arg('path')
@arg('--format', choices=['yaml','json'], default='json')
@arg('--dry-run', default=False)
@arg('-v', '--verbosity', choices=range(0,3), default=1)
@plain_signature
def load(path, format, dry_run, verbosity):
    ...
于 2012-11-11T02:03:15.460 に答える
1

モジュールを使用inspectして、独自の関数定義を確認できます。このようにして、少なくとも初歩的なargparseスケルトンを作成できます。ただし、引数名や場合によってはデフォルト値以外の情報が必要になる場合があります。

たとえば、説明も必要です。この情報の大部分は、適切な形式で docstring を作成することによって提供できます。docstring 用のパーサーがあります (例: Sphynx )。この追加情報を使用すると、関数の argparse 呼び出しを自動的に生成できると思います。

おそらくすべての情報を docstring に保存できるため、デコレータは必要ないと思います。

あなたのプロジェクトの結果に興味があります。

于 2012-11-06T10:28:02.710 に答える
1

別の興味深い代替手段は、追加のユーティリティを使用して argparse への宣言型インターフェイスとしての commando python モジュールです。

コマンドなし:

def main():
    parser = argparse.ArgumentParser(description='hyde - a python static website generator',
                                  epilog='Use %(prog)s {command} -h to get help on individual commands')
    parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__)
    parser.add_argument('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
    subcommands = parser.add_subparsers(title="Hyde commands",
                                     description="Entry points for hyde")
    init_command = subcommands.add_parser('init', help='Create a new hyde site')
    init_command.set_defaults(run=init)
    init_command.add_argument('-t', '--template', action='store', default='basic', dest='template',
                     help='Overwrite the current site if it exists')
    init_command.add_argument('-f', '--force', action='store_true', default=False, dest='force',
                     help='Overwrite the current site if it exists')
    args = parser.parse_args()
    args.run(args)

def init(self, params):
    print params.sitepath
    print params.template
    print params.overwrite

コマンドで:

class Engine(Application):

    @command(description='hyde - a python static website generator',
            epilog='Use %(prog)s {command} -h to get help on individual commands')
    @param('-v', '--version', action='version', version='%(prog)s ' + __version__)
    @param('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
    def main(self, params): pass

    @subcommand('init', help='Create a new hyde site')
    @param('-t', '--template', action='store', default='basic', dest='template',
            help='Overwrite the current site if it exists')
    @param('-f', '--force', action='store_true', default=False, dest='overwrite',
            help='Overwrite the current site if it exists')
    def init(self, params):
        print params.sitepath
        print params.template
        print params.overwrite
于 2013-04-02T17:54:05.483 に答える