Manolo Martínez wrote: > Peter, thanks for taking the time to look into my code. > > On 12/01/15 at 11:40am, Peter Otten wrote: >> Manolo Martínez wrote: >> > def main(): # parse the args and call whatever function was >> selected >> > try: >> > args = parser.parse_args(sys.argv[1:]) >> > args.func(vars(args)) >> > except AttributeError as err: >> > if str(err) == "\'Namespace\' object has no >> attribute \'func\'": >> > parser.print_help() >> > else: >> > print("Something has gone wrong: >> {}".format(err), file = sys.stderr, flush = True) >> >> What probably is typical for a beginner in that snippet is that you don't >> trust the exception system and handle exceptions that will never occur >> once the script is debugged. Just write >> >> args = parser.parse_args() >> args.func(vars(args)) > > Well, one fully possible situation is for the user to mistype a > subcommand. In that case, the script outputs the help provided by > argparse, so that they know what is and isn't meaningful. That is what > the "if str(err)..." is doing. > > The else clause is there to output the traceback (in full trust of the > exception system ;) in case the error was not due to the user mistyping. > > Is there a better way to do this?
As far as I can see in a correctly written script the AttributeError cannot be triggered by the user as argparse handles this case automatically by showing the help. For example: $ cat subparsers.py #!/usr/bin/env python3 import argparse def foo(args): print(args) def main(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers() foo_parser = subparsers.add_parser("foo") foo_parser.set_defaults(func=foo) bar_parser = subparsers.add_parser("bar") # programming error --> missing func attribute args = parser.parse_args() args.func(args) main() $ ./subparsers.py foo Namespace(func=<function foo at 0x7ff18e8a2158>) $ ./subparsers.py bar Traceback (most recent call last): File "./subparsers.py", line 23, in <module> main() File "./subparsers.py", line 20, in main args.func(args) AttributeError: 'Namespace' object has no attribute 'func' $ ./subparsers.py baz # erroneous user input usage: subparsers.py [-h] {foo,bar} ... subparsers.py: error: invalid choice: 'baz' (choose from 'foo', 'bar') The traceback may be a bit intimidating, but with your error handling the user will get $ cat subparsers2.py #!/usr/bin/env python3 import argparse def foo(args): print(args) def main(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers() foo_parser = subparsers.add_parser("foo") foo_parser.set_defaults(func=foo) bar_parser = subparsers.add_parser("bar") # programming error --> missing func attribute try: args = parser.parse_args() args.func(args) except AttributeError as err: if str(err) == "\'Namespace\' object has no attribute \'func\'": print("WE ARE HERE") parser.print_help() else: print("Something has gone wrong: {}".format(err), file = sys.stderr, flush = True) main() $ ./subparsers2.py foo Namespace(func=<function foo at 0x7ff9f6fedbf8>) $ ./subparsers2.py baz # erroneous user input does not trigger your error handling usage: subparsers2.py [-h] {foo,bar} ... subparsers2.py: error: invalid choice: 'baz' (choose from 'foo', 'bar') $ ./subparsers2.py bar # but an error in your script does WE ARE HERE usage: subparsers2.py [-h] {foo,bar} ... positional arguments: {foo,bar} optional arguments: -h, --help show this help message and exit which is very confusing. Again, don't replace the traceback unless you are absolutely sure you can do better than the Python interpreter. By the way, I recommend coverage.py to find dead code. I have not used it for too long, and I regret it ;) -- https://mail.python.org/mailman/listinfo/python-list