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

我的要求如下:

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

I have a requirement as follows:

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

for the argument prox , I use action=’store_true’ to check if it is present or not. I do not require any of the arguments. But, if –prox is set I require rport and lport as well. Is there an easy way of doing this with argparse without writing custom conditional coding.

More Code:

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

回答 0

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

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

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

No, there isn’t any option in argparse to make mutually inclusive sets of options.

The simplest way to deal with this would be:

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

回答 1

您是在说要有条件地要求参数。就像@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相互之间的独立行为。

You’re talking about having conditionally required arguments. Like @borntyping said you could check for the error and do parser.error(), or you could just apply a requirement related to --prox when you add a new argument.

A simple solution for your example could be:

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)

This way required receives either True or False depending on whether the user as used --prox. This also guarantees that -lport and -rport have an independent behavior between each other.


回答 2

如果存在,如何使用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不显示在帮助消息中

How about using method and then adding the --lport and --rport args as required args if --prox is present.

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

Also keep in mind that you can supply the namespace opts generated after the first parsing while parsing the remaining arguments the second time. That way, in the the end, after all the parsing is done, you’ll have a single namespace with all the options.

Drawbacks:

  • If --prox is not present the other two dependent options aren’t even present in the namespace. Although based on your use-case, if --prox is not present, what happens to the other options is irrelevant.
  • Need to modify usage message as parser doesn’t know full structure
  • --lport and --rport don’t show up in help message

回答 3

未设置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:

Do you use lport when prox is not set. If not, why not make lport and rport arguments of prox? e.g.

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

That saves your users typing. It is just as easy to test if args.prox is not None: as if args.prox:.


回答 4

接受的答案对我很有用!由于所有代码都未经测试就被破坏,这就是我测试接受答案的方式。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-退出错误

The accepted answer worked great for me! Since all code is broken without tests here is how I tested the accepted answer. parser.error() does not raise an argparse.ArgumentError error it instead exits the process. You have to test for SystemExit.

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

with unittests

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

inspired from: Using unittest to test argparse – exit errors


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。