81

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?

3
  • use try except to capture TypeError, so you know that nothing has been passed
    – avasal
    Commented May 22, 2012 at 8:36
  • 9
    @avasal, len(args) always throws a TypeError.
    – huon
    Commented May 22, 2012 at 8:37
  • 1
    Trap for others - don't use 'args' as a variable name if you're planning on debugging with pdb... it's a pdb keyword and will give you blank results while looking like it's instantiated correctly. If you do use it, '!args' in pdb will show you the actual object Commented Nov 7, 2022 at 2:05

8 Answers 8

128

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.

4
  • 1
    it works and it is probably the better/simpliest way to do it :D Commented May 22, 2012 at 9:54
  • Accepted this answer, as it solves my problem, w/o me having to rethink things. => bad me ;)
    – Framester
    Commented May 22, 2012 at 15:18
  • +1 Much more practical advice for the problem at hand than my suggestion to check all the options against None.
    – Ben
    Commented May 23, 2012 at 22:39
  • Note that this does not work for checking the number of arguments given to an arbitrarily nested subparser. Commented Nov 4, 2021 at 7:42
24

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.

0
15

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.

2
  • 2
    This won't work if you have default arguments as they will overwrite theNone.
    – hkh
    Commented Jan 20, 2022 at 15:41
  • Additionally, store_true and store_false type arguments implicitly set defaults. The default for store_true is False, which doesn't break your test (though it will break any(var is not None for var in vars(args).values())), but the default for store_false is True, which definitely will.
    – AI0867
    Commented Sep 14, 2023 at 9:49
14

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
11

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.

0
3

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:

1
  • Creative 😋. Liked the answer! Commented Nov 5, 2018 at 5:02
2

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])
1
  • This is useful if using unittest to test something with argparse, in that case the accepted answer can misbehave.
    – cardamom
    Commented Apr 8, 2019 at 14:19
0

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.

2
  • you can get a Namespace with all the default values by using defaults = vars(parser.parse_args([]))
    – Girardi
    Commented Jun 5, 2021 at 22:49
  • You're right, thanks. You can select a default value from your code snippet :) Commented Jun 7, 2021 at 9:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.