0

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?

Jon Cage
  • 36,366
  • 38
  • 137
  • 215
  • I'm able to run this code without any errors. – Abhinav Upadhyay May 05 '16 at 11:40
  • 1
    (1) There is a syntax error in `def StartWork(…)` (missing trailing colon). (2) The error message does not match the shown code (e.g. there’s no `Main` function), so whatever is wrong with your code is not showing in your simplified version. – poke May 05 '16 at 11:46
  • It was an attempt to pare down the original code into a workable example. I'll fix it so it's a complete working solution... – Jon Cage May 05 '16 at 12:16
  • @poke - give that a shot now. – Jon Cage May 05 '16 at 12:23
  • Are you not mixing between the arguments given to your script and the ones the function StartWork get? Doesn't `def StartWork(self, issueNumber=None, **kwargs):` solve your issue? – zezollo May 05 '16 at 12:33

1 Answers1

2

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.

poke
  • 369,085
  • 72
  • 557
  • 602
  • Well spotted on the source of the error @poke and setting `issueNumber=None` in the definition of the `StartWork` method makes it work as expected once the case of the method matches that of the argument definition. – Jon Cage May 05 '16 at 12:41
  • Just one more thing to note; When setting up the name so it always matches what `StartWork` requires, this works very well: `inspect.getargspec(MyClass.StartWork).args[1]` – Jon Cage May 05 '16 at 12:57