176

I would like to check whether an optional argparse argument has been set by the user or not.

Can I safely check using isset?

Something like this:

if(isset(args.myArg)):
    #do something
else:
    #do something else

Does this work the same for float / int / string type arguments?

I could set a default parameter and check it (e.g., set myArg = -1, or "" for a string, or "NOT_SET"). However, the value I ultimately want to use is only calculated later in the script. So I would be setting it to -1 as a default, and then updating it to something else later. This seems a little clumsy in comparison with simply checking if the value was set by the user.

Madeleine P. Vincent
  • 3,361
  • 5
  • 25
  • 30

11 Answers11

227

I think that optional arguments (specified with --) are initialized to None if they are not supplied. So you can test with is not None. Try the example below:

import argparse

def main():
    parser = argparse.ArgumentParser(description="My Script")
    parser.add_argument("--myArg")
    args, leftovers = parser.parse_known_args()

    if args.myArg is not None:
        print "myArg has been set (value is %s)" % args.myArg
David Jones
  • 4,766
  • 3
  • 32
  • 45
Honza Osobne
  • 2,629
  • 1
  • 13
  • 9
  • 4
    The "is None" and "is not None" tests work exactly as I would like and expect. Thanks. – Madeleine P. Vincent May 28 '15 at 09:37
  • 69
    Unfortunately it doesn't work then the argument got it's `default` value defined. – kcpr Dec 14 '16 at 00:48
  • 11
    If you want to set a `default`, then you can still set `nargs='?'` and provide a `const` value, as described [in the docs](https://docs.python.org/3/library/argparse.html#nargs). When the arg is absent, `default` is used, when arg given w/o value, then `const` is used, otherwise given value is used. With only `default` and `nargs='?'`, `default` is used if not given, `None` if given w/o value, otherwise the given value. – 0 _ Jan 26 '17 at 03:27
  • @IoannisFilippidis if you use `action= "store_true"` or `action="store_const", const="yourconst"` you can't use that argument to store other value. This will not work when using defaults. In my own i've removed all defaults from argparser and handled all inside another function `def defaults():` where i do mix ConfigParser, ArgumentParser and default values in the order i want – m3nda Oct 25 '17 at 04:23
  • @erm3nda I didn't mention setting an `action`. The answer does not use an `action`. The actions that you mention are documented to behave in one specific way (as you observed). It is not necessary to define an action though. – 0 _ Oct 25 '17 at 05:33
  • "and provide a const value", i read that as set store_const. I tried with `nargs="?"` but that didn't cover the optional `default`. If you set a `default`, that arg will be reported as "set". Optional is meant for all not Required. Is not about a boolean args or whatever. The problem is to know if the arg values was given by argument or from default. Maybe this current question is only asking about how to check arg appearance (not it's value). In case of that, sorry. – m3nda Oct 26 '17 at 20:56
  • You don't have to write if args.myArg is not None: just type if args.myArgs: that should do. – Let'sbecool Mar 04 '22 at 09:57
55

As @Honza notes is None is a good test. It's the default default, and the user can't give you a string that duplicates it.

You can specify another default='mydefaultvalue', and test for that. But what if the user specifies that string? Does that count as setting or not?

You can also specify default=argparse.SUPPRESS. Then if the user does not use the argument, it will not appear in the args namespace. But testing that might be more complicated:

parser.add_argument("--foo", default=argparse.SUPPRESS)

# ...

args.foo # raises an AttributeError
hasattr(args, 'foo')  # returns False
getattr(args, 'foo', 'other') # returns 'other'

Internally the parser keeps a list of seen_actions, and uses it for 'required' and 'mutually_exclusive' testing. But it isn't available to you out side of parse_args.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • This is *GOLD*. I dont want to clobber optional keyword arguments in the called function. This default does the job. – Merlin Aug 29 '23 at 04:07
29

I think using the option default=argparse.SUPPRESS makes most sense. Then, instead of checking if the argument is not None, one checks if the argument is in the resulting namespace.

Example:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--foo", default=argparse.SUPPRESS)
ns = parser.parse_args()

print("Parsed arguments: {}".format(ns))
print("foo in namespace?: {}".format("foo" in ns))

Usage:

$ python argparse_test.py --foo 1
Parsed arguments: Namespace(foo='1')
foo in namespace?: True
Argument is not supplied:
$ python argparse_test.py
Parsed arguments: Namespace()
foo in namespace?: False
Erasmus Cedernaes
  • 1,787
  • 19
  • 15
14

You can check an optionally passed flag with store_true and store_false argument action options:

import argparse

argparser = argparse.ArgumentParser()
argparser.add_argument('-flag', dest='flag_exists', action='store_true')

print argparser.parse_args([])
# Namespace(flag_exists=False)
print argparser.parse_args(['-flag'])
# Namespace(flag_exists=True)

This way, you don't have to worry about checking by conditional is not None. You simply check for True or False. Read more about these options in the docs here

Harry Sadler
  • 326
  • 3
  • 7
  • 5
    this doesn't solve to know if an argument that has a value is set or not. the main problem here is to know if the args value comes from defaul="" or it's supplied by user. – m3nda Oct 25 '17 at 04:24
6

If your argument is positional (ie it doesn't have a "-" or a "--" prefix, just the argument, typically a file name) then you can use the nargs parameter to do this:

parser = argparse.ArgumentParser(description='Foo is a program that does things')
parser.add_argument('filename', nargs='?')
args = parser.parse_args()

if args.filename is not None:
    print('The file name is {}'.format(args.filename))
else:
    print('Oh well ; No args, no problems')
yPhil
  • 8,049
  • 4
  • 57
  • 83
4

In order to address @kcpr's comment on the (currently accepted) answer by @Honza Osobne

Unfortunately it doesn't work then the argument got it's default value defined.

one can first check if the argument was provided by comparing it with the Namespace object and providing the default=argparse.SUPPRESS option (see @hpaulj's and @Erasmus Cedernaes answers and this python3 doc) and if it hasn't been provided, then set it to a default value.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--infile', default=argparse.SUPPRESS)
args = parser.parse_args()
if 'infile' in args: 
    # the argument is in the namespace, it's been provided by the user
    # set it to what has been provided
    theinfile = args.infile
    print('argument \'--infile\' was given, set to {}'.format(theinfile))
else:
    # the argument isn't in the namespace
    # set it to a default value
    theinfile = 'your_default.txt'
    print('argument \'--infile\' was not given, set to default {}'.format(theinfile))

Usage

$ python3 testargparse_so.py
argument '--infile' was not given, set to default your_default.txt

$ python3 testargparse_so.py --infile user_file.txt
argument '--infile' was given, set to user_file.txt
NelsonGon
  • 13,015
  • 7
  • 27
  • 57
calocedrus
  • 2,430
  • 20
  • 25
4

Here is a slightly different approach:
Suppose you know the argument name, then you can do the following:

import sys

def is_set(arg_name):
    if arg_name in sys.argv:
        return True 
    return False

This way you don't need to change your argument parser in anyway and can still add your custom logic.

oopsi
  • 1,919
  • 3
  • 21
  • 28
  • It's a good idea, but there's an issue: `argparse` allows partial matches for optional arguments if there is no ambiguity. (For example if you have a flag `--option` then `--opt` will work if there is no other flag that starts with `"opt"`.) You can modify this method to work if you look in sys.argv for an entry that starts with the shortest unique portion of the option, though. – mb7744 Jan 09 '22 at 01:16
3

Here is my solution to see if I am using an argparse variable

import argparse

ap = argparse.ArgumentParser()
ap.add_argument("-1", "--first", required=True)
ap.add_argument("-2", "--second", required=True)
ap.add_argument("-3", "--third", required=False) 
# Combine all arguments into a list called args
args = vars(ap.parse_args())
if args["third"] is not None:
# do something

This might give more insight to the above answer which I used and adapted to work for my program.

Adiii
  • 54,482
  • 7
  • 145
  • 148
C.Radford
  • 882
  • 4
  • 13
  • 31
0

A custom action can handle this problem. And I found that it is not so complicated.

is_set = set() #global set reference
class IsStored(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        is_set.add(self.dest) # save to global reference
        setattr(namespace, self.dest + '_set', True) # or you may inject directly to namespace
        setattr(namespace, self.dest, values) # implementation of store_action
        # You cannot inject directly to self.dest until you have a custom class


parser.add_argument("--myarg", type=int, default=1, action=IsStored)
params = parser.parse_args()
print(params.myarg, 'myarg' in is_set)
print(hasattr(params, 'myarg_set'))
Askold Ilvento
  • 1,405
  • 1
  • 17
  • 20
0

I have a solution that detects if an argument was explicitly set on the command line or if its value is from the default. Here is an example that accepts the login of a user:

import argparse                                                                 
import os                                                                       
                                                                            
parser = argparse.ArgumentParser()
login = os.getlogin()
parser.add_argument("-l",
                    "--login",
                    type=str,
                    default=login)
args = parser.parse_args()
print(f"login: {args.login}, default={id(login) == id(args.login)}")

If the user specifies a login the parser will store a new value and the id of the default and the value in the result namespace will be different. If nothing was specified the namespace value will have the same id as the default. This even works if the user specifies the same value as the default.

I am unsure if relying on such undocumented implementation features is a good idea, but for my version of Python (3.11.2) is works.

P.S.: I have the same answer to a similar question here.

hochl
  • 12,524
  • 10
  • 53
  • 87
-1

Very simple, after defining args variable by 'args = parser.parse_args()' it contains all data of args subset variables too. To check if a variable is set or no assuming the 'action="store_true" is used...

if args.argument_name:
   # do something
else:
   # do something else
halfer
  • 19,824
  • 17
  • 99
  • 186
vatsa287
  • 127
  • 1
  • 8