Jon Cage
Jon Cage

Reputation: 37488

How do I make an argparse argument optional when using subparsers?

I'm working on a simple Git/Redmine glue script but I'm having some difficulty using optional arguments with the Python argparse module.

With the following code:

import argparse

class MyClass:
    def StartWork(self, issueNumber, **kwargs):
        if issueNumber is None:
            issueNumber = input("please enter an issue number: ")
        else:
            print("issue number detected")
        print(issueNumber)

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='MyClass-command', help='Command to perform')
subparsers.required = True
startWorkParser = subparsers.add_parser('startwork', help='Command to begin work on a new branch')
startWorkParser.add_argument("issuenumber", type=int, help="The issue number used to create a local branch based on the specified issue number", nargs='?', default=None)
startWorkParser.set_defaults(func=MyClass.StartWork)

# Parse the arguments to make sure we have all the information requried to actually do something.
args = parser.parse_args()
mc = MyClass()

try:
    args.func(mc, **vars(args))
except AssertionError as e:
    print("Error: "+str(e))

# Parse the arguments to make sure we have all the information required to actually do something.
args = parser.parse_args()

I'd expect a call like this:

python MyClass.py startwork

...to result in the user being prompted for an issue number. Instead I get:

Traceback (most recent call last):
  File "C:\Projects\RedmnieGlue\MyClass.py", line 23, in <module>
    args.func(mc, **vars(args))
TypeError: StartWork() missing 1 required positional argument: 'issueNumber'

So why is the nargs='?' not prevailing here?

Edit

If I call it like this:

python MyClass.py startwork -h

I get this:

usage: class1.py startwork [-h] [issuenumber]

positional arguments:
  issuenumber  The issue number used to create a local branch based on the
               specified issue number

optional arguments:
  -h, --help   show this help message and exit

...which (based on the [] around issuenumber) suggests to me it is understanding that is an optional argument but something is preventing it from working as I'd expect it to. Something to do with my use of subparsers and calling methods with the arg parser perhaps?

Upvotes: 0

Views: 725

Answers (1)

poke
poke

Reputation: 387707

If you print the contents of vars(args) before your function call like this:

print(vars(args))
args.func(mc, **vars(args))

Then you can easily verify whether there is something wrong with the argument parser or not. With a call of the script without arguments (e.g. python myscript.py), you get the following output:

{'MyClass-command': 'startwork', 'issuenumber': None, 'func': <function MyClass.StartWork at 0x000000493898C510>}

As you can see issuenumber actually is in that dictionary, and it did get the default value. So the error you are seeing is not because of the argument parser (it’s also not an argparse error, so the validation on the arguments—with issuenumber being optional—is absolutely correct).

Instead, what’s going wrong is that the argument issuenumber is not passed to the positional argument when using **vars(args). The reason that does not happen is actually quite simply:

The dictionary key is issuenumber; the function expects a issueNumber (note the upper case N). So either change the function to use a lowercase issuenumber, or change the argument parser to store the value in issueNumber instead.

Upvotes: 2

Related Questions