Issue 24070: Exceptions and arguments disappear when using argparse inside with statement (original) (raw)
Exceptions and arguments disappear when using argparse inside a "with" statement. The behavior was confusing and frustrating because I could not pinpoint why certain arguments were missing or unrecognized.
Unhandled exceptions inside the with statement typically trigger a traceback, unless exit returns True, in which the exception is "swallowed" (https://www.python.org/dev/peps/pep-0343/).
However, the NameError exception and tile_indices argument disappear because of a premature sys.exit (https://hg.python.org/cpython/file/default/Lib/argparse.py#l2385).
"""
$ python exception-in-with.py
EXPECTED
Traceback (most recent call last):
File "exception-in-with.py", line 51, in <module>
abc # !!!
NameError: name 'abc' is not defined
ACTUAL
usage: exception-in-with.py [-h] --image_path PATH
exception-in-with.py: error: argument --image_path is required
$ python exception-in-with.py --image_path x
EXPECTED == ACTUAL
Traceback (most recent call last):
File "exception-in-with.py", line 51, in <module>
abc # !!!
NameError: name 'abc' is not defined
$ python exception-in-with.py --image_path x --tile_indices 1
EXPECTED
Traceback (most recent call last):
File "exception-in-with.py", line 51, in <module>
abc # !!!
NameError: name 'abc' is not defined
ACTUAL
usage: exception-in-with.py [-h] --image_path PATH
exception-in-with.py: error: unrecognized arguments: --tile_indices 1
"""
from argparse import ArgumentParser
class Starter(object):
def __init__(self):
self.argument_parser = ArgumentParser()
def __enter__(self):
return self
def __exit__(self, exception_type, exception_value, exception_traceback):
self.argument_parser.parse_args()
def add_argument(self, *args, **kw):
self.argument_parser.add_argument(*args, **kw)
with Starter() as starter:
starter.add_argument('--image_path', metavar='PATH', required=True)
abc # !!!
starter.add_argument('--tile_indices', metavar='INTEGER')
The behavior may not be surprising from a technical perspective, but it is unintuitive.
I think exceptions inside a with statement should trigger a traceback, unless you are saying that it is the responsibility of the author to catch and raise the exception inside exit, which feels to me like a workaround that is specific to parse_args.
What is happening here is that the exit method gets passed the exception, and then instead of returning and allowing the exception to propagate and be printed, it raises SystemExit (via parse_args), which causes Python to immediately shut down, before exit returns and the exception is propagated.
So yes, you'd have to do something specific for argparse (which raises a SystemExit exception if parsing fails) if you want to do something non-normal with SystemExit. That is, you'll need to catch SystemExit.
This is really a duplicate of issue 9938.