Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python argparse ignore other options when a specific option is used

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.

like image 466
LexBailey Avatar asked Nov 22 '18 15:11

LexBailey


People also ask

How do you make an argument optional in Argparse?

To add an optional argument, simply omit the required parameter in add_argument() . args = parser. parse_args()if args.

What are mutually exclusive arguments?

In logic, two mutually exclusive propositions are propositions that logically cannot be true in the same sense at the same time. To say that more than two propositions are mutually exclusive, depending on the context, means that one cannot be true if the other one is true, or at least one of them cannot be true.

What does Nargs do in Argparse?

Number of Arguments If you want your parameters to accept a list of items you can specify nargs=n for how many arguments to accept. Note, if you set nargs=1 , it will return as a list not a single value.

What does Argparse ArgumentParser () do?

Python argparse The argparse module also automatically generates help and usage messages, and issues errors when users give the program invalid arguments. The argparse is a standard module; we do not need to install it. A parser is created with ArgumentParser and a new parameter is added with add_argument .


1 Answers

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()
like image 120
LexBailey Avatar answered Oct 21 '22 16:10

LexBailey