подкоманда python argparse с зависимостью и конфликтом - PullRequest
7 голосов
/ 02 марта 2011

Я хочу использовать argparse для создания инструмента с подкомандой. Возможный синтаксис может быть

/ tool.py скачать - с 1234 --интервал 60

/ tool.py загрузить --build 1432

/ tool.py clean --numbers 10

Итак, я хочу использовать argparse для реализации:

  1. убедитесь, что '--from' и '--interval' всегда используются вместе
  2. убедиться, что '--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 загрузить --interval 1234 --build 5678

не должно быть разрешено, потому что --build нельзя использовать с другим аргументом. Но мой код тоже это принимает.

Любое предложение будет высоко оценено. Спасибо.

1 Ответ

6 голосов
/ 02 марта 2011

Вы можете использовать пользовательские действия для этого:

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