Use nargs='?' (or nargs='*' if you need more than one dir)
Copyparser.add_argument('dir', nargs='?', default=os.getcwd())
extended example:
Copy>>> 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)
Copyparser.add_argument('dir', nargs='?', default=os.getcwd())
extended example:
Copy>>> 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.
python - Is it better practice to set default values for optional argparse arguments? - Software Engineering Stack Exchange
argparse - checking for optional argument
You are not using argparse correctly - the arguments are not set on the parser, but on another object that is returned by the parse_args method.
What you should be doing is this:
parser.add_argument("-f", "--filename", help="set output filename")
args = parser.parse_args()
if args.filename is None:
...Few notes:
-
action="store" is the default, specifying it does nothing useful
-
You probably want to use parse_args instead of parse_known_args, because it is simpler to use and raising arror if unknown argument is specified is what you want
-
argparse documentation will tell you all of this and much more, it also has a lot of examples
How to set several required arguments into an optional group?
Multiple optional position args with argparse.ArgumentParser
Videos
The premise behind your question is mistaken.
>>> import argparse
>>> p = argparse.ArgumentParser()
>>> p.add_argument('-a')
>>> p.parse_args()
Namespace(a=None)
The -a argument is already optional by default (you don't need required=False), and its default is already None, ensuring that args.a exists without any manual intervention.
You have to opt-in to having no default at all.
>>> p.add_argument("-b", default=argparse.SUPPRESS)
>>> p.parse_args()
Namespace(a=None)
So yes, it probably is a good idea to have some default, and argparse acknowledges that by providing one.
So some clarifications here. To answer this question for python specifically, argparse does in fact default optional argument values to None. Your problem arises from having multiple subcommands that take different arguments, but go through the same code flow. If you pass the same args from subcommands a, b, c that have different options, then you do have to handle each option that is not common across all three. In that workflow, I would recommend backfilling each option that is not guaranteed to exist.
So I currently have the following code for processing arguments with argparse:
import argparse # Processing arguments
filename = ""
parser = argparse.ArgumentParser(description="Convert quantum circuits into different environments")
def main():
process_args()
set_output_filename()
def process_args():
parser.add_argument("input_format", help="input format of your script", choices=('projectq','qutip'))
parser.add_argument("output_format", help="output format of your script", choices=('projectq','qutip'))
parser.add_argument("-f", "--filename", help="set output filename", action="store")
parser.parse_known_args()
def set_output_filename():
if parser.filename is None:
print("No input filename!")
else:
print("Input filename!")Which, in theory, should check if the -f flag was used and do something different whether or not it was. However, I get the following:
PS C:\Users\hyper\Documents\dissertation> py .\convertqc.py qutip projectq --filename test Traceback (most recent call last): File ".\convertqc.py", line 35, in <module> main() File ".\convertqc.py", line 17, in main set_output_filename() File ".\convertqc.py", line 28, in set_output_filename if parser.filename is None: AttributeError: 'ArgumentParser' object has no attribute 'filename'
Based on some research the "if is None" check should be handling that? Not sure what's gone wrong?
Thanks in advance
You are not using argparse correctly - the arguments are not set on the parser, but on another object that is returned by the parse_args method.
What you should be doing is this:
parser.add_argument("-f", "--filename", help="set output filename")
args = parser.parse_args()
if args.filename is None:
...
Few notes:
-
action="store" is the default, specifying it does nothing useful
-
You probably want to use parse_args instead of parse_known_args, because it is simpler to use and raising arror if unknown argument is specified is what you want
-
argparse documentation will tell you all of this and much more, it also has a lot of examples
You can use print(getattr(parser, 'filename', None)) but it seems odd to me that argparse isn't populating a filename attribute with a safe default. Have you tried giving it a default? I'm not particularly familiar with the library, sorry.
I'll say that using getattr feels like a hack in this instance, but it's something you should be aware of.