Grup eksklusif bersama Python argparse

92

Yang saya butuhkan adalah:

pro [-a xxx | [-b yyy -c zzz]]

Saya mencoba ini tetapi tidak berhasil. Bisakah seseorang membantu saya?

group= parser.add_argument_group('Model 2')
group_ex = group.add_mutually_exclusive_group()
group_ex.add_argument("-a", type=str, action = "store", default = "", help="test")
group_ex_2 = group_ex.add_argument_group("option 2")
group_ex_2.add_argument("-b", type=str, action = "store", default = "", help="test")
group_ex_2.add_argument("-c", type=str, action = "store", default = "", help="test")

Terima kasih!

Sean
sumber
Menancapkan, tetapi saya ingin menyebutkan perpustakaan saya joffrey . Memungkinkan Anda melakukan apa yang diinginkan pertanyaan ini, misalnya, tanpa membuat Anda menggunakan subperintah (seperti dalam jawaban yang diterima) atau memvalidasi semuanya sendiri (seperti dalam respons dengan suara terbanyak kedua).
halo

Jawaban:

110

add_mutually_exclusive_grouptidak membuat seluruh grup saling eksklusif. Itu membuat opsi dalam grup menjadi eksklusif.

Apa yang Anda cari adalah subperintah . Alih-alih prog [-a xxxx | [-b yyy -c zzz]], Anda akan memiliki:

prog 
  command 1 
    -a: ...
  command 2
    -b: ...
    -c: ...

Untuk memanggil dengan set argumen pertama:

prog command_1 -a xxxx

Untuk memanggil dengan set argumen kedua:

prog command_2 -b yyyy -c zzzz

Anda juga dapat mengatur argumen sub perintah sebagai posisi.

prog command_1 xxxx

Jenis suka git atau svn:

git commit -am
git merge develop

Contoh Kerja

# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='help for foo arg.')
subparsers = parser.add_subparsers(help='help for subcommand')

# create the parser for the "command_1" command
parser_a = subparsers.add_parser('command_1', help='command_1 help')
parser_a.add_argument('a', type=str, help='help for bar, positional')

# create the parser for the "command_2" command
parser_b = subparsers.add_parser('command_2', help='help for command_2')
parser_b.add_argument('-b', type=str, help='help for b')
parser_b.add_argument('-c', type=str, action='store', default='', help='test')

Menguji

>>> parser.print_help()
usage: PROG [-h] [--foo] {command_1,command_2} ...

positional arguments:
  {command_1,command_2}
                        help for subcommand
    command_1           command_1 help
    command_2           help for command_2

optional arguments:
  -h, --help            show this help message and exit
  --foo                 help for foo arg.
>>>

>>> parser.parse_args(['command_1', 'working'])
Namespace(a='working', foo=False)
>>> parser.parse_args(['command_1', 'wellness', '-b x'])
usage: PROG [-h] [--foo] {command_1,command_2} ...
PROG: error: unrecognized arguments: -b x

Semoga berhasil.

Jonathan
sumber
Saya telah menempatkan mereka di bawah kelompok argumen. Bagaimana cara menambahkan sub-perintah dalam kasus ini? Terima kasih!
Sean
1
Diperbarui dengan kode sampel. Anda tidak akan menggunakan grup, tetapi subparser.
Jonathan
7
Tapi bagaimana Anda akan melakukan apa yang awalnya diminta OP? Saat ini saya memiliki satu set sub-perintah, tetapi salah satu dari sub-perintah itu membutuhkan kemampuan untuk memilih antara[[-a <val>] | [-b <val1> -c <val2>]]
code_dredd
3
Ini tidak menjawab pertanyaan karena itu tidak memungkinkan Anda untuk membuat perintah "noname" dan mencapai apa yang diminta OP[-a xxx | [-b yyy -c zzz]]
The Godfather
38

Meskipun jawaban Jonathan baik-baik saja untuk opsi yang kompleks, ada solusi yang sangat sederhana yang akan berfungsi untuk kasus sederhana, misalnya 1 opsi mengecualikan 2 opsi lain seperti di

command [- a xxx | [ -b yyy | -c zzz ]] 

atau bahkan seperti pada pertanyaan awal:

pro [-a xxx | [-b yyy -c zzz]]

Inilah cara saya melakukannya:

parser = argparse.ArgumentParser()

# group 1 
parser.add_argument("-q", "--query", help="query", required=False)
parser.add_argument("-f", "--fields", help="field names", required=False)

# group 2 
parser.add_argument("-a", "--aggregation", help="aggregation",
                    required=False)

Saya menggunakan opsi di sini yang diberikan ke pembungkus baris perintah untuk menanyakan mongodb. Itucollection contoh baik dapat memanggil metode aggregateatau metode finddengan argumen opsionalquery dan fields, karenanya Anda melihat mengapa dua argumen pertama yang kompatibel dan yang terakhir adalah tidak.

Jadi sekarang saya menjalankan parser.parse_args()dan memeriksa isinya:

args = parser().parse_args()

print args.aggregation
if args.aggregation and (args.query or args.fields):
    print "-a and -q|-f are mutually exclusive ..."
    sys.exit(2)

Tentu saja, peretasan kecil ini hanya berfungsi untuk kasus-kasus sederhana dan akan menjadi mimpi buruk untuk memeriksa semua opsi yang memungkinkan jika Anda memiliki banyak opsi dan grup yang saling eksklusif. Dalam hal ini Anda harus membagi pilihan Anda menjadi kelompok perintah seperti yang disarankan Jonathan.

Oz123
sumber
5
Saya tidak akan menyebut ini 'peretasan' untuk kasus ini, karena tampaknya lebih mudah dibaca dan dikelola - terima kasih telah menunjukkannya!
sage
16
Cara yang lebih baik adalah dengan menggunakan parser.error("-a and -q ..."). Dengan cara ini bantuan penggunaan lengkap akan dicetak secara otomatis.
WGH
Harap dicatat bahwa dalam kasus ini Anda juga perlu memvalidasi kasus-kasus seperti: (1) keduanya qdan fdiperlukan di grup pertama adalah pengguna, (2) grup mana pun diperlukan. Dan ini membuat solusi "sederhana" tidak sesederhana itu lagi. Jadi saya setuju bahwa ini lebih merupakan retasan untuk skrip buatan tangan, tetapi bukan solusi nyata
The Godfather
4

Ada patch python (dalam pengembangan) yang memungkinkan Anda melakukan ini.
http://bugs.python.org/issue10984

Idenya adalah untuk memungkinkan tumpang tindih kelompok yang saling eksklusif. Jadi usagemungkin terlihat seperti:

pro [-a xxx | -b yyy] [-a xxx | -c zzz]

Mengubah kode argparse sehingga Anda dapat membuat dua grup seperti ini adalah bagian yang mudah. Mengubah usagekode format diperlukan penulisan customHelpFormatter .

Di argparse, grup tindakan tidak memengaruhi penguraian. Mereka hanyalah helpalat pemformatan. Di dalam help, kelompok yang saling eksklusif hanya mempengaruhi usagegaris. Saat mengurai, parserpenggunaan grup yang saling eksklusif untuk membuat kamus potensi konflik ( atidak dapat terjadi dengan batau c, btidak dapat terjadi dengana , dll), dan kemudian menimbulkan kesalahan jika konflik muncul.

Tanpa patch argparse itu, menurut saya pilihan terbaik Anda adalah menguji namespace yang diproduksi parse_argssendiri (misalnya jika keduanya adan bmemiliki nilai nondefault), dan meningkatkan kesalahan Anda sendiri. Anda bahkan dapat menggunakan mekanisme kesalahan parser sendiri.

parser.error('custom error message')
hpaulj
sumber
1
Masalah Python: bugs.python.org/issue11588 sedang mencari cara untuk memungkinkan Anda menulis pengujian eksklusif / inklusif khusus.
hpaulj