python argparse ignore other options when a specific option is used

我与影子孤独终老i 提交于 2021-01-20 08:36:36

问题


I am writing a python program that I want to have a command line interface that behaves in a particular way

The command line interface should accept the following invocations:

my_prog test.svg foo
my_prog --font=Sans test.svg foo

(it will generate an svg with the word foo written in the specified or default font)

Now I want to be able to also have this command accept the following invocation...

my_prog --list-fonts

which will list all of the valid options to --font as determined by the fonts available on the system.

I am using argparse, and I have something like this:

parser = argparse.ArgumentParser()

parser.add_argument('output_file')
parser.add_argument('text')
parser.add_argument('--font', help='list options with --list-fonts')
parser.add_argument('--list-fonts', action='store_true')

args = parser.parse_args()

however this does not make the --list-fonts option behave as I would like as the two positional arguments are still required.

I have also tried using subparsers, but these still need a workaround to prevent the other options being required every time.

How do I get the desired behaviour with argparse.


回答1:


argparse allows you to define arbitrary actions to take when encountering an argument, based on the action keyword argument to add_argument (see the docs)

You can define an action to list your fonts and then abort argument parsing, which will avoid checking for the other required arguments.

this could look like this:

class ListFonts(argparse.Action):
    def __call__(self, parser, namespace, values, option_string):
        print("list of fonts here")
        parser.exit() # exits the program with no more arg parsing and checking

Then you can add it to your argument like so:

parser.add_argument('--list-fonts', nargs=0, action=ListFonts)

Note nargs=0 has been added so that this argument doesn't require a value (the code in the question achieved this with action='store_true')

This solution has a side-effect of enabling the invocations like the following to also list the fonts and exits without running the main program:

my_prog --font Sans test.svg text --list-fonts

This is likely not a problem as it's not a typical use case, especially if the help text explains this behaviour.

If defining a new class for each such option feels too heavyweight, or perhaps you have more than one option that has this behaviour, then you could consider having a function that implements the desired action for each argument and then have a kind of factory function that returns a class that wraps the function. A complete example of this is shown below.

def list_fonts():
    print("list of fonts here")

def override(func):
    """ returns an argparse action that stops parsing and calls a function
    whenever a particular argument is encountered. The program is then exited """
    class OverrideAction(argparse.Action):
        def __call__(self, parser, namespace, values, option_string):
            func()
            parser.exit()
    return OverrideAction

parser = argparse.ArgumentParser()

parser.add_argument('output_file')
parser.add_argument('text')
parser.add_argument('--font', help='list options with --list-fonts')
parser.add_argument('--list-fonts', nargs=0, action=override(list_fonts),
    help='list the font options then stop, don\'t generate output')
args = parser.parse_args()


来源:https://stackoverflow.com/questions/53434478/python-argparse-ignore-other-options-when-a-specific-option-is-used

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!