501

How do I add an optional flag to my command line args?

eg. so I can write

python myprog.py 

or

python myprog.py -w

I tried

parser.add_argument('-w')

But I just get an error message saying

Usage [-w W]
error: argument -w: expected one argument

which I take it means that it wants an argument value for the -w option. What's the way of just accepting a flag?

I'm finding http://docs.python.org/library/argparse.html rather opaque on this question.

interstar
  • 26,048
  • 36
  • 112
  • 180
  • 4
    If you just want 1 flag to your script, sys.argv would be a whole lot easier. Unless your specifically trying to learn argparse, which is a good because its a handy module to know. – chown Nov 24 '11 at 15:08
  • 17
    Even after I know the answer now I don't see how I could have understood it from the documentation. – Andreas Haferburg Jul 30 '19 at 12:40

5 Answers5

816

As you have it, the argument w is expecting a value after -w on the command line. If you are just looking to flip a switch by setting a variable True or False, have a look here (specifically store_true and store_false)

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-w', action='store_true')

where action='store_true' implies default=False.

Conversely, you could haveaction='store_false', which implies default=True.

Jdog
  • 10,071
  • 4
  • 25
  • 42
78

Adding a quick snippet to have it ready to execute:

Source: myparser.py

import argparse
parser = argparse.ArgumentParser(description="Flip a switch by setting a flag")
parser.add_argument('-w', action='store_true')

args = parser.parse_args()
print args.w

Usage:

python myparser.py -w
>> True
user1767754
  • 23,311
  • 18
  • 141
  • 164
12

Your script is right. But by default is of None type. So it considers true of any other value other than None is assigned to args.argument_name variable.

I would suggest you to add a action="store_true". This would make the True/False type of flag. If used its True else False.

import argparse
parser = argparse.ArgumentParser('parser-name')
parser.add_argument("-f","--flag",action="store_true",help="just a flag argument")

usage

$ python3 script.py -f

After parsing when checked with args.f it returns true,

args = parser.parse_args()
print(args.f)
>>>true
halfer
  • 19,824
  • 17
  • 99
  • 186
vatsa287
  • 127
  • 1
  • 8
2

If you are looking for a binary flag, then the argparse actions store_true or store_false provide exactly this. This approach is well explained in the accepted answer by @Jdog.

The official docs are also fairly clear. I would only complete the example with one line, so to make it very clear how the store_true/store_false act:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='store_true')
>>> parser.add_argument('--fov', action='store_true')  # this is not in the docs!
>>> parser.add_argument('--bar', action='store_false')
>>> parser.add_argument('--baz', action='store_false')
>>> parser.parse_args('--foo --bar'.split())  # --baz and --fov are missing
Out[4]: Namespace(bar=False, baz=True, foo=True, fov=False)  # mind the fov=False

A slightly more powerful approach is to use the count action. You typically have used this type of flag already when setting the verbosity level when running a command.

For example ssh's verbose mode flag -v is a counter:

-v Verbose mode. Causes ssh to print debugging messages about its progress. This is helpful in debugging connection, authentication, and configuration problems. Multiple -v options increase the verbosity. The maximum is 3.

So if you run ssh it's non verbose, ssh -v is slightly verbose and ssh -vvv is maximally verbose.

With argparse in python such a counter flag can be defined as follows:

parser.add_argument('--verbose', '-v', action='count', default=0)

If you want to use it as a boolena (True/False) flag, then you need to cast args.verbose into a boolean. You can either do this explicitly yourself, or rely a conditional statement like if args.verbose: ....

Here is a full working example to illustrate how you can use the counter flag:

With the script test.py:

#!/usr/bin/env python3
# test.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='count', default=0)
args = parser.parse_args()

if args.verbose:
    print('verbose')
    print(f'verbosity level: {args.verbose}')
else:
    print('non-verbose')

You get the following outputs:

python test.py 
>> non-verbose 

python test.py -v
>> verbose
>> verbosity level: 1

python test.py -vvv
>> verbose
>> verbosity level: 3
j-i-l
  • 10,281
  • 3
  • 53
  • 70
  • 1
    This met the need that I had to have an argument passed without a value to the argument, thanks for posting this. – that_roy Jun 14 '23 at 16:53
-13

Here's a quick way to do it, won't require anything besides sys.. though functionality is limited:

flag = "--flag" in sys.argv[1:]

[1:] is in case if the full file name is --flag

OSA413
  • 387
  • 2
  • 4
  • 16
dbalagula23
  • 307
  • 1
  • 5
  • 13
  • 18
    Just logged in simply to express how BAD an idea this is in the long run. Imagine that you start out by checking if the string "--flag" is in sys.argv. Then you look at the end of sys.argv[-1] to see which file to open. All of a sudden you end up with a situation where if you try to open a file named `--flag`, then it will behave unexpectedly, and what's worse, is that this often ends up being a security flaw down the road, when untrusted data ends up being passed to argv. In the end, this method just shouldn't be done. Real arg parsers are just far more robust... – timthelion Nov 25 '16 at 16:32
  • 6
    As mentioned, this solution is not robust at all, but it works if you need this done super quickly (e.g. for testing purposes). Definitely keep it away from production code. – Jonathan Sudiaman Apr 09 '17 at 19:14
  • Bad examples like this are also good, in a sense. They show you what NOT to do. Sometimes your function in life is to simply serve as a warning to others. – Anjan May 01 '23 at 22:52