Python โต้แย้งกลุ่มเอกสิทธิ์ร่วมกัน


92

สิ่งที่ฉันต้องการคือ:

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

ฉันลองแล้ว แต่ไม่ได้ผล มีใครช่วยฉันได้ไหม

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")

ขอบคุณ!



เสียบ แต่ผมอยากจะพูดถึงห้องสมุดของฉันJoffrey ให้คุณทำในสิ่งที่คำถามนี้ต้องการตัวอย่างเช่นโดยไม่ต้องให้คุณใช้คำสั่งย่อย (เช่นเดียวกับคำตอบที่ยอมรับ) หรือตรวจสอบทุกสิ่งด้วยตัวเอง (เช่นเดียวกับคำตอบที่ได้รับการโหวตสูงสุดเป็นอันดับสอง)
hallo

คำตอบ:


110

add_mutually_exclusive_groupไม่ได้ทำให้ทั้งกลุ่มมีเอกสิทธิ์ร่วมกัน ทำให้ตัวเลือกภายในกลุ่มไม่ซ้ำกัน

สิ่งที่คุณกำลังมองหาsubcommands แทนที่จะเป็น prog [-a xxxx | [-b yyy -c zzz]] คุณมี:

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

ในการเรียกใช้อาร์กิวเมนต์ชุดแรก:

prog command_1 -a xxxx

ในการเรียกใช้อาร์กิวเมนต์ชุดที่สอง:

prog command_2 -b yyyy -c zzzz

คุณยังสามารถตั้งค่าอาร์กิวเมนต์คำสั่งย่อยเป็นตำแหน่ง

prog command_1 xxxx

ประเภทของ git หรือ svn:

git commit -am
git merge develop

ตัวอย่างการทำงาน

# 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')

ทดสอบ

>>> 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

โชคดี.


ฉันใส่ไว้ในกลุ่มโต้แย้งแล้ว ฉันจะเพิ่มคำสั่งย่อยในกรณีนี้ได้อย่างไร? ขอบคุณ!
ฌอน

1
อัปเดตด้วยโค้ดตัวอย่าง คุณจะไม่ใช้กลุ่ม แต่จะใช้แยกย่อย
Jonathan

7
แต่คุณจะทำตามที่ OP ถาม แต่แรกได้อย่างไร ขณะนี้ฉันมีชุดคำสั่งย่อย แต่หนึ่งในคำสั่งย่อยเหล่านั้นต้องการความสามารถในการเลือกระหว่าง[[-a <val>] | [-b <val1> -c <val2>]]
code_dredd

3
สิ่งนี้ไม่ตอบคำถามเพราะไม่อนุญาตให้คุณสร้างคำสั่ง "noname" และบรรลุสิ่งที่ OP ขอ[-a xxx | [-b yyy -c zzz]]
The Godfather

38

ในขณะที่คำตอบของโจนาธานนั้นดีอย่างสมบูรณ์แบบสำหรับตัวเลือกที่ซับซ้อน แต่ก็มีวิธีแก้ปัญหาที่ง่ายมากซึ่งจะใช้ได้กับกรณีง่ายๆเช่น 1 ตัวเลือกไม่รวม 2 ตัวเลือกอื่น ๆ

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

หรือแม้กระทั่งในคำถามเดิม:

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

นี่คือวิธีที่ฉันจะทำ:

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)

ฉันกำลังใช้ตัวเลือกที่นี่ที่มอบให้กับ wrapper บรรทัดคำสั่งเพื่อค้นหา mongodb collectionเช่นสามารถเรียกวิธีaggregateหรือวิธีการfindที่มีการขัดแย้งที่ไม่จำเป็นqueryและfieldsดังนั้นคุณจะเห็นว่าทำไมทั้งสองมีปากเสียงแรกที่เข้ากันได้และสุดท้ายไม่ได้

ตอนนี้ฉันเรียกใช้parser.parse_args()และตรวจสอบเนื้อหา:

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)

แน่นอนว่าการแฮ็กเล็ก ๆ น้อย ๆ นี้ใช้งานได้เฉพาะในกรณีธรรมดา ๆ เท่านั้นและมันจะกลายเป็นฝันร้ายในการตรวจสอบตัวเลือกที่เป็นไปได้ทั้งหมดหากคุณมีตัวเลือกและกลุ่มพิเศษซึ่งกันและกัน ในกรณีนี้คุณควรแบ่งตัวเลือกของคุณในการสั่งการกลุ่มต่างๆตามที่โจนาธานแนะนำ


5
ฉันจะไม่เรียกสิ่งนี้ว่า 'แฮ็ก' สำหรับกรณีนี้เนื่องจากดูเหมือนว่าทั้งอ่านง่ายและจัดการได้มากกว่า - ขอบคุณที่ชี้ให้เห็น!
ปราชญ์

16
parser.error("-a and -q ...")ทางที่ดีกว่าจะใช้งาน วิธีนี้จะถูกพิมพ์ออกมาโดยอัตโนมัติ
WGH

โปรดทราบว่าในกรณีนี้คุณจะต้องตรวจสอบความถูกต้องของกรณีเช่น: (1) ทั้งสองอย่างqและfจำเป็นในกลุ่มแรกคือผู้ใช้ (2) จำเป็นต้องใช้กลุ่มใดกลุ่มหนึ่ง และนี่ทำให้วิธีแก้ปัญหา "ง่าย ๆ " ไม่ใช่เรื่องง่ายอีกต่อไป ดังนั้นฉันจึงยอมรับว่านี่เป็นการแฮ็กมากกว่าสำหรับสคริปต์ที่ทำด้วยมือ แต่ไม่ใช่วิธีแก้ปัญหาที่แท้จริง
The Godfather

4

มีแพทช์ python (อยู่ระหว่างการพัฒนา) ที่จะช่วยให้คุณทำสิ่งนี้ได้
http://bugs.python.org/issue10984

แนวคิดคือการอนุญาตให้มีกลุ่มที่ไม่ซ้ำกันซึ่งทับซ้อนกัน ดังนั้นusageอาจมีลักษณะดังนี้:

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

การเปลี่ยนรหัสอาร์กิวเมนต์เพื่อให้คุณสามารถสร้างกลุ่มสองกลุ่มเช่นนี้เป็นส่วนที่ง่าย เปลี่ยนรหัสการจัดรูปแบบการเขียนต้องกำหนดเองusageHelpFormatter

ในargparseกลุ่มการดำเนินการจะไม่มีผลต่อการแยกวิเคราะห์ เป็นเพียงhelpเครื่องมือในการจัดรูปแบบ ในกลุ่มที่ใช้helpร่วมกันเฉพาะจะมีผลกับusageบรรทัดเท่านั้น เมื่อแยกการparserใช้กลุ่มพิเศษร่วมกันในการสร้างพจนานุกรมของความขัดแย้งที่มีศักยภาพ ( aไม่สามารถเกิดขึ้นกับbหรือc, bไม่สามารถเกิดขึ้นกับaฯลฯ ) แล้วทำให้เกิดข้อผิดพลาดหากมีความขัดแย้งเกิดขึ้น

หากไม่มีโปรแกรมแก้ไขอาร์กิวเมนต์นั้นฉันคิดว่าทางเลือกที่ดีที่สุดของคุณคือทดสอบเนมสเปซที่สร้างขึ้นparse_argsเอง (เช่นถ้าทั้งสองaและbมีค่าที่ไม่เป็นค่าเริ่มต้น) และเพิ่มข้อผิดพลาดของคุณเอง คุณสามารถใช้กลไกข้อผิดพลาดของตัวแยกวิเคราะห์ได้

parser.error('custom error message')

1
ปัญหา Python: bugs.python.org/issue11588กำลังสำรวจวิธีที่จะช่วยให้คุณเขียนการทดสอบพิเศษ / รวมที่กำหนดเอง
hpaulj
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.