From e6a9a37607b72e70349fdf737c21fd00770bb127 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 3 Aug 2019 09:06:27 +0200 Subject: [PATCH] support integer choiceoption --- tests/test_leadership.py | 2 +- tiramisu_cmdline_parser/api.py | 65 ++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/tests/test_leadership.py b/tests/test_leadership.py index c4da041..1272916 100644 --- a/tests/test_leadership.py +++ b/tests/test_leadership.py @@ -237,7 +237,7 @@ def test_leadership_modif_follower_choice_unknown(json): [--leader.follower_boolean INDEX] [--leader.no-follower_boolean INDEX] [--leader.follower_choice INDEX [{opt1,opt2}]] -prog.py: error: invalid choice: 'opt_unknown' (choose from 'opt1', 'opt2') +prog.py: error: argument --leader.follower_choice: invalid choice: 'opt_unknown' (choose from 'opt1', 'opt2') """ config = get_config(json) parser = TiramisuCmdlineParser(config, 'prog.py') diff --git a/tiramisu_cmdline_parser/api.py b/tiramisu_cmdline_parser/api.py index eda2c21..aac4de8 100644 --- a/tiramisu_cmdline_parser/api.py +++ b/tiramisu_cmdline_parser/api.py @@ -33,6 +33,17 @@ except ModuleNotFoundError: ConfigJson = Config +def get_choice_list(obj, properties, display): + choices = obj.value.list() + if choices[0] == '': + del choices[0] + if display: + choices = '{{{}}}'.format(','.join(choices)) + if 'mandatory' not in properties: + choices = f'[{choices}]' + return choices + + class TiramisuNamespace(Namespace): def __init__(self, config: Config, @@ -41,6 +52,7 @@ class TiramisuNamespace(Namespace): super().__setattr__('_root', root) super().__setattr__('list_force_no', {}) super().__setattr__('list_force_del', {}) + super().__setattr__('arguments', {}) self._populate() super().__init__() @@ -74,6 +86,22 @@ class TiramisuNamespace(Namespace): else: _setattr = self._setattr true_value = value + if option.option.type() == 'choice': + # HACK if integer in choice + values = option.value.list() + if isinstance(value, list): + int_value = [] + for val in value: + if isinstance(val, str) and val.isdigit(): + int_val = int(val) + if int_val in values: + val = int_val + int_value.append(val) + value = int_value + elif value not in values and isinstance(value, str) and value.isdigit(): + int_value = int(value) + if int_value in values: + value = int_value try: if key in self.list_force_del: option.value.pop(value) @@ -81,7 +109,16 @@ class TiramisuNamespace(Namespace): _setattr(option, true_key, key, value) except ValueError as err: if option.option.type() == 'choice': - raise ValueError("invalid choice: '{}' (choose from {})".format(true_value, ', '.join([f"'{val}'" for val in option.value.list()]))) + choices = get_choice_list(option, option.property.get(), False) + values = option.value.list() + if isinstance(true_value, list): + for val in value: + if val not in values: + display_value = val + break + else: + display_value = true_value + raise ValueError("argument {}: invalid choice: '{}' (choose from {})".format(self.arguments[key], display_value, ', '.join([f"'{val}'" for val in choices]))) else: raise err @@ -173,8 +210,11 @@ class _BuildKwargs: else: ga_name = name self.kwargs['dest'] = self.gen_argument_name(option.path(), False) - self.args = [self.cmdlineparser._gen_argument(ga_name, is_short_name)] + argument = self.cmdlineparser._gen_argument(ga_name, is_short_name) + self.cmdlineparser.namespace.arguments[option.path()] = argument + self.args = [argument] else: + self.cmdlineparser.namespace.arguments[option.path()] = option.path() self.args = [option.path()] def __setitem__(self, @@ -191,7 +231,9 @@ class _BuildKwargs: name = self.gen_argument_name(option.name(), is_short_name) else: name = option.name() - self.args.insert(0, self.cmdlineparser._gen_argument(name, is_short_name)) + argument = self.cmdlineparser._gen_argument(name, is_short_name) + self.cmdlineparser.namespace.arguments[option.path()] = argument + self.args.insert(0, argument) def gen_argument_name(self, name, is_short_name): if self.force_no: @@ -467,14 +509,9 @@ class TiramisuCmdlineParser(ArgumentParser): kwargs['nargs'] = 2 if _forhelp and 'mandatory' not in properties: metavar = f'[{metavar}]' - if option.type() == 'choice': - choice_list = obj.value.list() - if choice_list[0] == '': - del choice_list[0] - choices = '{{{}}}'.format(','.join(choice_list)) - if 'mandatory' not in properties: - choices = f'[{choices}]' - kwargs['metavar'] = ('INDEX', choices) + if option.type() == 'choice' and _forhelp: + # do not manage choice with argparse there is problem with integer problem + kwargs['metavar'] = ('INDEX', get_choice_list(obj, properties, True)) else: kwargs['metavar'] = ('INDEX', metavar) if force_del: @@ -488,11 +525,11 @@ class TiramisuCmdlineParser(ArgumentParser): if _forhelp and option.type() == 'boolean': kwargs['metavar'] = 'INDEX' kwargs['nargs'] = 1 - elif option.type() == 'choice' and not option.isfollower(): - kwargs['choices'] = obj.value.list() + elif option.type() == 'choice' and not option.isfollower() and _forhelp: + # do not manage choice with argparse there is problem with integer problem + kwargs['choices'] = get_choice_list(obj, properties, False) else: pass - #raise NotImplementedError('not supported yet') actions.setdefault(option.name(), []).append(kwargs) for option_is_not_default in options_is_not_default.values(): self._option_is_not_default(**option_is_not_default)