2011-12-15 19 views
19

Tôi đã đoạn mã sau (sử dụng Python 2.7):subparser argparse tùy chọn (ví --version)

# shared command line options, like --version or --verbose 
parser_shared = argparse.ArgumentParser(add_help=False) 
parser_shared.add_argument('--version', action='store_true') 

# the main parser, inherits from `parser_shared` 
parser = argparse.ArgumentParser(description='main', parents=[parser_shared]) 

# several subcommands, which can't inherit from the main parser, since 
# it would expect subcommands ad infinitum 
subparsers = parser.add_subparsers('db', parents=[parser_shared]) 

... 

args = parser.parse_args() 

Bây giờ tôi muốn để có thể gọi chương trình này ví dụ với --version được nối vào chương trình thông thường hoặc một số tiểu ban:

$ prog --version 
0.1 

$ prog db --version 
0.1 

Về cơ bản, tôi cần khai báo các trình con tùy chọn tùy chọn. Tôi biết rằng đây không phải là really supported, nhưng có cách giải quyết hoặc giải pháp thay thế nào không?

Edit: Các thông báo lỗi Tôi nhận:

$ prog db --version 
# works fine 

$ prog --version 
usage: .... 
prog: error: too few arguments 

Trả lời

17

Theo tài liệu, --version với action='version' (và không phải với action='store_true') in tự động số phiên bản:

parser.add_argument('--version', action='version', version='%(prog)s 2.0') 
2

Yeah, tôi chỉ đã kiểm tra svn, được sử dụng làm ví dụ đối tượng trong add_subparsers() documentation và chỉ hỗ trợ '--version' trên lệnh chính:

python zacharyyoung$ svn log --version 
Subcommand 'log' doesn't accept option '--version' 
Type 'svn help log' for usage. 

Tuy nhiên:

# create common parser 
parent_parser = argparse.ArgumentParser('parent', add_help=False) 
parent_parser.add_argument('--version', action='version', version='%(prog)s 2.0') 

# create the top-level parser 
parser = argparse.ArgumentParser(parents=[parent_parser]) 
subparsers = parser.add_subparsers() 

# create the parser for the "foo" command 
parser_foo = subparsers.add_parser('foo', parents=[parent_parser]) 

nào mang lại:

python zacharyyoung$ ./arg-test.py --version 
arg-test.py 2.0 
python zacharyyoung$ ./arg-test.py foo --version 
arg-test.py foo 2.0 
1

Trong khi chúng tôi wait for this feature sẽ được chuyển giao, chúng ta có thể sử dụng mã như thế này:

# Make sure that main is the default sub-parser 
if '-h' not in sys.argv and '--help' not in sys.argv: 
    if len(sys.argv) < 2: 
     sys.argv.append('main') 
    if sys.argv[1] not in ('main', 'test'): 
     sys.argv = [sys.argv[0], 'main'] + sys.argv[1:] 
+0

Lưu ý rằng chúng tôi chờ tính năng cơ bản này kể từ năm 2009. – yac

+0

Tôi đã bắt đầu sử dụng docopt thay vì trình phân tích đối số dựng sẵn. Nó hỗ trợ sử dụng hỗn hợp có hoặc không có "hành động", hay còn gọi là "động từ". http://docopt.org/ –

7

FWIW, tôi chạy vào điều này cũng có, và kết thúc "giải quyết" nó bằng cách không sử dụng subparsers (tôi đã có của riêng tôi hệ thống hỗ trợ in ấn, vì vậy không mất bất cứ thứ gì ở đó).

Thay vào đó, tôi làm điều này:

parser.add_argument("command", nargs="?", 
        help="name of command to execute") 

args, subcommand_args = parser.parse_known_args() 

... và sau đó là subcommand tạo phân tích cú pháp riêng của mình (tương tự như một subparser) mà hoạt động chỉ trên subcommand_args.

3

Như được thảo luận trong http://bugs.python.org/issue9253 (argparse: subparsers tùy chọn), như của Python 3.3, subparsers bây giờ là tùy chọn. Đây là một kết quả không mong muốn của một thay đổi trong cách parse_args kiểm tra đối số bắt buộc.

Tôi đã tìm thấy một fudge khôi phục hành vi trước đó (yêu cầu subparsers), đặt rõ ràng thuộc tính required của hành động subparsers.

parser = ArgumentParser(prog='test') 
subparsers = parser.add_subparsers() 
subparsers.required = True # the fudge 
subparsers.dest = 'command' 
subparser = subparsers.add_parser("foo", help="run foo") 
parser.parse_args() 

Xem vấn đề đó để biết thêm chi tiết. Tôi hy vọng rằng nếu và khi vấn đề này được vá đúng cách, các trình con con sẽ được yêu cầu theo mặc định, với một số tùy chọn để đặt thuộc tính required thành False. Nhưng có một phần tồn đọng lớn của các bản vá lỗi argparse.

0

Mặc dù câu trả lời của @ eumiro giải quyết tùy chọn --version, nhưng nó chỉ có thể làm như vậy bởi vì đó là trường hợp đặc biệt để chọn tham gia.Để cho phép lời gọi chung của:

prog 
prog --verbose 
prog --verbose main 
prog --verbose db 

và có prog --version công việc giống như prog --verbose main (và prog main --verbose) bạn có thể thêm một phương pháp để Argumentparser và gọi đó với tên của subparser mặc định, chỉ cần trước khi gọi parse_args():

import argparse 
import sys 

def set_default_subparser(self, name, args=None): 
    """default subparser selection. Call after setup, just before parse_args() 
    name: is the name of the subparser to call by default 
    args: if set is the argument list handed to parse_args() 

    , tested with 2.7, 3.2, 3.3, 3.4 
    it works with 2.6 assuming argparse is installed 
    """ 
    subparser_found = False 
    for arg in sys.argv[1:]: 
     if arg in ['-h', '--help']: # global help if no subparser 
      break 
    else: 
     for x in self._subparsers._actions: 
      if not isinstance(x, argparse._SubParsersAction): 
       continue 
      for sp_name in x._name_parser_map.keys(): 
       if sp_name in sys.argv[1:]: 
        subparser_found = True 
     if not subparser_found: 
      # insert default in first position, this implies no 
      # global options without a sub_parsers specified 
      if args is None: 
       sys.argv.insert(1, name) 
      else: 
       args.insert(0, name) 

argparse.ArgumentParser.set_default_subparser = set_default_subparser 

def do_main(args): 
    print 'main verbose', args.verbose 

def do_db(args): 
    print 'db verbose:', args.verbose 

parser = argparse.ArgumentParser() 
parser.add_argument('--verbose', action='store_true') 
parser.add_argument('--version', action='version', version='%(prog)s 2.0') 
subparsers = parser.add_subparsers() 
sp = subparsers.add_parser('main') 
sp.set_defaults(func=do_main) 
sp.add_argument('--verbose', action='store_true') 
sp = subparsers.add_parser('db') 
sp.set_defaults(func=do_db) 

parser.set_default_subparser('main') 
args = parser.parse_args() 

if hasattr(args, 'func'): 
    args.func(args) 

Phương thức set_default_subparser() là một phần của gói ruamel.std.argparse.

4

Điều này dường như thực hiện ý tưởng cơ bản của một trình tùy chọn con. Chúng tôi phân tích các đối số chuẩn áp dụng cho tất cả các tiểu ban. Sau đó, nếu bất cứ điều gì còn lại, chúng tôi gọi trình phân tích cú pháp trên phần còn lại. Các đối số chính là một phụ huynh của tiểu ban để cho -h xuất hiện đúng. Tôi có kế hoạch nhập một dấu nhắc tương tác nếu không có các lệnh con.

import argparse 

p1 = argparse.ArgumentParser(add_help = False)  
p1.add_argument(‘–flag1′) 

p2 = argparse.ArgumentParser(parents = [ p1 ]) 
s = p2.add_subparsers() 
p = s.add_parser(‘group’) 
p.set_defaults(group=True) 

(init_ns, remaining) = p1.parse_known_args() 

if remaining: 
    p2.parse_args(args = remaining, namespace=init_ns) 
else: 
    print(‘Enter interactive loop’) 

print(init_ns)