Argparse - Как указать подкоманду по умолчанию - PullRequest
20 голосов
/ 03 марта 2011

Я использую пакет argparse в Python 2.7 для написания некоторой логики разбора опций для инструмента командной строки.Инструмент должен принимать один из следующих аргументов:

«ON»: включить функцию.«OFF»: выключить функцию.[Аргументы не предоставлены]: вывод текущего состояния функции.

Просмотр документации argparse заставил меня поверить, что я хотел определить две - возможно три - подкоманды, поскольку эти три состояния являются взаимоисключающими и представляют разные концептуальные действия.Это моя текущая попытка кода:

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.set_defaults(func=print_state) # I think this line is wrong.

parser_on = subparsers.add_parser('ON')
parser_on.set_defaults(func=set_state, newstate='ON')

parser_off = subparsers.add_parser('OFF')
parser_off.set_defaults(func=set_state, newstate='OFF')

args = parser.parse_args()

if(args.func == set_state):
    set_state(args.newstate)
elif(args.func == print_state):
    print_state()
else:
    args.func() # Catchall in case I add more functions later

У меня сложилось впечатление, что если бы я предоставил 0 аргументов, основной анализатор установил бы func=print_state, а если бы я предоставил 1 аргумент, основной анализаториспользуйте значения по умолчанию для соответствующей подкоманды и вызовите func=set_state.Вместо этого я получаю следующую ошибку с 0 аргументами:

usage: cvsSecure.py [-h] {ON,OFF} ...
cvsSecure.py: error: too few arguments

И если я предоставлю «OFF» или «ON», вместо 1011 * будет вызван print_state.Если я закомментирую строку parser.set_defaults, set_state будет вызван правильно.

Я программист уровня подмастерье, но я новичок в Python.Любые предложения о том, как я могу заставить это работать?

Редактировать : Еще одна причина, по которой я смотрел подкоманды, была потенциальная четвертая функция, которую я рассматриваю в будущем:

«FORCE txtval»: установить состояние функции на txtval.

Ответы [ 2 ]

12 голосов
/ 03 марта 2011

Значения по умолчанию синтаксического анализатора верхнего уровня переопределяют значения по умолчанию для подпарасеров, поэтому установка значения по умолчанию func в подпарасерах игнорируется, но значение newstate из значений по умолчанию подпарасераправильно.

Я не думаю, что вы хотите использовать подкоманды.Подкоманды используются, когда доступные опции и позиционные аргументы изменяются в зависимости от того, какая подкоманда выбрана.Однако у вас нет других опций или позиционных аргументов.

Следующий код, кажется, делает то, что вам требуется:

import argparse

def print_state():
    print "Print state"

def set_state(s):
    print "Setting state to " + s

parser = argparse.ArgumentParser()
parser.add_argument('state', choices = ['ON', 'OFF'], nargs='?')

args = parser.parse_args()

if args.state is None:
    print_state()
elif args.state in ('ON', 'OFF'):
    set_state(args.state)

Запишите необязательные параметры для parser.add_argument.Параметр "choices" указывает допустимые параметры, а для параметра "nargs" устанавливается значение "?"указывает, что 1 аргумент должен использоваться, если он доступен, в противном случае ни один из них не должен использоваться.

Редактировать: Если вы хотите добавить команду FORCE с аргументом и иметь отдельный текст справки для ON иКоманда OFF, тогда вам нужно использовать подкоманды.К сожалению, кажется, нет способа указать подкоманду по умолчанию.Однако вы можете обойти эту проблему, проверив пустой список аргументов и предоставив свой собственный.Вот пример кода, иллюстрирующий, что я имею в виду:

import argparse
import sys

def print_state(ignored):
    print "Print state"

def set_state(s):
    print "Setting state to " + s

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
on = subparsers.add_parser('ON', help = 'On help here.')
on.set_defaults(func = set_state, newstate = 'ON')
off = subparsers.add_parser('OFF', help = 'Off help here.')
off.set_defaults(func = set_state, newstate = 'OFF')
prt = subparsers.add_parser('PRINT')
prt.set_defaults(func = print_state, newstate = 'N/A')
force = subparsers.add_parser('FORCE' , help = 'Force help here.')
force.add_argument('newstate', choices = [ 'ON', 'OFF' ])
force.set_defaults(func = set_state)

if (len(sys.argv) < 2):
    args = parser.parse_args(['PRINT'])
else:
    args = parser.parse_args(sys.argv[1:])

args.func(args.newstate)
0 голосов
/ 15 октября 2014

В вашем подходе есть две проблемы.

Сначала вы, вероятно, уже заметили, что newstate не является каким-то под_значением подпарасера ​​и его необходимо устранить на верхнем уровне args как * 1005.*.Это должно объяснить, что присвоение значения по умолчанию newstate дважды приведет к перезаписи первого значения.Независимо от того, вызываете ли вы свою программу с «ON» или «OFF» в качестве параметра, каждый раз set_state() будет вызываться с OFF.Если вы просто хотите иметь возможность python cvsSecure ON и python cvsSecure OFF, сработало бы следующее:

from __future__ import print_function

import sys
import argparse

def set_state(state):
    print("set_state", state)

def do_on(args):
    set_state('ON')

def do_off(args):
    set_state('OFF')

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser_on = subparsers.add_parser('ON')
parser_on.set_defaults(func=do_on)
parser_on.add_argument('--fast', action='store_true')
parser_off = subparsers.add_parser('OFF')
parser_off.set_defaults(func=do_off)

args = parser.parse_args()
args.func(args)

Вторая проблема заключается в том, что argparse обрабатывает подпарасеры как аргументы с одним значением, поэтому вы должныукажите один перед вызовом parser.parse_args().Вы можете автоматизировать вставку недостающего аргумента, добавив дополнительный подпарсер 'PRINT' и автоматически вставив его, используя set_default_subparser, добавленный к argparse.ArgumentParser() (этот код является частью пакета ruamel.std.argparse

from __future__ import print_function

import sys
import argparse

def set_default_subparser(self, name, args=None):
    """default subparser selection. Call after setup, just before parse_args()
    name: is the name of the subparser to call by default
    args: if set is the argument list handed to parse_args()

    , tested with 2.7, 3.2, 3.3, 3.4
    it works with 2.6 assuming argparse is installed
    """
    subparser_found = False
    for arg in sys.argv[1:]:
        if arg in ['-h', '--help']:  # global help if no subparser
            break
    else:
        for x in self._subparsers._actions:
            if not isinstance(x, argparse._SubParsersAction):
                continue
            for sp_name in x._name_parser_map.keys():
                if sp_name in sys.argv[1:]:
                    subparser_found = True
        if not subparser_found:
            # insert default in first position, this implies no
            # global options without a sub_parsers specified
            if args is None:
                sys.argv.insert(1, name)
            else:
                args.insert(0, name)

argparse.ArgumentParser.set_default_subparser = set_default_subparser


def print_state(args):
    print("print_state")

def set_state(state):
    print("set_state", state)

def do_on(args):
    set_state('ON')

def do_off(args):
    set_state('OFF')

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser_print = subparsers.add_parser('PRINT', help='default action')
parser_print.set_defaults(func=print_state)
parser_on = subparsers.add_parser('ON')
parser_on.set_defaults(func=do_on)
parser_on.add_argument('--fast', action='store_true')
parser_off = subparsers.add_parser('OFF')
parser_off.set_defaults(func=do_off)

parser.set_default_subparser('PRINT')

args = parser.parse_args()
args.func(args)

Вам не нужно обрабатывать от args до do_on() и т. Д., Но это пригодится, если вы начнете указывать параметры для разных подпарсеров.

...