142

我有如下要求:

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

6 回答 6

156

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

处理这个问题的最简单方法是:

if args.prox and (args.lport is None or args.rport is None):
    parser.error("--prox requires --lport and --rport.")
于 2013-10-16T22:11:20.213 回答
68

你说的是有条件需要的参数。就像@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接收TrueFalse取决于用户是否使用--prox。这也保证了彼此之间具有独立的行为-lport-rport

于 2017-05-26T23:28:56.610 回答
16

如果存在,如何使用parser.parse_known_args()方法,然后根据需要的 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不会出现在帮助信息中
于 2017-06-14T00:55:54.797 回答
7

未设置lport时使用。prox如果不是,为什么不提出lportrport争论prox?例如

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

这可以节省您的用户打字。if args.prox is not None:它就像测试一样容易if args.prox:

于 2013-10-17T01:42:43.223 回答
2

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

于 2019-08-06T01:16:05.223 回答
0

我是这样做的:

if t or x or y:
    assert t and x and y, f"args: -t, -x and -y should be given together"
于 2021-01-29T05:56:12.083 回答