Use nargs='?' (or nargs='*' if you need more than one dir)
parser.add_argument('dir', nargs='?', default=os.getcwd())
extended example:
>>> import os, argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-v', action='store_true')
_StoreTrueAction(option_strings=['-v'], dest='v', nargs=0, const=True, default=False, type=None, choices=None, help=None, metavar=None)
>>> parser.add_argument('dir', nargs='?', default=os.getcwd())
_StoreAction(option_strings=[], dest='dir', nargs='?', const=None, default='/home/vinay', type=None, choices=None, help=None, metavar=None)
>>> parser.parse_args('somedir -v'.split())
Namespace(dir='somedir', v=True)
>>> parser.parse_args('-v'.split())
Namespace(dir='/home/vinay', v=True)
>>> parser.parse_args(''.split())
Namespace(dir='/home/vinay', v=False)
>>> parser.parse_args(['somedir'])
Namespace(dir='somedir', v=False)
>>> parser.parse_args('somedir -h -v'.split())
usage: [-h] [-v] [dir]
positional arguments:
dir
optional arguments:
-h, --help show this help message and exit
-v
Answer from Vinay Sajip on Stack OverflowUse nargs='?' (or nargs='*' if you need more than one dir)
parser.add_argument('dir', nargs='?', default=os.getcwd())
extended example:
>>> import os, argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-v', action='store_true')
_StoreTrueAction(option_strings=['-v'], dest='v', nargs=0, const=True, default=False, type=None, choices=None, help=None, metavar=None)
>>> parser.add_argument('dir', nargs='?', default=os.getcwd())
_StoreAction(option_strings=[], dest='dir', nargs='?', const=None, default='/home/vinay', type=None, choices=None, help=None, metavar=None)
>>> parser.parse_args('somedir -v'.split())
Namespace(dir='somedir', v=True)
>>> parser.parse_args('-v'.split())
Namespace(dir='/home/vinay', v=True)
>>> parser.parse_args(''.split())
Namespace(dir='/home/vinay', v=False)
>>> parser.parse_args(['somedir'])
Namespace(dir='somedir', v=False)
>>> parser.parse_args('somedir -h -v'.split())
usage: [-h] [-v] [dir]
positional arguments:
dir
optional arguments:
-h, --help show this help message and exit
-v
As an extension to @VinaySajip answer. There are additional nargs worth mentioning.
parser.add_argument('dir', nargs=1, default=os.getcwd())
N (an integer). N arguments from the command line will be gathered together into a list
parser.add_argument('dir', nargs='*', default=os.getcwd())
'*'. All command-line arguments present are gathered into a list. Note that it generally doesn't make much sense to have more than one positional argument with nargs='*', but multiple optional arguments with nargs='*' is possible.
parser.add_argument('dir', nargs='+', default=os.getcwd())
'+'. Just like '*', all command-line args present are gathered into a list. Additionally, an error message will be generated if there wasn’t at least one command-line argument present.
parser.add_argument('dir', nargs=argparse.REMAINDER, default=os.getcwd())
argparse.REMAINDER. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities
If the nargs keyword argument is not provided, the number of arguments consumed is determined by the action. Generally this means a single command-line argument will be consumed and a single item (not a list) will be produced.
Edit (copied from a comment by @Acumenus) nargs='?' The docs say: '?'. One argument will be consumed from the command line if possible and produced as a single item. If no command-line argument is present, the value from default will be produced.
Is it possible to use argparse to make optional positional arguments, and if not, what alternatives are available?
Cleaner way to do argparse with optional positional arguments and parse order
Argparse conditional optional positional arguments?
argparse: nargs='*' positional argument doesn't accept any items if preceded by an option and another positional
Videos
I am trying to use argparse to take arguments from user input, and I basically want them to either be able to process an infile into an output, or update the script resources. So if the user wants to use the script, they would do something like:
myscript.py "infile.txt" "path/to/output/dir"
or if they need to update, I'd like to have the option to just do the following and ignore the in/out arguments completely:
myscript.py --update
This seems like a very trivial thing but I'm not quite sure how to implement it, or if it's even possible using argparse. If it is not, what other options are available to do accomplish what I'd like to do? Thank you in advance!
You can parse the command line in 2 passes: The first pass is to parse --extra-info, the second will parse the rest.
#!/usr/bin/env python3
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument("--extra-info", action="store_true")
options, remainder = parser.parse_known_args()
if options.extra_info:
print("Show extra information")
sys.exit()
parser = argparse.ArgumentParser()
parser.add_argument("foo")
parser.add_argument("--bar")
# Add here for the help text only
parser.add_argument("--extra-info", action="store_true", help="blah")
options = parser.parse_args(remainder)
print(options)
Some interactions:
# Show help
$ ./main.py
usage: main.py [-h] [--bar BAR] [--extra-info] foo
main.py: error: the following arguments are required: foo
# Show extra info
$ ./main.py --extra-info
Show extra information
# specify foo/bar
$ ./main.py myfoo --bar mybar
Namespace(foo='myfoo', bar='mybar', extra_info=False)
Notes
In the first pass, I used
parse_known_argsjust to parse--extra-info. Theremainderholds those command-line arguments which the first-pass parser does not understand. We will use this remainder in the second pass.In the second pass, I added
--extra-infosimply for the help/usage.
Testing the version action class, in an ipython session.
I added RawTextHelpFormatter to allow for multiple lines in the version text.
In [125]: import argparse
...: parser = argparse.ArgumentParser(formatter_class=argparse.RawTex
...: tHelpFormatter)
...: txt = 'this is some extra info\nlets try a second line'
...: parser.add_argument('--extra-info', action='version', version=tx
...: t)
...: parser.add_argument('foo')
...: parser.add_argument('--bar');
with extra:
In [126]: parser.parse_args('--bar xyz fooarg --extra-info'.split())
this is some extra info
lets try a second line
An exception has occurred, use %tb to see the full traceback. <this is ipython exit capture>
without extra:
In [127]: parser.parse_args('--bar xyz fooarg'.split())
Out[127]: Namespace(foo='fooarg', bar='xyz')
Most Action subclasses have a __call__ method that writes something to the args namespace, and returns. The help and version classes print something, and issue a system exit, thus terminating all remaining parsing.
First, let me say that I have a working solution (below) but it seems a bit hacky. I am only moderately experienced with argparse so I hope someone here can give me a cleaner way to do this
Consider a program that takes 1 to any number of arguments but groups them into A, B, and C
-
0 args: Error
-
1 arg: Set
Bto the args,Cto None andAto empty list -
2 args: Set
BandCto args (respectively),Ato empty list -
3+ args: Set
BandCto last two args (respectively) and all preceding toA
All the while maintain the autoformatter to (at least roughly) show this in the help.
Here is my current workings solution. I hope there is something cleaner though:
parser = argparse.ArgumentParser()
parser.add_argument('A',nargs='*')
parser.add_argument('B',nargs=1)
parser.add_argument('C',nargs='?')
args = parser.parse_args(sys.argv[1:])
all_positional = args.A + args.B # args.C will always be empty
if len(all_positional) == 1:
args.A = []; args.B = all_positional[0]; args.C = None
else:
args.A = all_positional[0:-2] # will be empty if only 2
args.B = all_positional[-2]; args.C = all_positional[-1];
print(args)
Which shows the usage as:
usage: argtest.py [-h] [A [A ...]] B [C]
Is there a cleaner way?