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
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
.
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')
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.
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
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
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
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
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'))
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.
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.
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.
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
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
.
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')
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.
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
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
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
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
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'))
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.
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.