add all options even if already set by user
This commit is contained in:
parent
dcc7510676
commit
528d16ec88
3 changed files with 136 additions and 109 deletions
|
@ -290,17 +290,18 @@ def test_leadership_modif_mandatory(json):
|
||||||
'leader.follower_choice': [None],
|
'leader.follower_choice': [None],
|
||||||
'leader.follower_integer': [None],
|
'leader.follower_integer': [None],
|
||||||
'leader.follower_submulti': [['255.255.255.128']]}
|
'leader.follower_submulti': [['255.255.255.128']]}
|
||||||
output2 = """usage: prog.py --leader.leader ['192.168.1.1'] [-h]
|
output2 = """usage: prog.py --leader.leader "192.168.1.1" [-h]
|
||||||
[--leader.pop-leader INDEX]
|
[--leader.leader [LEADER [LEADER ...]]]
|
||||||
[--leader.follower INDEX [FOLLOWER]]
|
[--leader.pop-leader INDEX]
|
||||||
--leader.follower_submulti
|
[--leader.follower INDEX [FOLLOWER]]
|
||||||
INDEX [FOLLOWER_SUBMULTI ...]
|
--leader.follower_submulti INDEX
|
||||||
[--leader.follower_integer INDEX [FOLLOWER_INTEGER]]
|
[FOLLOWER_SUBMULTI ...]
|
||||||
[--leader.follower_boolean INDEX]
|
[--leader.follower_integer INDEX [FOLLOWER_INTEGER]]
|
||||||
[--leader.no-follower_boolean INDEX]
|
[--leader.follower_boolean INDEX]
|
||||||
[--leader.follower_choice INDEX [{opt1,opt2}]]
|
[--leader.no-follower_boolean INDEX]
|
||||||
--leader.follower_mandatory
|
[--leader.follower_choice INDEX [{opt1,opt2}]]
|
||||||
INDEX FOLLOWER_MANDATORY
|
--leader.follower_mandatory INDEX
|
||||||
|
FOLLOWER_MANDATORY
|
||||||
prog.py: error: the following arguments are required: --leader.follower_submulti"""
|
prog.py: error: the following arguments are required: --leader.follower_submulti"""
|
||||||
|
|
||||||
config = get_config(json, with_mandatory=True)
|
config = get_config(json, with_mandatory=True)
|
||||||
|
|
|
@ -131,7 +131,10 @@ root:
|
||||||
|
|
||||||
|
|
||||||
def test_readme_help_modif_positional(json):
|
def test_readme_help_modif_positional(json):
|
||||||
output = """usage: prog.py str [-h] [-v] [-nv] --str STR
|
output = """usage: prog.py "str" [-h] [-v] [-nv] --str STR {str,list,int,none}
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
{str,list,int,none} choice the sub argument
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
@ -152,12 +155,17 @@ optional arguments:
|
||||||
|
|
||||||
|
|
||||||
def test_readme_help_modif(json):
|
def test_readme_help_modif(json):
|
||||||
output = """usage: prog.py str --str toto [-h] [-v] [-nv]
|
output = """usage: prog.py "str" --str "toto" [-h] [-v] [-nv] --str STR
|
||||||
|
{str,list,int,none}
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
{str,list,int,none} choice the sub argument
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-v, --verbosity increase output verbosity
|
-v, --verbosity increase output verbosity
|
||||||
-nv, --no-verbosity
|
-nv, --no-verbosity
|
||||||
|
--str STR string option
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
|
@ -172,11 +180,16 @@ optional arguments:
|
||||||
|
|
||||||
|
|
||||||
def test_readme_help_modif_short1(json):
|
def test_readme_help_modif_short1(json):
|
||||||
output = """usage: prog.py str --verbosity [-h] --str STR
|
output = """usage: prog.py "str" -v [-h] [-v] [-nv] --str STR {str,list,int,none}
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
{str,list,int,none} choice the sub argument
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--str STR string option
|
-v, --verbosity increase output verbosity
|
||||||
|
-nv, --no-verbosity
|
||||||
|
--str STR string option
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
|
@ -191,11 +204,16 @@ optional arguments:
|
||||||
|
|
||||||
|
|
||||||
def test_readme_help_modif_short_no(json):
|
def test_readme_help_modif_short_no(json):
|
||||||
output = """usage: prog.py str --verbosity [-h] --str STR
|
output = """usage: prog.py "str" -v [-h] [-v] [-nv] --str STR {str,list,int,none}
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
{str,list,int,none} choice the sub argument
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--str STR string option
|
-v, --verbosity increase output verbosity
|
||||||
|
-nv, --no-verbosity
|
||||||
|
--str STR string option
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
|
@ -258,7 +276,7 @@ prog.py: error: the following arguments are required: cmd
|
||||||
|
|
||||||
|
|
||||||
def test_readme_mandatory(json):
|
def test_readme_mandatory(json):
|
||||||
output = """usage: prog.py str [-h] [-v] [-nv] --str STR
|
output = """usage: prog.py "str" [-h] [-v] [-nv] --str STR {str,list,int,none}
|
||||||
prog.py: error: the following arguments are required: --str
|
prog.py: error: the following arguments are required: --str
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
||||||
|
@ -274,7 +292,7 @@ prog.py: error: the following arguments are required: --str
|
||||||
|
|
||||||
|
|
||||||
def test_readme_mandatory_tree(json):
|
def test_readme_mandatory_tree(json):
|
||||||
output = """usage: prog.py str [-h] [-v] [-nv] --root.str STR
|
output = """usage: prog.py "str" [-h] [-v] [-nv] --root.str STR {str,list,int,none}
|
||||||
prog.py: error: the following arguments are required: --root.str
|
prog.py: error: the following arguments are required: --root.str
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py')
|
||||||
|
@ -290,7 +308,7 @@ prog.py: error: the following arguments are required: --root.str
|
||||||
|
|
||||||
|
|
||||||
def test_readme_mandatory_tree_flatten(json):
|
def test_readme_mandatory_tree_flatten(json):
|
||||||
output = """usage: prog.py str [-h] [-v] [-nv] --str STR
|
output = """usage: prog.py "str" [-h] [-v] [-nv] --str STR {str,list,int,none}
|
||||||
prog.py: error: the following arguments are required: --str
|
prog.py: error: the following arguments are required: --str
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py', fullpath=False)
|
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py', fullpath=False)
|
||||||
|
@ -306,7 +324,7 @@ prog.py: error: the following arguments are required: --str
|
||||||
|
|
||||||
|
|
||||||
def test_readme_cross(json):
|
def test_readme_cross(json):
|
||||||
output = """usage: prog.py none [-h] [-v] [-nv]
|
output = """usage: prog.py "none" [-h] [-v] [-nv] {str,list,int,none}
|
||||||
prog.py: error: unrecognized arguments: --int
|
prog.py: error: unrecognized arguments: --int
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
||||||
|
@ -322,7 +340,7 @@ prog.py: error: unrecognized arguments: --int
|
||||||
|
|
||||||
|
|
||||||
def test_readme_cross_tree(json):
|
def test_readme_cross_tree(json):
|
||||||
output = """usage: prog.py none [-h] [-v] [-nv]
|
output = """usage: prog.py "none" [-h] [-v] [-nv] {str,list,int,none}
|
||||||
prog.py: error: unrecognized arguments: --int
|
prog.py: error: unrecognized arguments: --int
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py')
|
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py')
|
||||||
|
@ -338,7 +356,7 @@ prog.py: error: unrecognized arguments: --int
|
||||||
|
|
||||||
|
|
||||||
def test_readme_cross_tree_flatten(json):
|
def test_readme_cross_tree_flatten(json):
|
||||||
output = """usage: prog.py none [-h] [-v] [-nv]
|
output = """usage: prog.py "none" [-h] [-v] [-nv] {str,list,int,none}
|
||||||
prog.py: error: unrecognized arguments: --int
|
prog.py: error: unrecognized arguments: --int
|
||||||
"""
|
"""
|
||||||
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py', fullpath=False)
|
parser = TiramisuCmdlineParser(get_config(json, True), 'prog.py', fullpath=False)
|
||||||
|
|
|
@ -19,13 +19,14 @@ from gettext import gettext as _
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tiramisu import Config
|
from tiramisu import Config
|
||||||
from tiramisu.error import PropertiesOptionError, RequirementError
|
from tiramisu.error import PropertiesOptionError, RequirementError, LeadershipError
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
Config = None
|
Config = None
|
||||||
from tiramisu_json_api.error import PropertiesOptionError
|
from tiramisu_api.error import PropertiesOptionError
|
||||||
RequirementError = PropertiesOptionError
|
RequirementError = PropertiesOptionError
|
||||||
|
LeadershipError = ValueError
|
||||||
try:
|
try:
|
||||||
from tiramisu_json_api import Config as ConfigJson
|
from tiramisu__api import Config as ConfigJson
|
||||||
if Config is None:
|
if Config is None:
|
||||||
Config = ConfigJson
|
Config = ConfigJson
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
|
@ -284,7 +285,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
def _parse_known_args(self, args=None, namespace=None):
|
def _parse_known_args(self, args=None, namespace=None):
|
||||||
try:
|
try:
|
||||||
namespace_, args_ = super()._parse_known_args(args, namespace)
|
namespace_, args_ = super()._parse_known_args(args, namespace)
|
||||||
except ValueError as err:
|
except (ValueError, LeadershipError) as err:
|
||||||
self.error(err)
|
self.error(err)
|
||||||
if args != args_ and args_ and args_[0].startswith(self.prefix_chars):
|
if args != args_ and args_ and args_[0].startswith(self.prefix_chars):
|
||||||
# option that was disabled are no more disable
|
# option that was disabled are no more disable
|
||||||
|
@ -297,7 +298,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
formatter_class=self.formatter_class,
|
formatter_class=self.formatter_class,
|
||||||
epilog=self.epilog,
|
epilog=self.epilog,
|
||||||
fullpath=self.fullpath)
|
fullpath=self.fullpath)
|
||||||
namespace_, args_ = new_parser._parse_known_args(args_, namespace)
|
namespace_, args_ = new_parser._parse_known_args(args_, new_parser.namespace)
|
||||||
else:
|
else:
|
||||||
if self._registries['action']['help'].needs:
|
if self._registries['action']['help'].needs:
|
||||||
# display help only when all variables assignemnt are done
|
# display help only when all variables assignemnt are done
|
||||||
|
@ -327,7 +328,11 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
is_short_name = self._is_short_name(name, 'longargument' in properties)
|
is_short_name = self._is_short_name(name, 'longargument' in properties)
|
||||||
self.prog += ' {}'.format(self._gen_argument(name, is_short_name))
|
self.prog += ' {}'.format(self._gen_argument(name, is_short_name))
|
||||||
if type != 'boolean':
|
if type != 'boolean':
|
||||||
self.prog += f' {value}'
|
if isinstance(value, list):
|
||||||
|
for val in value:
|
||||||
|
self.prog += f' "{val}"'
|
||||||
|
else:
|
||||||
|
self.prog += f' "{value}"'
|
||||||
|
|
||||||
def _config_list(self,
|
def _config_list(self,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
@ -372,6 +377,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
group = super()
|
group = super()
|
||||||
actions = {}
|
actions = {}
|
||||||
leadership_len = None
|
leadership_len = None
|
||||||
|
options_is_not_default = {}
|
||||||
for obj, force_no, force_del in self._config_list(config, prefix, _forhelp, group, level):
|
for obj, force_no, force_del in self._config_list(config, prefix, _forhelp, group, level):
|
||||||
option = obj.option
|
option = obj.option
|
||||||
name = option.name()
|
name = option.name()
|
||||||
|
@ -379,6 +385,8 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
raise ValueError(_('name cannot startswith "{}"').format(self.prefix_chars))
|
raise ValueError(_('name cannot startswith "{}"').format(self.prefix_chars))
|
||||||
if option.issymlinkoption():
|
if option.issymlinkoption():
|
||||||
symlink_name = option.name(follow_symlink=True)
|
symlink_name = option.name(follow_symlink=True)
|
||||||
|
if symlink_name in options_is_not_default:
|
||||||
|
options_is_not_default[symlink_name]['name'] = name
|
||||||
if symlink_name in actions:
|
if symlink_name in actions:
|
||||||
for action in actions[symlink_name]:
|
for action in actions[symlink_name]:
|
||||||
action.add_argument(option)
|
action.add_argument(option)
|
||||||
|
@ -401,90 +409,90 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
else:
|
else:
|
||||||
properties = obj.property.get()
|
properties = obj.property.get()
|
||||||
kwargs = _BuildKwargs(name, option, self, properties, force_no, force_del)
|
kwargs = _BuildKwargs(name, option, self, properties, force_no, force_del)
|
||||||
if not option.isfollower() and _forhelp and not obj.owner.isdefault() and value is not None:
|
if not option.isfollower() and _forhelp and not obj.owner.isdefault() and value is not None and not force_no:
|
||||||
if not force_no:
|
options_is_not_default[option.name()] = {'properties': properties,
|
||||||
self._option_is_not_default(properties,
|
'type': option.type(),
|
||||||
option.type(),
|
'name': name,
|
||||||
name,
|
'value': value}
|
||||||
value)
|
if 'positional' in properties:
|
||||||
else:
|
if option.type() == 'boolean':
|
||||||
if 'positional' in properties:
|
raise ValueError(_('boolean option must not be positional'))
|
||||||
if option.type() == 'boolean':
|
if not 'mandatory' in properties:
|
||||||
raise ValueError(_('boolean option must not be positional'))
|
raise ValueError('"positional" argument must be "mandatory" too')
|
||||||
if not 'mandatory' in properties:
|
if _forhelp:
|
||||||
raise ValueError('"positional" argument must be "mandatory" too')
|
kwargs['default'] = obj.value.default()
|
||||||
if _forhelp:
|
|
||||||
kwargs['default'] = obj.value.default()
|
|
||||||
else:
|
|
||||||
kwargs['default'] = value
|
|
||||||
kwargs['nargs'] = '?'
|
|
||||||
else:
|
else:
|
||||||
kwargs['default'] = SUPPRESS
|
kwargs['default'] = value
|
||||||
if _forhelp and 'mandatory' in properties:
|
kwargs['nargs'] = '?'
|
||||||
kwargs['required'] = True
|
else:
|
||||||
if not force_del and option.type() == 'boolean':
|
kwargs['default'] = SUPPRESS
|
||||||
if not option.isfollower():
|
if _forhelp and 'mandatory' in properties:
|
||||||
if 'storefalse' in properties:
|
kwargs['required'] = True
|
||||||
if force_no:
|
if not force_del and option.type() == 'boolean':
|
||||||
action = 'store_true'
|
if not option.isfollower():
|
||||||
else:
|
if 'storefalse' in properties:
|
||||||
action = 'store_false'
|
if force_no:
|
||||||
elif force_no:
|
|
||||||
action = 'store_false'
|
|
||||||
else:
|
|
||||||
action = 'store_true'
|
action = 'store_true'
|
||||||
kwargs['action'] = action
|
|
||||||
else:
|
|
||||||
kwargs['metavar'] = 'INDEX'
|
|
||||||
if option.type() != 'boolean' or force_del:
|
|
||||||
if not force_del:
|
|
||||||
if _forhelp:
|
|
||||||
value = obj.value.default()
|
|
||||||
if value not in [None, []]:
|
|
||||||
#kwargs['default'] = kwargs['const'] = option.default()
|
|
||||||
#kwargs['action'] = 'store_const'
|
|
||||||
kwargs['nargs'] = '?'
|
|
||||||
|
|
||||||
if not option.isfollower() and option.ismulti():
|
|
||||||
if _forhelp and 'mandatory' in properties:
|
|
||||||
kwargs['nargs'] = '+'
|
|
||||||
else:
|
else:
|
||||||
kwargs['nargs'] = '*'
|
action = 'store_false'
|
||||||
if option.isfollower() and not option.type() == 'boolean':
|
elif force_no:
|
||||||
metavar = option.name().upper()
|
action = 'store_false'
|
||||||
if option.issubmulti():
|
else:
|
||||||
|
action = 'store_true'
|
||||||
|
kwargs['action'] = action
|
||||||
|
else:
|
||||||
|
kwargs['metavar'] = 'INDEX'
|
||||||
|
if option.type() != 'boolean' or force_del:
|
||||||
|
if not force_del:
|
||||||
|
if _forhelp:
|
||||||
|
value = obj.value.default()
|
||||||
|
if value not in [None, []]:
|
||||||
|
#kwargs['default'] = kwargs['const'] = option.default()
|
||||||
|
#kwargs['action'] = 'store_const'
|
||||||
|
kwargs['nargs'] = '?'
|
||||||
|
|
||||||
|
if not option.isfollower() and option.ismulti():
|
||||||
|
if _forhelp and 'mandatory' in properties:
|
||||||
kwargs['nargs'] = '+'
|
kwargs['nargs'] = '+'
|
||||||
else:
|
else:
|
||||||
kwargs['nargs'] = 2
|
kwargs['nargs'] = '*'
|
||||||
if _forhelp and 'mandatory' not in properties:
|
if option.isfollower() and not option.type() == 'boolean':
|
||||||
metavar = f'[{metavar}]'
|
metavar = option.name().upper()
|
||||||
if option.type() == 'choice':
|
if option.issubmulti():
|
||||||
choice_list = obj.value.list()
|
kwargs['nargs'] = '+'
|
||||||
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)
|
|
||||||
else:
|
|
||||||
kwargs['metavar'] = ('INDEX', metavar)
|
|
||||||
if force_del:
|
|
||||||
kwargs['metavar'] = 'INDEX'
|
|
||||||
kwargs['type'] = int
|
|
||||||
elif option.type() == 'string':
|
|
||||||
pass
|
|
||||||
elif option.type() == 'integer' or option.type() == 'boolean':
|
|
||||||
# when boolean we are here only if follower
|
|
||||||
kwargs['type'] = int
|
|
||||||
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()
|
|
||||||
else:
|
else:
|
||||||
pass
|
kwargs['nargs'] = 2
|
||||||
#raise NotImplementedError('not supported yet')
|
if _forhelp and 'mandatory' not in properties:
|
||||||
actions.setdefault(option.name(), []).append(kwargs)
|
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)
|
||||||
|
else:
|
||||||
|
kwargs['metavar'] = ('INDEX', metavar)
|
||||||
|
if force_del:
|
||||||
|
kwargs['metavar'] = 'INDEX'
|
||||||
|
kwargs['type'] = int
|
||||||
|
elif option.type() == 'string':
|
||||||
|
pass
|
||||||
|
elif option.type() == 'integer' or option.type() == 'boolean':
|
||||||
|
# when boolean we are here only if follower
|
||||||
|
kwargs['type'] = int
|
||||||
|
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()
|
||||||
|
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)
|
||||||
for values in actions.values():
|
for values in actions.values():
|
||||||
for value in values:
|
for value in values:
|
||||||
args, kwargs = value.get()
|
args, kwargs = value.get()
|
||||||
|
|
Loading…
Reference in a new issue