简体   繁体   中英

How to use optional argparse argument and subparsers properly

I'm trying to write a little app that can do multiple things depending on arguments specified in argparse.

I use a positional argument (soundfiledir) for file directory which must always be specified, but after that I want to specify arguments based on what I want the app to perform. For example, the -A flag will run a specific set of jobs (python main.py [soundfile path] -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

On top of this, I also use subparsers. For example a subparser called fileconcatenator (python main.py [soundfile path] fileconcatenator) can be specified with some options (python main.py [soundfile path] 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.")

note: You might notice I have type set as positive_int, this is a user specified type programmed using

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}")

In main, I have things set up as following:

def main():

if args.all:
    
    do stuff

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

The problem is, when I run python main.py [soundfile path] -A , I get AttributeError: 'Namespace' object has no attribute 'autoconcat'

The program still runs (because the if args.autoconcat comes after the if args.all block), but I'd like to know what I'm doing wrong.

Any help greatly appreciated. I will amend the question if you find it unclear.

A quote from the Python argparse docs :

Note that the object returned by parse_args() will only contain attributes for the main parser and the subparser that was selected by the command line (and not any other subparsers). So in the example above, when the a command is specified, only the foo and bar attributes are present, and when the b command is specified, only the foo and baz attributes are present.

This is exactly your case: You are not invoking the programs subcommand fileconcatenator , so the args object will not contain the arguments of that subcommand, eg autoconcat . You have to check first which subcommand was called. This can be done by having an option common for all subcommands, which is not modifiable by the command-line user. It will be set for each subcommand separately, and when subcommand a is called, this argument will have value a, and when subcommand b is called the argument will have value b. This can be achieve by calling set_defaults on each subparser like this:

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

and then in main, check first if parser_name is fileconverter or fileconcatenator, and check for the arguments based on which subcommand was called.

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

You may have to call set_defaults(parser_name="main") on the main parser to make it work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM