简体   繁体   English

如何正确使用可选的 argparse 参数和子解析器

[英]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.我正在尝试编写一个小应用程序,它可以根据 argparse 中指定的 arguments 执行多项操作。

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.我对必须始终指定的文件目录使用位置参数 (soundfiledir),但之后我想根据我希望应用程序执行的内容指定 arguments。 For example, the -A flag will run a specific set of jobs (python main.py [soundfile path] -A)例如,-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

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)例如,可以使用一些选项(python main.py [soundfile path] fileconcatenator -a 15)指定名为 fileconcatenator (python main.py [soundfile path] fileconcatenator) 的子解析器

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注意:您可能会注意到我将类型设置为 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}")

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'问题是,当我运行python main.py [soundfile path] -A时,我得到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.该程序仍在运行(因为 if args.autoconcat 出现在 if args.all 块之后),但我想知道我做错了什么。

Any help greatly appreciated.非常感谢任何帮助。 I will amend the question if you find it unclear.如果您觉得不清楚,我会修改问题。

A quote from the Python argparse docs :来自 Python argparse 文档的引用:

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).请注意,由 parse_args() 返回的 object 将仅包含由命令行选择的主解析器和子解析器(而不是任何其他子解析器)的属性。 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.所以上例中,指定a命令时,只存在foo和bar属性,指定b命令时,只存在foo和baz属性。

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 .这正是您的情况:您没有调用程序子命令fileconcatenator ,因此args object 将不包含该子命令的 arguments ,例如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.它将为每个子命令单独设置,当子命令 a 被调用时,该参数的值为 a,而当子命令 b 被调用时,该参数的值为 b。 This can be achieve by calling set_defaults on each subparser like this:这可以通过在每个子解析器上调用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

and then in main, check first if parser_name is fileconverter or fileconcatenator, and check for the arguments based on which subcommand was called.然后在 main 中,首先检查parser_name是 fileconverter 还是 fileconcatenator,然后根据调用的子命令检查 arguments。

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.您可能必须在主解析器上调用set_defaults(parser_name="main")才能使其工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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