# The software in this package is distributed under the GNU General # Public License version 2 (with a special exception described below). # # A copy of GNU General Public License (GPL) is included in this distribution, # in the file COPYING.GPL. # # As a special exception, if other files instantiate templates or use macros # or inline functions from this file, or you compile this file and link it # with other works to produce a work based on this file, this file # does not by itself cause the resulting work to be covered # by the GNU General Public License. # # However the source code for this file must still be made available # in accordance with section (3) of the GNU General Public License. # # This exception does not invalidate any other reasons why a work based # on this file might be covered by the GNU General Public License. """ argparse_helpers: Some convenience helpers for argparse Featuring: - NonExitingParser: a subclass of :py:class:`argparse.ArgumentParser` that does not do *sys.exit(2)* on parse error but instead raises a :py:class:`ArgParserWantsExit` exception. - function existing_file/dir[_or_empty] which can be used as a type in calls to :py:func:`argparse.ArgumentParser.add_argument` .. codeauthor:: Intra2net """ from argparse import ArgumentParser, ArgumentTypeError from os.path import isfile, isdir class ArgParserWantsExit(Exception): """ Exception raised from NonExitingParser instead of calling sys.exit(2). """ def __init__(self, message): super(ArgParserWantsExit, self).__init__("Error parsing args: " + message) class NonExitingParser(ArgumentParser): """ArgumentParser that does not call sys.exit(2) on parse failure. Calling `sys.exit` also just raises a SystemExit exception. But that is not a subclass of Exception and not as explicit and specific as this one. Convenient e.g. for global try-except blocks e.g. in a daemon:: def main(): log = None try: log = create_log() parser = NonExitingParser() # ... add args ... args = parser.parse_args() # ... rest of your program ... except Exception as exc: log.error('uncaught exception: exc') # will always show in log """ def error(self, message): """Called when error occurred parsing args. Raise error, not exit.""" raise ArgParserWantsExit(message) def existing_file(filename): """ Function that raises ArgumentTypeError if argument is not an existing file. Returns filename if it is valid. Can be used as `type` argument in :py:meth:`argparse.ArgumentParser.add_argument` If you want to open the file, you can as well use the python built-in :py:class:`argparse.FileType` instead. """ if isfile(filename): return filename raise ArgumentTypeError('{} is not an existing file'.format(filename)) def existing_file_or_empty(filename=''): """ Like :py:func:`existing_file` but accepts empty filename (returns '' then). """ if not filename.strip(): return '' return existing_file(filename) def existing_dir(path): """ Function that raises ArgumentTypeError if argument is not an existing directory. .. seealso:: :py:func:`existing_file`, :py:func:`existing_dir_or_empty` """ if isdir(path): return path raise ArgumentTypeError('{} is not an existing directory'.format(path)) def existing_dir_or_empty(path=''): """ Like :py:func:`existing_dir` but accepts empty path (returns '' then). """ if not path.strip(): return '' return existing_dir(path)