18

Intro

I'm trouble for a school project. I'm making a testsuit and i'm needing bot a configuration generation interface and a test runner. For that i used the library argparse and two subparsers cgi and run

The issue itself

So here is the failing code section:

def CGI(args): print("CGI: Work In Progress") exit(0) def runTest(args): print("Run: Work in Progress") exit(0) parser = argparse.ArgumentParser() subparser = parser.add_subparsers() cgi = subparser.add_parser("cgi", help="CSV Generator Interface") run = subparser.add_parser("run", help="Test running") verbosity = parser.add_argument_group("Verbosity").add_mutually_exclusive_group() check = run.add_argument_group("Checks") # Arguments #Run parser run.set_defaults(func=runTest) # Run argument declaration ... # Verbosity argument declaration ... # Check argument declaration ... #CGI cgi.set_defaults(func=CGI) args = parser.parse_args() args.func(args) # Error is here 

Whenever i run this code i have the following error:

 File "/home/thor/Projects/EPITA/TC/test/test.py", line 44, in main args.func(args) AttributeError: 'Namespace' object has no attribute 'func' 

Python version

$ python -V Python 3.6.4 

Argparse version

$ pip show argparse Name: argparse Version: 1.4.0 Summary: Python command-line parsing library Home-page: https://github.com/ThomasWaldmann/argparse/ Author: Thomas Waldmann Author-email: [email protected] License: Python Software Foundation License Location: /usr/lib/python3.6/site-packages Requires: 

EDIT

If i install argparse manually it work sudo pip install argparse. But is there any native solution. I'm not sure it will work on school's computers (we can' install packages)

EDIT 2

OK my bad i've been a total idiot i didn't rewrited my running script so i forgot to input run or cgi

Thanks for reading my message and for your future help :)

3
  • 1
    Yep, exactly. You learned the hard way that subparsers (including their defaults) are only run when their respective command is present in the arguments. Note that you can answer your own questions. Commented Feb 6, 2018 at 17:05
  • The recommended solution is p1 = parser.add_subparsers(required=True, dest='cmd') See bugs.python.org/issue16308#msg350415 Commented Sep 3, 2021 at 12:24
  • Just to add to above comment, as mentioned by "makeiteasy" below Python version 3.6 onwards 'required' has to be used explicitly: subparser.required = True Commented Jun 19, 2023 at 4:33

6 Answers 6

19

This is a known bug in the Python 3 version of argparse (https://bugs.python.org/issue16308). In Python 2, if the script is called with no arguments whatsoever (i.e., no subcommand), it exits cleanly with "error: too few arguments". In Python3, however, you get an unhandled AttributeError. Luckily, the workaround is pretty straightforward:

 try: func = args.func except AttributeError: parser.error("too few arguments") func(args) 
Sign up to request clarification or add additional context in comments.

Comments

19
parser = ArgumentParser() parser.set_defaults(func=lambda args: parser.print_help()) 

imho better than try..except

1 Comment

This is expressive (it tells you what it does) and does exactly what you want. Only thing I'd say is that you can replace func=lambda args: with func=lambda _: because args isn't used.
5

You would have to make subparsers required to call the script without arguments. To do that, you have to specify the dest and required arguments for the parser.add_subparsers:

parser = argparse.ArgumentParser() subparser = parser.add_subparsers(dest='cmd', required=True) 

Note that for Python 3.6 and earlier there's no required argument and you have to set it explicitly for the subparser object:

subparser.required = True 

Details are available in this SO answer: Argparse with required subparser

1 Comment

For anyone reading this answer (which is the official answer according to bugs.python.org/issue16308#msg350415 and should therefore be upvoted), I suggest adding also metavar='cmd' to add_subparsers. Without metavar: usage: jobs.py [-h] {run,list,show}. With metavar: usage: jobs.py [-h] cmd. As the error message when no cmd is provided is "error: the following arguments are required: cmd", I prefer to have cmd explicitly displayed in usage. The available choices for cmd are displayed in the full help anyway.
3

Another solution could be:

if len(args.__dict__) <= 1: # No arguments or subcommands were given. parser.print_help() parser.exit() 

Comments

1

Or, a combination of @simleo and @nehaljwani's answers:

 # Parse the arguments and call the sub command args = parser.parse_args() try: args.func(args) except AttributeError: parser.print_help() parser.exit() 

1 Comment

exactly, was going to post the same, but without passing args to func, just args.func inside the try
1

This error only happened when you run 'python script.py' directly. 'python script.py --help' works fine.

Add

args = parser.parse_args() try: args.func(args) except AttributeError: parser.print_help() parser.exit() 

will help you handle this case of running 'python script.py' directly. It resolved my issue, thanks very much!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.