feat(cli): add error handling

The CLI now handles error that may result from user input and prints an
error message. Following error types are handled:
 - file not found
 - not a directory
 - directory instead of file
 - file exists
 - permission error
 - general operating system errors
 - Unicode errors
 - errors parsing project source code

Additionally, KeyboardInterrupt and BrokenPipeError are catched and an
appropriate exit code is returned.
This commit is contained in:
jakob.scheid committed 2026-09-10 13:36:03 +02:00
1 parent 60a097f0cc
commit 3ab9884be0
1 file changed
+38
+38
View File
@@ -22,9 +22,11 @@ __all__ = [
import argparse
import pathlib
import sys
from .parsers.python import VALID_SECTIONS as VALID_PYTHON_SECTIONS
_LANG_PYTHON = 'python'
_PROGRAM_NAME = 'jcloud-docsgen'
def existing_directory(path: str) -> pathlib.Path:
_path = path
@@ -35,6 +37,19 @@ def existing_directory(path: str) -> pathlib.Path:
raise argparse.ArgumentTypeError(f'{_path}: not a directory')
return path
def error(message: str, exit_code: int = 1):
'''
Shows an error message and returns the exit code.
:param message: The error message to show.
:type message: str
:param exit_code: The exit code.
:type exit_code: int
'''
print(f'{_PROGRAM_NAME}: error: {message}', file = sys.stderr)
return exit_code
def main(argv = None) -> int:
common_parser = argparse.ArgumentParser(add_help = False)
common_parser.add_argument(
@@ -93,6 +108,7 @@ def main(argv = None) -> int:
if args.output_dir is None:
args.output_dir = args.project_directory / 'docs/reference'
try:
if args.language == _LANG_PYTHON:
from .parsers.python import generate
generate(
@@ -103,5 +119,27 @@ def main(argv = None) -> int:
allow_html = args.allow_html,
allow_tables = args.allow_tables
)
except FileNotFoundError as exc:
return error(f'{exc.filename}: no such file or directory')
except NotADirectoryError as exc:
return error(f'{exc.filename}: not a directory')
except IsADirectoryError as exc:
return error(f'{exc.filename}: is a directory')
except FileExistsError as exc:
return error(f'{exc.filename}: file already exists')
except PermissionError as exc:
return error(f'{exc.filename}: permission denied')
except OSError as exc:
return error(f'{exc.filename}: {exc.args[1]}')
except UnicodeError:
return error('invalid text encoding')
except SyntaxError:
# TODO: add a custom exception class to clearify that a project
# parsing syntax error is meant.
return error(f'syntax error in project source code')
except KeyboardInterrupt:
return 130
except BrokenPipeError:
return 141
return 0