Argparse: Đối số bắt buộc 'y' nếu có 'x'


118

Tôi có một yêu cầu như sau:

./xyifier --prox --lport lport --rport rport

đối với đối số prox, tôi sử dụng action = 'store_true' để kiểm tra xem nó có hiện diện hay không. Tôi không yêu cầu bất kỳ đối số nào. Nhưng, nếu --prox được thiết lập, tôi cũng yêu cầu rport và lport. Có cách nào dễ dàng để làm điều này với argparse mà không cần viết mã điều kiện tùy chỉnh không.

Thêm mã:

non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', type=int, help='Listen Port.')
non_int.add_argument('--rport', type=int, help='Proxy port.')

Đang cắm, nhưng tôi muốn đề cập đến joffrey thư viện của mình . Cho phép bạn làm những gì câu hỏi này muốn, chẳng hạn, mà không bắt bạn phải tự mình xác thực mọi thứ (như trong câu trả lời được chấp nhận) hoặc dựa vào một bản hack loopholey (như trong câu trả lời được bình chọn cao thứ hai).
MI Wright

Đối với bất kỳ ai đến đây, một giải pháp tuyệt vời khác: stackoverflow.com/a/44210638/6045800
Tomerikoo

Câu trả lời:


120

Không, không có bất kỳ tùy chọn nào trong argparse để đưa ra các nhóm tùy chọn bao gồm lẫn nhau .

Cách đơn giản nhất để giải quyết vấn đề này sẽ là:

if args.prox and (args.lport is None or args.rport is None):
    parser.error("--prox requires --lport and --rport.")

2
Thats những gì tôi đã kết thúc làm
asudhak

20
Cảm ơn bạn về parser.errorphương pháp, đây là những gì tôi đang tìm kiếm!
MarSoft

7
bạn không nên sử dụng 'hoặc'? sau khi tất cả các bạn yêu cầu cả hai args if args.prox and (args.lport is None or args.rport is None):
yossiz74

1
Thay vào đó args.lport is None, bạn có thể chỉ cần sử dụng not args.lport. Tôi nghĩ nó giống con trăn hơn một chút.
CGFoX

7
Điều đó sẽ ngăn bạn cài đặt --lporthoặc --rportđến 0, có thể là đầu vào hợp lệ cho chương trình.
borntyping

53

Bạn đang nói về việc có các đối số bắt buộc có điều kiện. Giống như @borntyping đã nói rằng bạn có thể kiểm tra lỗi và thực hiện parser.error()hoặc bạn có thể chỉ áp dụng một yêu cầu liên quan đến --proxthời điểm bạn thêm một đối số mới.

Một giải pháp đơn giản cho ví dụ của bạn có thể là:

non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', required='--prox' in sys.argv, type=int)
non_int.add_argument('--rport', required='--prox' in sys.argv, type=int)

Cách này requirednhận được một trong hai Truehoặc Falsetùy thuộc vào việc người dùng có được sử dụng hay không --prox. Điều này cũng đảm bảo điều đó -lport-rportcó một hành vi độc lập giữa nhau.


8
Lưu ý rằng ArgumentParsercó thể được sử dụng để phân tích cú pháp các đối số từ một danh sách khác sys.argv, trường hợp này sẽ không thành công.
BallpointBen

Ngoài ra, điều này sẽ không thành công nếu --prox=<value>cú pháp được sử dụng.
fnkr

11

Làm thế nào về việc sử dụng parser.parse_known_args()phương thức và sau đó thêm args --lport--rportargs theo yêu cầu nếu --proxcó.

# just add --prox arg now
non_int = argparse.ArgumentParser(description="stackoverflow question", 
                                  usage="%(prog)s [-h] [--prox --lport port --rport port]")
non_int.add_argument('--prox', action='store_true', 
                     help='Flag to turn on proxy, requires additional args lport and rport')
opts, rem_args = non_int.parse_known_args()
if opts.prox:
    non_int.add_argument('--lport', required=True, type=int, help='Listen Port.')
    non_int.add_argument('--rport', required=True, type=int, help='Proxy port.')
    # use options and namespace from first parsing
    non_int.parse_args(rem_args, namespace = opts)

Cũng nên nhớ rằng bạn có thể cung cấp không gian tên optsđược tạo sau lần phân tích cú pháp đầu tiên trong khi phân tích cú pháp các đối số còn lại lần thứ hai. Bằng cách đó, cuối cùng, sau khi tất cả quá trình phân tích cú pháp hoàn tất, bạn sẽ có một không gian tên duy nhất với tất cả các tùy chọn.

Hạn chế:

  • Nếu --proxkhông có, hai tùy chọn phụ thuộc khác thậm chí không có trong không gian tên. Mặc dù dựa trên trường hợp sử dụng của bạn, nếu --proxkhông có thì điều gì xảy ra với các tùy chọn khác là không liên quan.
  • Cần sửa đổi thông báo sử dụng vì trình phân tích cú pháp không biết cấu trúc đầy đủ
  • --lport--rportkhông hiển thị trong thông báo trợ giúp

5

Bạn có sử dụng lportkhi proxkhông được thiết lập. Nếu không, tại sao không thực hiện lportrportlập luận của prox? ví dụ

parser.add_argument('--prox', nargs=2, type=int, help='Prox: listen and proxy ports')

Điều đó giúp tiết kiệm việc nhập của người dùng. Nó chỉ là dễ dàng để kiểm tra if args.prox is not None:như if args.prox:.


1
Để hoàn thiện ví dụ, khi tường thuật của bạn> 1, bạn sẽ nhận được một danh sách trong đối số được phân tích cú pháp, v.v., bạn có thể giải quyết theo cách thông thường. Ví dụ a,b = args.prox, a = args.prox[0]vv
Dannid

1

Câu trả lời được chấp nhận làm việc rất tốt cho tôi! Vì tất cả mã bị hỏng mà không có bài kiểm tra, đây là cách tôi kiểm tra câu trả lời được chấp nhận. parser.error()không đưa ra argparse.ArgumentErrorlỗi thay vào đó nó sẽ thoát khỏi quy trình. Bạn phải kiểm tra cho SystemExit.

với pytest

import pytest
from . import parse_arguments  # code that rasises parse.error()


def test_args_parsed_raises_error():
    with pytest.raises(SystemExit):
        parse_arguments(["argument that raises error"])

với kỳ lân

from unittest import TestCase
from . import parse_arguments  # code that rasises parse.error()

class TestArgs(TestCase):

    def test_args_parsed_raises_error():
        with self.assertRaises(SystemExit) as cm:
            parse_arguments(["argument that raises error"])

lấy cảm hứng từ: Sử dụng đơn nhất để kiểm tra argparse - thoát lỗi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.