Question :
In a simple code I’m doing in Python, I get the exception below, when I call the function parser_args()
on the object of type ArgumentParser
:
Unexpected error: (<type 'exceptions.SystemExit'>, SystemExit(0,), <traceback object at 0x10c97fdd0>)
At the end I added the full tracer.
I started writing the code now, based on the template offered by Eclipse Mars.1 plus its the PyDev plugin, and there is nothing else besides the code below:
#!/usr/local/bin/python2.7
# encoding: utf-8
'''
br.com.cdcp.python.client -- Um Cliente TCP para comprovação de conhecimentos em programação Python
br.com.cdcp.python.client faz conexão com um sevidor que receber uma mensagem TCP,
aguarda a confirmação do envio, e armazena o token para uso futuro.
@author: Carlos Delfino
@copyright: 2015, cdcp. All rights reserved.
@license: GNU
@contact: consultoria@carlosdelfino.eti.br
@deffield updated: Updated
'''
import sys
import os
from argparse import ArgumentParser
from argparse import RawDescriptionHelpFormatter
__all__ = []
__version__ = 0.1
__date__ = '2015-11-17'
__updated__ = '2015-11-17'
DEBUG = 1
TESTRUN = 0
PROFILE = 0
class CLIError(Exception):
'''Generic exception to raise and log different fatal errors.'''
def __init__(self, msg):
super(CLIError).__init__(type(self))
self.msg = "E: %s" % msg
def __str__(self):
return self.msg
def __unicode__(self):
return self.msg
def main(argv=None): # IGNORE:C0111
'''Opções da Linha de Comando.'''
if argv is None:
argv = sys.argv
else:
sys.argv.extend(argv)
program_name = os.path.basename(sys.argv[0])
program_version = "v%s" % __version__
program_build_date = str(__updated__)
program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date)
program_shortdesc = __import__('__main__').__doc__.split("n")[1]
program_license = '''%s
Created by Carlos Delfino on %s.
Copyright 2015 cdcp. All rights reserved.
GNU - Versão 3.0
Distributed on an "AS IS" basis without warranties
or conditions of any kind, either express or implied.
USAGE
''' % (program_shortdesc, str(__date__))
try:
# Setup argument parser
parser = ArgumentParser(
description=program_license,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('-s', '--server',
action="store",
required = False)
parser.add_argument('-p', '--port',
action="store",
required = False)
#parser.add_argument('-v', '--version',
# action='version',
# version=program_version_message)
# Process arguments
args = parser.parse_args()
server = args.server
port = args.port
msg = '''
Servidor Selecionado: %s
Porta a ser usada: %s
''' % (server,port)
sys.stdout.write(msg)
return 0
except KeyboardInterrupt:
### handle keyboard interrupt ###
return 0
except Exception as e:
if DEBUG or TESTRUN:
raise(e)
indent = len(program_name) * " "
sys.stderr.write(program_name + ": " + repr(e) + "n")
sys.stderr.write(indent + " use --help para obter ajuda.")
return 2
except:
print "Unexpected error:", sys.exc_info()
raise
if __name__ == "__main__":
if DEBUG:
sys.argv.append("-h")
if TESTRUN:
import doctest
doctest.testmod()
if PROFILE:
import cProfile
import pstats
profile_filename = 'br.com.cdcp.python.client_profile.txt'
cProfile.run('main()', profile_filename)
statsfile = open("profile_stats.txt", "wb")
p = pstats.Stats(profile_filename, stream=statsfile)
stats = p.strip_dirs().sort_stats('cumulative')
stats.print_stats()
statsfile.close()
sys.exit(0)
sys.exit(main())
I’m using Python 2.7 on a Mac with El Capitain.
I’ve tried both Eclipse Mars.1, the Python console, and the command line by running the file directly.
Below is the complete tracer:
Traceback (most recent call last):
File "/Users/extracbd/workspace/cdcp/workspace-entrevista-carlos-delfino/Client-Python/src/br/com/cdcp/python/client.py", line 197, in <module>
exitCode = main()
File "/Users/extracbd/workspace/cdcp/workspace-entrevista-carlos-delfino/Client-Python/src/br/com/cdcp/python/client.py", line 134, in main
args = parseArgs(argv)
File "/Users/extracbd/workspace/cdcp/workspace-entrevista-carlos-delfino/Client-Python/src/br/com/cdcp/python/client.py", line 95, in parseArgs
args = parser.parse_args()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1701, in parse_args
args, argv = self.parse_known_args(args, namespace)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1733, in parse_known_args
namespace, args = self._parse_known_args(args, namespace)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1939, in _parse_known_args
start_index = consume_optional(start_index)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1879, in consume_optional
take_action(action, args, option_string)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1807, in take_action
action(self, namespace, argument_values, option_string)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 997, in __call__
parser.exit()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 2362, in exit
_sys.exit(status)
SystemExit: 0
Answer :
Well, following my hypothesis, I went looking for the same Python code to see how it works. In the end it’s pretty simple.
The class _HelpAction is logged as the action that should be taken when the argument -h
or --help
is passed in the command line. This class defines the __call__
method, which, when executed, calls the exit
method of the parser that is passed as an argument.
The exit method belongs to the ArgumentParser
class and it swims more than calling the exit
method of the sys
module.
Arriving at the root of all ills, the Python documentation on the sys module , says that this method does nothing more than generate a SystemExit
exception and therefore, it will be caught if you have a finally
or a last except
like the one you put in.
The last paragraph of the documentation is clearer, which, translated freely, looks like this:
Given that
exit()
ultimately only throws an exception,
it will only terminate the process if it is called from the main thread and
if the exception is not intercepted .
To work around this problem and keep an exception that ensures that all errors have been caught, you can put a condition that if it is SystemExit
passed with argument 0
, you can end the program quietly without printing anything or logging of something.
Or, a simpler solution, you can rely on the argparse
module to deal with exceptions and simply take management out of block arguments try
:
# ...
parser = ArgumentParser(
description=program_license,
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument(
'-s', '--server',
action="store",
required = False)
parser.add_argument(
'-p', '--port',
action="store",
required = False)
# Process arguments
args = parser.parse_args()
server = args.server
port = args.port
try:
msg = '''
Servidor Selecionado: %s
Porta a ser usada: %s
''' % (server,port)
sys.stdout.write(msg)
# ...