2011-12-14 36 views
5

Thực thi các tiểu trình con "lồng nhau" bằng Python với cmdln.Tôi nên triển khai các tiểu trình con "lồng nhau" bằng Python như thế nào?

Tôi không chắc mình đang sử dụng đúng thuật ngữ ở đây. Tôi đang cố gắng để thực hiện một công cụ dòng lệnh sử dụng cmdln cho phép các lệnh con "lồng nhau". Đây là ví dụ về thế giới thực:

git svn rebase 

Cách tốt nhất để thực hiện điều này là gì? Tôi đã tìm kiếm thêm thông tin về điều này trong tài liệu, ở đây và trên web, nhưng đã trở nên trống rỗng. (Có lẽ tôi đang tìm kiếm với các điều khoản sai.)

Viết tắt của một tính năng không có giấy tờ thực hiện điều này một cách tự động, suy nghĩ ban đầu của tôi là có trình xử lý phổ biến trước đó xác định rằng có một tiểu ban khác và gửi lại điều phối lệnh. Tôi đã nhìn vào bên trong của cmdln mặc dù và dispatcher là một phương pháp riêng, _dispatch_cmd. Suy nghĩ tiếp theo của tôi là tạo ra điều phối viên phụ phụ của riêng tôi, nhưng điều đó có vẻ ít lý tưởng và lộn xộn hơn.

Mọi trợ giúp sẽ được đánh giá cao.

Trả lời

5

argparse làm cho các lệnh phụ rất dễ dàng.

+1

Công ty tôi đang làm việc cho có v2.6 ban đầu để sử dụng argparse là một vấn đề ở chỗ nó sẽ phải được đưa vào như một thư viện bên ngoài và chỉ được nạp nếu cần thiết. Từ xa không thể, chỉ là không lý tưởng. Đối với thư viện cmdln, cung cấp cho tôi một chút chức năng cơ bản mà tôi không muốn tạo lại. Điều đó nói rằng tôi phản đối việc sử dụng cái gì khác. – tima

4

Tôi cảm thấy như có một chút hạn chế với sub_parsers trong argparse, nếu nói, bạn có một bộ công cụ có thể có các tùy chọn tương tự có thể trải rộng trên các cấp độ khác nhau. Có thể hiếm khi có tình huống này, nhưng nếu bạn đang viết mã có thể cắm/mô-đun, nó có thể xảy ra.

Tôi có ví dụ sau. Nó là xa vời và không được giải thích vào lúc này bởi vì nó là khá muộn, nhưng ở đây nó đi:

Usage: tool [-y] {a, b} 
    a [-x] {create, delete} 
    create [-x] 
    delete [-y] 
    b [-y] {push, pull} 
    push [-x] 
    pull [-x] 
from argparse import ArgumentParser 

parser = ArgumentParser() 
parser.add_argument('-x', action = 'store_true') 
parser.add_argument('-y', action = 'store_true') 

subparsers = parser.add_subparsers(dest = 'command') 

parser_a = subparsers.add_parser('a') 
parser_a.add_argument('-x', action = 'store_true') 
subparsers_a = parser_a.add_subparsers(dest = 'sub_command') 
parser_a_create = subparsers_a.add_parser('create') 
parser_a_create.add_argument('-x', action = 'store_true') 
parser_a_delete = subparsers_a.add_parser('delete') 
parser_a_delete.add_argument('-y', action = 'store_true') 

parser_b = subparsers.add_parser('b') 
parser_b.add_argument('-y', action = 'store_true') 
subparsers_b = parser_b.add_subparsers(dest = 'sub_command') 
parser_b_create = subparsers_b.add_parser('push') 
parser_b_create.add_argument('-x', action = 'store_true') 
parser_b_delete = subparsers_b.add_parser('pull') 
parser_b_delete.add_argument('-y', action = 'store_true') 

print parser.parse_args(['-x', 'a', 'create']) 
print parser.parse_args(['a', 'create', '-x']) 
print parser.parse_args(['b', '-y', 'pull', '-y']) 
print parser.parse_args(['-x', 'b', '-y', 'push', '-x'])

Output

Namespace(command='a', sub_command='create', x=True, y=False) 
Namespace(command='a', sub_command='create', x=True, y=False) 
Namespace(command='b', sub_command='pull', x=False, y=True) 
Namespace(command='b', sub_command='push', x=True, y=True)

Như bạn có thể thấy, rất khó để phân biệt nơi dọc theo chuỗi, mỗi đối số được thiết lập. Bạn có thể giải quyết vấn đề này bằng cách thay đổi tên cho mỗi biến. Ví dụ: bạn có thể đặt 'dest' thành 'x', 'a_x', 'a_create_x', 'b_push_x', v.v., nhưng điều đó sẽ gây khó khăn và khó phân tách.

Một giải pháp thay thế là dừng ArgumentParser khi nó đạt đến một tiểu ban và chuyển các đối số còn lại sang một trình phân tích cú pháp độc lập khác, để nó có thể tạo ra các đối tượng riêng biệt. Bạn có thể cố gắng đạt được điều đó bằng cách sử dụng 'parse_known_args()' và không xác định đối số cho mỗi tiểu nhóm. Tuy nhiên, điều đó sẽ không tốt vì bất kỳ đối số chưa được phân tích cú pháp nào từ trước sẽ vẫn ở đó và có thể gây nhầm lẫn cho chương trình.

Tôi cảm thấy một giải pháp hơi rẻ nhưng hữu ích là để argparse giải thích các đối số sau dưới dạng chuỗi trong danh sách. Điều này có thể được thực hiện bằng cách thiết lập tiền tố cho một null-terminator '\ 0' (hoặc một số khác 'khó sử dụng' nhân vật) - nếu tiền tố là trống rỗng, mã sẽ ném một lỗi, ít nhất là trong Python 2,7. 3.

Ví dụ:

parser = ArgumentParser() 
parser.add_argument('-x', action = 'store_true') 
parser.add_argument('-y', action = 'store_true') 
subparsers = parser.add_subparsers(dest = 'command') 
parser_a = subparsers.add_parser('a' prefix_chars = '\0') 
parser_a.add_argument('args', type = str, nargs = '*') 

print parser.parse_args(['-xy', 'a', '-y', '12'])

Output:

Namespace(args=['-y', '12'], command='a', x=True, y=True) 

Lưu ý rằng nó không tiêu thụ -y tùy chọn thứ hai. Sau đó, bạn có thể chuyển kết quả 'args' sang một ArgumentParser khác.

Nhược điểm:

  • Trợ giúp có thể không được xử lý tốt. Sẽ phải thực hiện một số giải pháp khác với các lỗi này
  • Lỗi gặp phải có thể khó theo dõi và yêu cầu một số nỗ lực bổ sung để đảm bảo các thông báo lỗi được ghép đúng cách.
  • Chi phí đầu tư ít hơn một chút được kết hợp với nhiều ArgumentParsers.

Nếu có ai có thêm thông tin về điều này, vui lòng cho tôi biết.

5

Đến bữa tiệc ở đây, nhưng tôi đã phải làm điều này một chút và đã tìm thấy argparse khá clunky để làm điều này với. Điều này thúc đẩy tôi viết một phần mở rộng cho argparse được gọi là arghandler, trong đó có sự hỗ trợ rõ ràng cho việc tạo ra này có thể thực hiện các lệnh con với các dòng mã cơ bản bằng không.

Dưới đây là một ví dụ:

from arghandler import * 

@subcmd 
def push(context,args): 
    print 'command: push' 

@subcmd 
def pull(context,args): 
    print 'command: pull' 

# run the command - which will gather up all the subcommands 
handler = ArgumentHandler() 
handler.run() 
Các vấn đề liên quan