diff --git a/README.md b/README.md index 55d37ff..5e11f3e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ int_ = IntOption('int', 'expected': 'int', 'action': 'disabled', 'inverse': True}]) +# Now build Config config = Config(OptionDescription('root', 'root', [choiceoption, @@ -110,6 +111,15 @@ usage: prog.py [-h] [-v] --str STR --list LIST [LIST ...] --int INT prog.py: error: the following arguments are required: --str ``` +If 'cmd' is 'str', cannot set --int argument: + +```bash +[gnunux@localhost tiramisu-parser]$ python3 prog.py str --int 3 +usage: prog.py [-h] [-v] --str STR --list LIST [LIST ...] --int INT + {str,list,int} +prog.py: error: unrecognized arguments: --int +``` + With all mandatories arguments: ```bash @@ -138,3 +148,20 @@ result: - v (increase output verbosity): False - list (list string option): ['a', 'b', 'c'] ``` + +Add --verbosity argument: + +```bash +[gnunux@localhost tiramisu-parser]$ python3 prog.py list --list a b c -v +result: +- cmd (choice the sub argument): list +- verbosity (increase output verbosity): True +- v (increase output verbosity): True +- list (list string option): ['a', 'b', 'c'] +[gnunux@localhost tiramisu-parser]$ python3 prog.py list --list a b c --verbosity +result: +- cmd (choice the sub argument): list +- verbosity (increase output verbosity): True +- v (increase output verbosity): True +- list (list string option): ['a', 'b', 'c'] +``` diff --git a/test/test_readme.py b/test/test_readme.py new file mode 100644 index 0000000..e98d1ac --- /dev/null +++ b/test/test_readme.py @@ -0,0 +1,210 @@ +from io import StringIO +from contextlib import redirect_stdout, redirect_stderr + + +from tiramisu_cmdline_parser import TiramisuCmdlineParser +from tiramisu import IntOption, StrOption, BoolOption, ChoiceOption, \ + SymLinkOption, OptionDescription, Config + + +def get_config(): + choiceoption = ChoiceOption('cmd', + 'choice the sub argument', + ('str', 'list', 'int'), + properties=('mandatory', + 'positional')) + booloption = BoolOption('verbosity', + 'increase output verbosity', + default=False) + short_booloption = SymLinkOption('v', booloption) + str_ = StrOption('str', + 'string option', + properties=('mandatory',), + requires=[{'option': choiceoption, + 'expected': 'str', + 'action': 'disabled', + 'inverse': True}]) + list_ = StrOption('list', + 'list string option', + multi=True, + properties=('mandatory',), + requires=[{'option': choiceoption, + 'expected': 'list', + 'action': 'disabled', + 'inverse': True}]) + int_ = IntOption('int', + 'int option', + properties=('mandatory',), + requires=[{'option': choiceoption, + 'expected': 'int', + 'action': 'disabled', + 'inverse': True}]) + return Config(OptionDescription('root', + 'root', + [choiceoption, + booloption, + short_booloption, + str_, + list_, + int_ + ])) + +def test_readme_help(): + output = """usage: prog.py [-h] [-v] --str STR --list LIST [LIST ...] --int INT + {str,list,int} + +positional arguments: + {str,list,int} choice the sub argument + +optional arguments: + -h, --help show this help message and exit + -v, --verbosity increase output verbosity + --str STR string option + --list LIST [LIST ...] + list string option + --int INT int option +""" + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(get_config()) + f = StringIO() + with redirect_stdout(f): + parser.print_help() + assert f.getvalue() == output + + +def test_readme_positional_mandatory(): + output = """usage: prog.py [-h] [-v] --str STR --list LIST [LIST ...] --int INT + {str,list,int} +prog.py: error: the following arguments are required: cmd +""" + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(get_config()) + f = StringIO() + with redirect_stderr(f): + try: + parser.parse_args([]) + except SystemExit as err: + assert str(err) == "2" + else: + raise Exception('must raises') + assert f.getvalue() == output + + +def test_readme_mandatory(): + output = """usage: prog.py [-h] [-v] --str STR --list LIST [LIST ...] --int INT + {str,list,int} +prog.py: error: the following arguments are required: --str +""" + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(get_config()) + f = StringIO() + with redirect_stderr(f): + try: + parser.parse_args(['str']) + except SystemExit as err: + assert str(err) == "2" + else: + raise Exception('must raises') + assert f.getvalue() == output + + +def test_readme_cross(): + output = """usage: prog.py [-h] [-v] --str STR --list LIST [LIST ...] --int INT + {str,list,int} +prog.py: error: unrecognized arguments: --int +""" + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(get_config()) + f = StringIO() + with redirect_stderr(f): + try: + parser.parse_args(['str', '--int', '3']) + except SystemExit as err: + assert str(err) == "2" + else: + raise Exception('must raises') + assert f.getvalue() == output + + +def test_readme_int(): + output = {'cmd': 'int', + 'int': 3, + 'verbosity': False, + 'v': False} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['int', '--int', '3']) + assert config.value.dict() == output + + +def test_readme_int_verbosity(): + output = {'cmd': 'int', + 'int': 3, + 'verbosity': True, + 'v': True} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['int', '--int', '3', '--verbosity']) + assert config.value.dict() == output + + +def test_readme_int_verbosity_short(): + output = {'cmd': 'int', + 'int': 3, + 'verbosity': True, + 'v': True} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['int', '--int', '3', '-v']) + assert config.value.dict() == output + + +def test_readme_str(): + output = {'cmd': 'str', + 'str': 'value', + 'verbosity': False, + 'v': False} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['str', '--str', 'value']) + assert config.value.dict() == output + + +def test_readme_str_int(): + output = {'cmd': 'str', + 'str': '3', + 'verbosity': False, + 'v': False} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['str', '--str', '3']) + assert config.value.dict() == output + + +def test_readme_list(): + output = {'cmd': 'list', + 'list': ['a', 'b', 'c'], + 'verbosity': False, + 'v': False} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['list', '--list', 'a', 'b', 'c']) + assert config.value.dict() == output + + +def test_readme_list_uniq(): + output = {'cmd': 'list', + 'list': ['a'], + 'verbosity': False, + 'v': False} + config = get_config() + parser = TiramisuCmdlineParser('prog.py') + parser.add_arguments(config) + parser.parse_args(['list', '--list', 'a']) + assert config.value.dict() == output diff --git a/tiramisu_cmdline_parser.py b/tiramisu_cmdline_parser.py index 9ee7c26..d5245fe 100644 --- a/tiramisu_cmdline_parser.py +++ b/tiramisu_cmdline_parser.py @@ -158,21 +158,21 @@ class TiramisuCmdlineParser(ArgumentParser): def parse_args(self, *args, **kwargs): kwargs['namespace'] = TiramisuNamespace(self.config) - namespaces = super().parse_args(*args, **kwargs) try: + namespaces = super().parse_args(*args, **kwargs) del namespaces.__dict__['_config'] except PropertiesOptionError as err: + name = err._option_bag.option.impl_getname() + properties = self.config.unrestraint.option(name).property.get() + if 'positional' not in properties: + if len(name) == 1 and 'longargument' not in properties: + name = self.prefix_chars + name + else: + name = self.prefix_chars * 2 + name if err.proptype == ['mandatory']: - name = err._option_bag.option.impl_getname() - properties = self.config.option(name).property.get() - if 'positional' not in properties: - if len(name) == 1 and 'longargument' not in properties: - name = self.prefix_chars + name - else: - name = self.prefix_chars * 2 + name self.error('the following arguments are required: {}'.format(name)) else: - self.error('unexpected error: {}'.format(err)) + self.error('unrecognized arguments: {}'.format(name)) return namespaces def format_usage(self, *args, _forhelp=False, **kwargs):