diff --git a/aheui/_argparse.py b/aheui/_argparse.py index df196f9..b1056b1 100644 --- a/aheui/_argparse.py +++ b/aheui/_argparse.py @@ -25,12 +25,14 @@ class ArgumentNotInChoicesError(ParserError): __description__ = 'argument is not in choices: ' -class InformationException(ParserError): - __description__ = '' +class InformationException(Exception): + def __init__(self, desc=''): + self.desc = desc + class HelpException(InformationException): - __description__ = '' + pass class ArgumentParser(object): @@ -88,7 +90,10 @@ def _parse_args(self, args): done = True elif name.startswith('-'): if name == arg: - arg = args[idx + 1] + try: + arg = args[idx + 1] + except IndexError: + raise TooFewArgumentError(name) parsed[dest] = arg idx += 2 else: @@ -113,7 +118,7 @@ def parse_args(self, args): try: return self._parse_args(args) except HelpException: - os.write(2, 'usage: %s [option] ... file\n\n' % self.kwargs.get('prog', args[0])) + os.write(2, 'usage: %s [option] ... file\n\n' % get_prog(args[0])) for names, opt in self.arguments: name = names[0] if names[0] == names[1] else ('%s,%s' % names[0:2]) os.write(2, '%s%s: %s' % (name, ' ' * (12 - len(name)), opt['description'])) @@ -125,9 +130,11 @@ def parse_args(self, args): os.write(2, '\n') os.write(2, opt['full_description']) os.write(2, '\n') + raise except InformationException as e: os.write(2, '%s\n' % e.desc) - except ParserError as e: - prog = self.kwargs.get('prog', args[0]) - os.write(2, '%s: error: %s\n' % (prog, e.message())) - return {}, [] + raise + + +def get_prog(arg0): + return arg0.rsplit('/', 1)[-1] diff --git a/aheui/aheui.py b/aheui/aheui.py index 0cf145f..b72ccf3 100644 --- a/aheui/aheui.py +++ b/aheui/aheui.py @@ -6,9 +6,10 @@ import os from aheui import const as c +from aheui._argparse import InformationException, get_prog from aheui._compat import jit, unichr, ord, _unicode, bigint, PYR from aheui import compile -from aheui.option import process_options +from aheui.option import process_options, OptionError from aheui.warning import WarningPool @@ -483,7 +484,10 @@ def prepare_compiler(contents, opt_level=2, source='code', aheuic_output=None, a def entry_point(argv): try: cmd, source, contents, str_opt_level, target, aheuic_output, comment_aheuis, output, warning_limit, trace_limit = process_options(argv, os.environ) - except SystemExit: + except InformationException: + return 0 + except OptionError as e: + os.write(errfp, b"%s: error: %s\n" % (get_prog(argv[0]), e.message())) return 1 warnings.limit = warning_limit diff --git a/aheui/option.py b/aheui/option.py index 38a550c..d22f160 100644 --- a/aheui/option.py +++ b/aheui/option.py @@ -3,7 +3,7 @@ from __future__ import absolute_import import os -from aheui._argparse import ArgumentParser +from aheui._argparse import ArgumentParser, ParserError from aheui._compat import bigint, PY3 from aheui.version import VERSION from aheui import compile @@ -37,6 +37,39 @@ parser.add_argument('--help', '-h', narg='-1', default='no', description='Show this help text') +class OptionError(Exception): + pass + + +class ParsingError(OptionError): + def __init__(self, msg): + self.args = (msg,) + + def message(self): + return self.args[0] + + +class IntOptionParsingError(OptionError): + def __init__(self, key, value): + self.args = (key, value) + def message(self): + return 'The value of %s="%s" is not a valid integer' % self.args + + +class SourceError(Exception): + pass + + +class NoInputError(Exception): + def message(self): + return "no input files" + + +class CommandConflictInputFileError(Exception): + def message(self): + return "--cmd,-c and input file cannot be used together" + + def kwarg_or_environ(kwargs, environ, arg_key, env_key): if arg_key in kwargs and kwargs[arg_key] != '': return (1, kwargs[arg_key]) @@ -54,13 +87,11 @@ def kwarg_or_environ_int(kwargs, environ, arg_key, env_key, default): value = int(arg) except ValueError: if source == 1: - msg = b'The value of --%s="%s" is not a valid integer\n' % (arg_key, arg) + raise IntOptionParsingError('--' + arg_key, arg) elif source == 2: - msg = b'The value %s="%s" is not a valid integer\n' % (env_key, arg) + raise IntOptionParsingError(env_key, arg) else: assert False - os.write(2, msg) - raise return value @@ -69,15 +100,15 @@ def open_input(filename): def process_options(argv, environ): - kwargs, args = parser.parse_args(argv) - if not args: - raise SystemExit() + try: + kwargs, args = parser.parse_args(argv) + except ParserError as e: + raise ParsingError(e.message()) cmd = kwargs['cmd'] if cmd == '': if len(args) != 2: - os.write(2, b'aheui: error: no input files\n') - raise SystemExit() + raise NoInputError() filename = args[1] if filename == '-': fp = 0 @@ -88,8 +119,7 @@ def process_options(argv, environ): os.close(fp) else: if len(args) != 1: - os.write(2, b'aheui: error: --cmd,-c but input file found\n') - raise SystemExit() + raise CommandConflictInputFileError if PY3: cmd = cmd.encode('utf-8') contents = cmd @@ -145,7 +175,7 @@ def process_options(argv, environ): elif target == 'run': output = '-' else: - os.write(2, b'aheui: error: --target,-t must be one of "bytecode", "asm", "asm+comment", "run"\n') # noqa: E501 + assert False # must be handled by argparse raise SystemExit() warning_limit = kwarg_or_environ_int(kwargs, environ, 'warning-limit', 'RPAHEUI_WARNING_LIMIT', 3) diff --git a/setup.py b/setup.py index 7c22fc7..29de84c 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ def get_readme(): tests_require = [ - 'ruff', 'tox', 'pytest>=3.0.1', + 'ruff', 'tox', 'pytest>=3.0.1', 'pytest-mock' ] setup( diff --git a/tests/test_option.py b/tests/test_option.py index 6995e51..a0bfcd5 100644 --- a/tests/test_option.py +++ b/tests/test_option.py @@ -1,4 +1,5 @@ import pytest +from aheui import option from aheui.option import process_options @@ -26,9 +27,9 @@ def test_option_cmd(mocker): assert (heui, 'text', heui, '1', 'run', None, False, '-', 3, -1) == process_options(['aheui-c', '-c', '희'], {}) assert (heui, 'text', heui, '1', 'run', None, False, '-', 3, -1) == process_options(['aheui-c', '-c', '희', '--output=-'], {}) assert (heui, 'text', heui, '1', 'run', None, False, 'out', 3, -1) == process_options(['aheui-c', '-c', '희', '--output=out'], {}) - # with pytest.raises(SystemExit): - # process_options(['aheui-c', '-c'], {}) - with pytest.raises(SystemExit): + with pytest.raises(option.ParsingError): + process_options(['aheui-c', '-c'], {}) + with pytest.raises(option.CommandConflictInputFileError): process_options(['aheui-c', '-c', '희', 'x'], {}) assert (heui, 'text', heui, '1', 'asm', None, False, '-', 3, -1) == process_options(['aheui-c', '-c', '희', '--target=asm'], {}) assert (heui, 'text', heui, '1', 'asm', None, False, '-', 3, -1) == process_options(['aheui-c', '-c', '희', '--target=asm', '--output=-'], {})