Как правильно использовать необязательный аргумент argparse и подразделы

#python #python-3.x #argparse

#python #python-3.x #argparse

Вопрос:

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

Я использую позиционный аргумент (soundfiledir) для каталога файлов, который всегда должен быть указан, но после этого я хочу указать аргументы, основанные на том, что я хочу, чтобы приложение выполняло. Например, флаг -A будет запускать определенный набор заданий (python main.py [путь к звуковому файлу] -A)

 parser = argparse.ArgumentParser()

parser.add_argument('soundfiledir', type=soundfiledir_format, help = "Specify soundfile directory") #positional argument. must always be provided.
parser.add_argument('-A', '--all', action = "store_true", help = "If this flag is specified, the program will transcribe all the sound files in the sound file directory (with timestamps), and will automatically concatenate files recorded close in time")

if args.all: 
does stuff
 

Кроме того, я также использую подразделы. Например, подраздел с именем fileconcatenator (python main.py [путь к звуковому файлу] fileconcatenator) можно указать с помощью некоторых параметров (python main.py [путь к звуковому файлу] fileconcatenator -a 15)

 subparser = parser.add_subparsers(dest = 'command')

fileconcatenator_parser = subparser.add_parser('fileconcatenator', help = "Concatenates sound files together")
group1 = fileconcatenator_parser.add_mutually_exclusive_group(required=True)
group1.add_argument('-a','--autoconcat',type = positive_int,  nargs = "?", const = 3, default = None, 
    help="Concatenate audio files recorded close in time. By default any file recorded within 3mns of each other.")
group1.add_argument('-m', '--manconcat', type = list, default = [], 
    help = "Concatenate audio files specified as a list.")

fileconverter_parser = subparser.add_parser('fileconverter',help = "Converts files to 16kHz mono wav")
fileconverter_parser.add_argument('-f', '--filestoconvert', type = list, required=True, default = ["All"], 
    help = "Specify which files to convert.")
 

примечание: Вы можете заметить, что у меня установлен тип как positive_int, это пользовательский тип, запрограммированный с помощью

 def positive_int(s):
    try:
        value = int(s)
        return int(s)
    except ValueError:
        raise argparse.ArgumentTypeError(f"Expected positive integer got {s!r}")
    if value <= 0:
        raise argparse.ArgumentTypeError(f"Expected positive integer got {s!r}")
 

В основном, у меня все настроено следующим образом:

 def main():

if args.all:
    
    do stuff

if args.autoconcat is None:
    pass
else:
   do stuff
 

Проблема в том, что когда я запускаю python main.py [путь к звуковому файлу] -A, я получаю AttributeError: объект ‘Namespace’ не имеет атрибута ‘autoconcat’

Программа все еще выполняется (потому что if args.autoconcat идет после if args .весь блок), но я хотел бы знать, что я делаю не так.

Любая помощь с благодарностью. Я изменю вопрос, если вы сочтете его неясным.

Ответ №1:

Цитата из документации Python argparse:

Обратите внимание, что объект, возвращаемый parse_args(), будет содержать только атрибуты для основного анализатора и подпараметра, выбранного в командной строке (а не для любых других подпараметров). Итак, в приведенном выше примере, когда указана команда a, присутствуют только атрибуты foo и bar, а когда указана команда b, присутствуют только атрибуты foo и baz.

Это именно ваш случай: вы не вызываете подкоманду programs fileconcatenator , поэтому args объект не будет содержать аргументы этой подкоманды, например autoconcat . Сначала вы должны проверить, какая подкоманда была вызвана. Это можно сделать, используя параметр, общий для всех подкоманд, который не может быть изменен пользователем командной строки. Он будет установлен для каждой подкоманды отдельно, и при вызове подкоманды a этот аргумент будет иметь значение a, а при вызове подкоманды b аргумент будет иметь значение b. Этого можно достичь, вызывая set_defaults каждый подпараметр следующим образом:

 fileconcatenator_parser = subparser.add_parser('fileconcatenator', help = "Concatenates sound files together")
fileconcatenator_parser.set_defaults(parser_name="fileconcatenator")
# adding some arguments here

fileconverter_parser = subparser.add_parser('fileconverter',help = "Converts files to 16kHz mono wav")
fileconverter_parser.set_defaults(parser_name="fileconverter")
#adding some arguments here
 

а затем в main сначала проверьте, является ли parser_name fileconverter или fileconcatenator , и проверьте аргументы, на основе которых была вызвана подкоманда.

 def main():
    args = parser.parse_args()
    if args.parser_name == "fileconverter":
        # do something with args.filestoconvert
    elif args.parser_name == "fileconcatenator":
        if args.autoconcat is None:
             pass
        else:
             # do something
 

Возможно, вам придется вызвать set_defaults(parser_name="main") основной анализатор
, чтобы заставить его работать.