简体   繁体   中英

Is there a way to disable positional arguments given that a flag is true with Python argparse?

I am building a command line tool which should work as follows:

mytool [-h] [-c|--config FILE] [-l|--list] ACTION

positional arguments:
  ACTION                the action to be performed

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -l, --list            show list of configured actions and exit
  -c, --config CONFIG   use CONFIG instead of the default configuration file 

I am using argparse to parse the command line and I am facing what seems to be a limitation of the library. Let me explain through some use-cases.

Use-case 1

$ mytool -h
$ mytool -c path/to/file -h
$ mytool -l -h
$ mytool some-action -h

All the above invocations of mytool shall print the help message and exit, exactly as it is shown above, more importantly showing ACTIONS to be mandatory.

Use-case 2

$ mytool -l
$ mytool -c path/to/file --list
$ mytool --list some-action
$ mytool --list --config path/to/file

All the above invocations must list the configured actions given the content of the configuration files, and exit. The allowed values of ACTION depend on the content of the configuration file, they are not simply hard-coded in the program.

Notice that even if an action is given, it is ignored because -l|--list has a higher precendance, similar to how -h works against other flags and arguments.

Also, please note that solutions such as this , which implement custom argparse.Action sub-classes won't work because the action of the listing flag needs the value of the configuration flag, so the parsing must complete (at least partially) for the listing to begin.

Finally, the absence of the otherwise required positional argument ACTION does not cause the parser to abort with an error.

Use-case 3 In the absence of -l|--list the parser works as expected:

$ mytool -c path/to/file # exits with error, positional argument missing
$ mytool some-action     # ok

In simple words, I am trying to make the -l|--list flag "disable" the mandatory enforcing of the positional argument ACTION . Moreover I am looking for a solution that allows me to perform the listing (the action of the -l|--list flag) after the parsing has (at least partially) completed, so the value of the -c|--config flag is available.

-h works the way it does because it uses a special action, argparse._HelpAction . (Simiarly, -v uses argparse._VersionAction .) This action causes the script to exit while parsing, so no folllowing arguments are processed. (Note that any side effects produced while parsing previous arguments may still occur; the only notion of precedence parse_args has is that arguments are processed from left to right.)

If you want -l to similarly show available actions and exit, you need to define your own custom action. For example,

class ListAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string):
        print("Allowable actions are...")
        print("foo")
        print("bar")
        print("baz")
        parser.exit()

p = argparse.ArgumentParser()
p.add_argument("-l", "--list", action=ListAction)
...
args = p.parse_args()

In particular, p.parse_args(["-l", "-h"]) will list actions without displaying the help message, and p.parse_args(["-h", "-l"]) will print the help message but not list actions. Which one gets processed and terminates your script depends solely on which one appears first in the argument list.

As shown in the usage, ACTION will be required, unless the user uses '-h' or '-v', which exit in the middle of parsing.

You could define ACTION as nargs='?' , or as a flagged argument, in which case it is optional.

I was going to suggest giving ACTION choices, which will then appear in the help. But if they depend on '--config' value that could be more awkward (though not impossible).

'-l' could have a custom action class that behaves like 'version', but prints the desired list and exits. But then you can't provide an action as well.

Creating arguments that depend on each other in some way is awkward, though not impossible. It easiest to handle interdependencies after parsing, when all arguments have been read. Keep in mind that argparse tries to accept arguments in any order, '-c' could be before, or after '-l', or Action.

In theory though your custom list action, could check the Namespace for a 'config' value, and base its action on that value. That would work only if you can count on the user providing '-c' before '-l'; argparse won't enforce that for you.

As a general rule it's best to think of argparse as a tool for finding out what the user wants, with limited assistance in policing that, and an ability to automatically format usage and help. Don't expect it to help with complicated interactions and logical mixes of arguments.

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