Argparse: Check if any arguments have been passed

Question:

My script should start a demo mode, when the no parameters are given. I tried this:

args = parser.parse_args()
if len(args) == 0:
    run_demo()
else:
    # evaluate args

Which gives a *** TypeError: object of type 'Namespace' has no len() as args is no list.

How would I achieve what I want?

Asked By: Framester

||

Answers:

argparse lets you set (inside a Namespace object) all the variables mentioned in the arguments you added to the parser, based on your specification and the command line being parsed. If you set a default, then those variables will have that default value if they weren’t seen on the command line, they won’t be absent from the Namespace object. And if you don’t specify a default, then there is an implicit default of None. So checking the length of the Namespace object, however you manage to do it, doesn’t make sense as a way to check whether any arguments were parsed; it should always have the same length.

Instead, if you know you’ve got a bunch of arguments with no defaults and you want to check whether any of them were set to any non-None value… do that. You can use a list comprehension and the vars function to loop over them without having to duplicate the list of names from the add_argument calls, as shown in Martijn’s answer.

It gets a little trickier if some of your arguments have default values, and more so if they have default values that could be explicitly provided on the command line (e.g. a numeric argument that defaults to 0 makes it impossible to tell the default from the user providing 0). In that case I’m not sure that there’s a general solution that always works without knowledge of what the arguments are.

Answered By: Ben

If your goal is to detect when no argument has been given to the command, then doing this via argparse is the wrong approach (as Ben has nicely pointed out).

Think simple! 🙂 I believe that argparse does not depopulate sys.argv. So, if not len(sys.argv) > 1, then no argument has been provided by the user.

If one really needs the argument number (for whatever reason).
I have found this code very helpful (but do not know how much optimised it is, and I’d appreciate any comment on it).

args = parser.parse_args()
print( len( vars(args) ) )

This version counts only the -xx parameters and not any additional value passed.

If one wants everything (also the values passed), then just use len(sys.argv) as previously mentioned.

Answered By: 2dvisio

I expanded 2dvisio’s concept to count non zero or None arguments:

vm_opts = parser.parse_args()
v = vars(vm_opts)
n_args = sum([ 1 for a in v.values( ) if a])
Answered By: gerardw

Let us assume the following example to extend Yours for completeness:

#!/usr/bin/env python3

import argparse

...
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('input', nargs='?' action='store')
    parser.add_argument('-l', '--length', type=int, action='store')
    parser.add_argument('-v', '--verbose', action='store_true')
    args = parser.parse_args()
    if (args.input == None and args.length == None):
        parser.print_help()
    else:
        print(args)

if __name__ == '__main__':
    main()

Your Namespace object, mentioned by @Ben, in this example is args. From the strings in parser.add_argument a variable is created. You can access it through args.input or args.length or args.verbose. You can verify this by executing print(args) which will actually show something like this:

Namespace(input=None, length=None, verbose=False)

since verbose is set to True, if present and input and length are just variables, which don’t have to be instantiated (no arguments provided).

Also helpful can be group = parser.add_mutually_exclusive_group() if you want to ensure, two attributes cannot be provided simultaneously.

For further reference, please refer to:

Answered By: AdamKalisz

Don’t use argparse. Instead just use sys.argv. argparse creates a Namespace, so it will always give you a “dict” with their values, depending on what arguments you used when you called the script.

Here’s what I’ve done in the past:

args = parser.parse_args()
if len(sys.argv) == 1:
    parser.print_help()
    sys.exit()
return args
Answered By: edelatorre94

I know it’s an old thread but I found a more direct solution that might be useful for others as well:

You can check if any arguments have been passed:

if any(vars(args).values()):
    # evaluate args

Or, if no arguments have been passed(note the not operator):

if not any(vars(args).values()):
    run_demo()

Explanation:

  • parse_args() returns a “Namespace” object containing every argument name and their associated value.
    Example: Namespace(arg1='myfile.txt', arg2='some/path/to/some/folder')

  • If no arguments have been passed, parse_args() will return the same object but with all the values as None.
    Example: Namespace(arg1=None, arg2=None)

This object is not iterable, though, so you have to use vars() to turn it into a dict so we can access the values.

Finally, as we now have a dict on hands, we can get all the values(in a list), with .values(), and use the built-in any() function to check if any of the values is not None.
To make it clearer: any() returns False if there isn’t a single value that is not None, False or 0(check the docs for reference) in the list you’ve fed to it.

Hope it helps.

Answered By: Hyuri Pimentel

For the simplest case where you want to check whether a single type of argument that is the same among all the inputs has been passed, you can do it in three steps with argparse and numpy.

import argparse
import numpy as np

args = parser.parse_args()
# namespace to dictionary
args_dict = vars(args)
# unpack values from dictionary, pass to array
values = np.array([*args_dict.values()])
# Check if the defaults have changed
args_indices = np.where(values != default)[0]
# Did we pass any arguments?
if len(values) == len(args_indices):
   print("No arguments were passed")

The length is used as a proxy to check if any or no arguments have been passed. If you want to know which one has been passed you’d unpack the keys and check the changed index.

np.array() accepts logical operators for more complex cases.

Answered By: mobiuscreek