Check if argparse optional argument is set or not

Question:

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.

Answers:

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
Answered By: Honza Osobne

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.

Answered By: hpaulj

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')
Answered By: yPhil

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.

Answered By: C.Radford

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

Answered By: Harry Sadler

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
Answered By: Erasmus Cedernaes

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
Answered By: calocedrus

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
Answered By: vatsa287

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'))
Answered By: Askold Ilvento

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.

Answered By: oopsi

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.

Answered By: hochl
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.