具有依赖项和冲突的python argparse子命令

具有依赖项和冲突的python argparse子命令,python,argparse,Python,Argparse,我想使用argparse构建一个带有子命令的工具。可能的语法可能是 /tool.py下载--从1234开始--间隔60 /tool.py下载--构建1432 /tool.py clean--数字10 所以我想使用argparse来实现: 确保始终同时使用“--from”和“--interval” 确保“--build”从未与其他参数一起使用 但是我没有找到一种方法将“--from”和“--internal”配对到一个组,然后使该组与“--build”相互排斥 下面是我当前的代码,它只使'--fr

我想使用argparse构建一个带有子命令的工具。可能的语法可能是

/tool.py下载--从1234开始--间隔60

/tool.py下载--构建1432

/tool.py clean--数字10

所以我想使用argparse来实现:

  • 确保始终同时使用“--from”和“--interval”
  • 确保“--build”从未与其他参数一起使用
  • 但是我没有找到一种方法将“--from”和“--internal”配对到一个组,然后使该组与“--build”相互排斥

    下面是我当前的代码,它只使'--from'和'--build'互斥。既不能确保“--from”和“--interval”组合在一起,也不能确保“--interval”和“--build”是互斥的

    parser = argparse.ArgumentParser(description='A Tool')
    subparsers = parser.add_subparsers(help='sub-command help')
    
    #create the parser for the 'download' command
    download_parser = subparsers.add_parser('download', help='download help')
    download_parser.add_argument('--interval', dest='interval', type=int,help='interval help')
    group = download_parser.add_mutually_exclusive_group()
    group.add_argument('--from',type=int, help='from help')
    group.add_argument('--build', type=int, help='interval help')
    
    比如说,

    /tool.py下载--来自1234

    不应被允许,因为“--from”必须与“--interval”一起使用。但是我的代码默默地接受它

    /tool.py下载--间隔1234--构建5678

    不应被允许,因为“--build”不能与其他参数一起使用。但我的代码也接受它

    如有任何建议,将不胜感激。谢谢。

    您可以使用:

    import argparse
    import sys
    
    
    class VerifyNoBuild(argparse.Action):
        def __call__(self, parser, args, values, option_string=None):
            # print 'No: {n} {v} {o}'.format(n=args, v=values, o=option_string)
            if args.build is not None:
                parser.error(
                    '--build should not be used with --from or --interval')
            setattr(args, self.dest, values)
    
    
    class VerifyOnlyBuild(argparse.Action):
        def __call__(self, parser, args, values, option_string=None):
            # print 'Only: {n} {v} {o}'.format(n=args, v=values, o=option_string)
            if getattr(args, 'from') is not None:
                parser.error('--from should not be used with --build')
            if getattr(args, 'interval') is not None:
                parser.error('--interval should not be used with --build')
            setattr(args, self.dest, values)
    
    parser = argparse.ArgumentParser(description='A Tool')
    subparsers = parser.add_subparsers(help='sub-command help')
    
    # create the parser for the 'download' command
    download_parser = subparsers.add_parser('download', help='download help')
    
    download_parser.add_argument('--interval',
                                 type=int, help='interval help',
                                 action=VerifyNoBuild)
    download_parser.add_argument('--from',
                                 type=int, action=VerifyNoBuild)
    download_parser.add_argument('--build',
                                 type=int, action=VerifyOnlyBuild)
    
    args = parser.parse_args('download --from 1234 --interval 60'.split())
    print(args)
    # Namespace(build=None, from=1234, interval=60)
    
    args = parser.parse_args('download --build 1432'.split())
    print(args)
    # Namespace(build=1432, from=None, interval=None)
    
    args = parser.parse_args('download --build 1432 --from 1234'.split())
    print(args)
    # usage: test.py download [-h] [--interval INTERVAL] [--from FROM] [--build BUILD]
    # test.py download: error: --build should not be used with --from or --interval
    
    args = parser.parse_args('download --build 1432 --interval 60'.split())
    print(args)
    # usage: test.py download [-h] [--interval INTERVAL] [--from FROM] [--build BUILD]
    # test.py download: error: --build should not be used with --from or --interval
    
    但实际上,我认为这更简短:

    def parse_options():
        parser = argparse.ArgumentParser(description='A Tool')
        subparsers = parser.add_subparsers(help='sub-command help')
    
        #create the parser for the 'download' command
        download_parser = subparsers.add_parser('download', help='download help')
        download_parser.add_argument('--interval', type=int, help='interval help')
        download_parser.add_argument('--from', type=int)
        download_parser.add_argument('--build', type=int)
    
        opt=parser.parse_args()
        from_interval=[getattr(opt,key) is not None for key in ('from','interval')]
        if opt.build is not None:
            if any(from_interval):
                sys.exit('error!')
        elif not all(from_interval):
            sys.exit('error!')
        return opt
    

    你有代码,但到底是什么不起作用?举一个错误行为的例子,并解释你的预期。我添加了两个错误行为的例子。谢谢你的建议。自定义操作非常有用。我修改了您的代码以确保--from和--interval始终一起使用。它工作得很好。谢谢!:)@兰蒂:啊,是的,我忘了那种情况。您是否在
    VerifyNoBuild
    类中验证了该条件?如果是这样,请发布您的解决方案好吗?我想看看是怎么做到的。我已经修改了替代解决方案来处理这种情况。很抱歉响应太晚。实际上我的代码很简单。正如您所说,我修改了VerifyNoBuild类,在调用setattr()之前添加了以下行:
    if(args.begin为None)或(args.interval为None):sys.exit('--from和--interval必须一起使用')
    @Landy:您确定这样做有效吗?
    VerifyNoBuild
    操作通常被调用两次(当同时提供了
    --from
    --interval
    时)。第一次调用VerifyNoBuild.\uuu调用时,
    getattr(args,'from')
    getattr(args,'interval')
    将是
    None
    。这将导致调用
    sys.exit
    ,即使提供了两个参数。您是对的。我只是测试了不可接受的论点,没有尝试可接受的论点(((