argparse positional argument with nargs set to '?'

Question:

I want to make a list command that lists all items with:

python file.py list

I also want it to accept a filter query e.g. to list all items with the string "cat" in it would be:

python file.py list cat 

I have done the following to achieve this, which seems to work:

parser = argparse.ArgumentParser()
parser.add_argument("action", choices=["list"])
parser.add_argument("query",type=str,nargs='?')

I am curious to know why it is allowed to create a positional argument query, with the possibility of not suppling anything for it by using nargs=’?’? I mean, positional arguments have required=True by default and setting required=False is not allowed.

Asked By: Bloop

||

Answers:

Don’t get too focused on the "required". The fundamental distinction in argparse (other POSIX style parsers) is between "flagged" arguments, and "positionals". "flagged", also commonly called "optionals", take some sort of flag string, commonly prefixed with a dash or two (though this can be changed). A "positional" is "identified" by position, that is word order. Some parsers require a specific order, either all options first, or all after, the positionals. argparse shows positionals after optionals in the usage, but tries, where possible, to handle any order.

argparse also provides a required parameter for flagged arguments, and allows nargs like ‘?’, ‘+’, ‘*’, ‘…’, that handle a variable number of arguments. And some flagged ones don’t take any argument (True/False).

Help group names like "required", "optional", "positional" don’t reflect all possibilites, so don’t take them too literally.

A simple script that takes two file names, and one or more control values might look something like this:

In [1]: import argparse    
In [2]: parser = argparse.ArgumentParser()
   ...: parser.add_argument('-f','--foo')
   ...: parser.add_argument('input')
   ...: parser.add_argument('output', nargs='?', default='outfile')

‘[]’ are used in usage to give a hit as to which strings are required, and which may be omitted. If I defined a ‘-g’ with required=True, it would still be grouped with optional, since it is not a positional.

In [3]: parser.print_help()
usage: ipykernel_launcher.py [-h] [-f FOO] input [output]

positional arguments:
  input
  output

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

Sample runs:

In [4]: parser.parse_args([])
usage: ipykernel_launcher.py [-h] [-f FOO] input [output]
ipykernel_launcher.py: error: the following arguments are required: input
...
  
In [5]: parser.parse_args('in'.split())
Out[5]: Namespace(foo=None, input='in', output='outfile')

In [6]: parser.parse_args('in out -f foobar'.split())
Out[6]: Namespace(foo='foobar', input='in', output='out')

All defined arguments can be seen in the _actions list, and each has a ‘required’ attribute:

In [8]: parser._actions
Out[8]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
 _StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None),
 _StoreAction(option_strings=[], dest='input', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None),
 _StoreAction(option_strings=[], dest='output', nargs='?', const=None, default='outfile', type=None, choices=None, help=None, metavar=None)]

In [9]: [a.required for a in parser._actions]
Out[9]: [False, False, True, False]

The output positional is not required, because of its nargs value.

Answered By: hpaulj
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.