Argparse:如果存在“ x”,则必需的参数“ y”


118

我的要求如下:

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

对于参数prox,我使用action ='store_true'来检查它是否存在。我不需要任何论点。但是,如果设置了--prox,我也需要 rport和lport。有没有一种简单的方法可以使用argparse做到这一点,而无需编写自定义条件编码。

更多代码:

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

插入,但是我想提一下我的图书馆joffrey。例如,让您执行此问题想要的操作,而无需让您自己验证所有内容(如在接受的答案中)或依靠漏洞黑客(如投票第二高的响应)。
MI Wright

对于到达这里的任何人,另一个令人惊奇的解决方案:stackoverflow.com/a/44210638/6045800
Tomerikoo

Answers:


120

不,argparse中没有任何选项可以构成相互包含的选项集。

解决此问题的最简单方法是:

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

2
那就是我最终要做的
asudhak

20
谢谢你的parser.error方法,这就是我想要的!
MarSoft 2015年

7
您不应该使用“或”吗?毕竟您需要两个参数 if args.prox and (args.lport is None or args.rport is None):
yossiz74 '16

1
相反args.lport is None,您可以简单地使用not args.lport。我认为它有点Pythonic。
CGFoX

7
这将使您无法将--lport或设置--rport0,这可能是对该程序的有效输入。
出生

53

您是在说要有条件地要求参数。就像@borntyping所说的那样,您可以检查错误并执行parser.error(),或者可以应用与--prox添加新参数时相关的要求。

您的示例的简单解决方案可能是:

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)

这种方式required接收True还是False取决于用户是否使用过--prox。这也保证了-lport-rport相互之间的独立行为。


8
请注意,ArgumentParser可以使用来解析除以外的列表中的参数sys.argv,这种情况将失败。
BallpointBen

如果使用--prox=<value>语法,这也会失败。
fnkr

11

如果存在,如何使用parser.parse_known_args()method然后添加args --lport--rportargs --prox

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

还请记住,您可以提供opts第一次解析后生成的名称空间,而第二次解析其余参数。这样,最后,在完成所有解析之后,您将拥有一个包含所有选项的名称空间。

缺点:

  • 如果--prox不存在,则命名空间中甚至不存在其他两个从属选项。尽管根据您的用例(如果--prox不存在),则其他选项的发生无关紧要。
  • 需要修改用法消息,因为解析器不知道完整结构
  • --lport并且--rport不显示在帮助消息中

5

未设置lport时使用prox。如果不是,为什么不进行lport和的rport论证prox?例如

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

这样可以节省用户输入的时间。测试if args.prox is not None:和一样容易if args.prox:


1
为了使示例更完整,当nargs> 1时,将在解析的参数等中获得一个列表,您可以按通常的方式处理该列表。例如a,b = args.proxa = args.prox[0]等等
Dannid

1

接受的答案对我很有用!由于所有代码都未经测试就被破坏,这就是我测试接受答案的方式。parser.error()不会引发argparse.ArgumentError错误,而是退出该过程。您必须进行测试SystemExit

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

有单元测试

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

启发自:使用unittest测试argparse-退出错误

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.