support suboptiondescription in TiramisuCmdLineParser
This commit is contained in:
parent
9db0942291
commit
e14b997a54
9 changed files with 12758 additions and 21 deletions
12177
examples/AmonEcole/amonecole.py
Normal file
12177
examples/AmonEcole/amonecole.py
Normal file
File diff suppressed because one or more lines are too long
303
examples/AmonEcole/eosfunc.py
Normal file
303
examples/AmonEcole/eosfunc.py
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
import re, random, ipaddress
|
||||||
|
def get_version(*args, **kwargs):
|
||||||
|
return u'2.6.2'
|
||||||
|
|
||||||
|
|
||||||
|
def is_instanciate(*args, **kwargs):
|
||||||
|
return 'oui'
|
||||||
|
|
||||||
|
|
||||||
|
def calc_val(valeur):
|
||||||
|
return valeur
|
||||||
|
|
||||||
|
|
||||||
|
def concat(*args, **kwargs):
|
||||||
|
""" concatène deux valeurs """
|
||||||
|
sortedkeys = list(kwargs.keys())
|
||||||
|
sortedkeys.sort()
|
||||||
|
sortedkwvalues = []
|
||||||
|
for key in sortedkeys:
|
||||||
|
if kwargs[key] == '' or kwargs[key] is None:
|
||||||
|
return None
|
||||||
|
sortedkwvalues.append(kwargs[key])
|
||||||
|
if None in args:
|
||||||
|
return None
|
||||||
|
return "".join(args)+''.join(sortedkwvalues)
|
||||||
|
|
||||||
|
|
||||||
|
def auto_dns(*args, **kwargs):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def calc_multi_condition(param, match=None, mismatch=None, operator='AND',
|
||||||
|
default_match=None, default_mismatch=None,
|
||||||
|
eval_match='False', eval_mismatch='False',
|
||||||
|
**conditions):
|
||||||
|
if operator not in ('AND', 'OR'):
|
||||||
|
raise ValueError(_(u'Operator must be either "AND" or "OR"'))
|
||||||
|
if eval_match not in ('True', 'False'):
|
||||||
|
raise ValueError(_(u'eval_match must be either "True" or "False"'))
|
||||||
|
if eval_mismatch not in ('True', 'False'):
|
||||||
|
raise ValueError(_(u'eval_mismatch must be either "True" or "False"'))
|
||||||
|
if match is None:
|
||||||
|
if default_match is None:
|
||||||
|
match = u'oui'
|
||||||
|
else:
|
||||||
|
if default_match == 'None':
|
||||||
|
match = None
|
||||||
|
else:
|
||||||
|
match = default_match
|
||||||
|
if mismatch is None:
|
||||||
|
if default_mismatch is None:
|
||||||
|
mismatch = u'non'
|
||||||
|
else:
|
||||||
|
if default_mismatch == 'None':
|
||||||
|
mismatch = None
|
||||||
|
else:
|
||||||
|
mismatch = default_mismatch
|
||||||
|
conditions_keys = list(conditions.keys())
|
||||||
|
conditions_keys.sort()
|
||||||
|
for condition in conditions_keys:
|
||||||
|
if not condition.startswith('condition_'):
|
||||||
|
raise ValueError(_(u'Condition keys must start with "condition".'))
|
||||||
|
# si le paramètre est une liste...
|
||||||
|
if param.startswith('['):
|
||||||
|
param = eval(param)
|
||||||
|
# si il faut évaluer le résultat à retourner...
|
||||||
|
if eval_match == 'True':
|
||||||
|
match = eval(match)
|
||||||
|
if eval_mismatch == 'True':
|
||||||
|
mismatch = eval(mismatch)
|
||||||
|
if isinstance(param, list) and len(param) != len(conditions):
|
||||||
|
raise ValueError(_(u'Conditions and parameters counts do not match in calc_multi_condition.'))
|
||||||
|
for num in range(0, len(conditions)):
|
||||||
|
key = conditions_keys[num]
|
||||||
|
value = conditions[key]
|
||||||
|
if not isinstance(param, list):
|
||||||
|
if operator == 'AND' and value != param:
|
||||||
|
return mismatch
|
||||||
|
if operator == 'OR' and value == param:
|
||||||
|
return match
|
||||||
|
else:
|
||||||
|
if operator == 'AND' and value != param[num]:
|
||||||
|
return mismatch
|
||||||
|
if operator == 'OR' and value == param[num]:
|
||||||
|
return match
|
||||||
|
if operator == 'AND':
|
||||||
|
return match
|
||||||
|
else:
|
||||||
|
return mismatch
|
||||||
|
|
||||||
|
|
||||||
|
auto_copy_val = calc_val
|
||||||
|
|
||||||
|
|
||||||
|
def activate_master_only_web_app(*args, **kwargs):
|
||||||
|
return 'oui'
|
||||||
|
|
||||||
|
|
||||||
|
def calc_val_first_value(*args, **kwargs):
|
||||||
|
if args == tuple():
|
||||||
|
return None
|
||||||
|
return args[0]
|
||||||
|
|
||||||
|
|
||||||
|
def valid_entier(*args, **kwargs):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def list_cdrom_devices(*args, **kwargs):
|
||||||
|
return 'cdrom'
|
||||||
|
|
||||||
|
|
||||||
|
def cdrom_minormajor(*args, **kwargs):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def calc_free_PE(*args, **kwargs):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def enable_lv_creation(*args, **kwargs):
|
||||||
|
return "oui"
|
||||||
|
|
||||||
|
|
||||||
|
def get_lv_names(*args, **kwargs):
|
||||||
|
return "lvm"
|
||||||
|
|
||||||
|
|
||||||
|
def calc_multi_val(*args, **kwargs):
|
||||||
|
res = []
|
||||||
|
for arg in args:
|
||||||
|
if arg is None:
|
||||||
|
if kwargs.get('allow_none', u'False') == u'True':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
# gestion des valeurs multi
|
||||||
|
if isinstance(arg, list):
|
||||||
|
for ev_arg in arg:
|
||||||
|
if ev_arg is not None and ev_arg not in res:
|
||||||
|
res.append(ev_arg)
|
||||||
|
else:
|
||||||
|
res.append(arg)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def check_partitioning_auto_extend(*args, **kwargs):
|
||||||
|
return 'non'
|
||||||
|
|
||||||
|
|
||||||
|
def check_free_space(*args, **kwargs):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def is_fs_type(*args, **kwargs):
|
||||||
|
return "ext4"
|
||||||
|
|
||||||
|
|
||||||
|
def is_lv_name(*args, **kwargs):
|
||||||
|
return "/etc"
|
||||||
|
|
||||||
|
|
||||||
|
def get_net_device(*args, **kwargs):
|
||||||
|
return 'eth' + str(args[0])
|
||||||
|
|
||||||
|
|
||||||
|
def list_len_gt(param, max_len=None, match=None, mismatch=None,
|
||||||
|
default_match=None, default_mismatch=None,
|
||||||
|
eval_match='False', eval_mismatch='False',):
|
||||||
|
if match is None:
|
||||||
|
if default_match is None:
|
||||||
|
match = u'oui'
|
||||||
|
else:
|
||||||
|
if default_match == 'None':
|
||||||
|
match = None
|
||||||
|
else:
|
||||||
|
match = default_match
|
||||||
|
if mismatch is None:
|
||||||
|
if default_mismatch is None:
|
||||||
|
mismatch = u'non'
|
||||||
|
else:
|
||||||
|
if default_mismatch == 'None':
|
||||||
|
mismatch = None
|
||||||
|
else:
|
||||||
|
mismatch = default_mismatch
|
||||||
|
# si il faut évaluer le résultat à retourner...
|
||||||
|
if eval_match == 'True':
|
||||||
|
match = eval(match)
|
||||||
|
if eval_mismatch == 'True':
|
||||||
|
mismatch = eval(mismatch)
|
||||||
|
if isinstance(param, list):
|
||||||
|
if len(param) > int(max_len):
|
||||||
|
return match
|
||||||
|
else:
|
||||||
|
return mismatch
|
||||||
|
else:
|
||||||
|
return mismatch
|
||||||
|
|
||||||
|
|
||||||
|
def get_zone_name_bridge(*args, **kwargs):
|
||||||
|
return "non"
|
||||||
|
|
||||||
|
|
||||||
|
def get_zone_name(*args, **kwargs):
|
||||||
|
return 'eth0'
|
||||||
|
|
||||||
|
def auto_eth(*args, **kwargs):
|
||||||
|
if kwargs['parametre'] == kwargs['condition']:
|
||||||
|
return "192.168.1.1"
|
||||||
|
|
||||||
|
|
||||||
|
def auto_netmask(*args, **kwargs):
|
||||||
|
if kwargs['parametre'] == kwargs['condition']:
|
||||||
|
return "255.255.255.0"
|
||||||
|
|
||||||
|
|
||||||
|
def calc_or_auto_network(*args, **kwargs):
|
||||||
|
if kwargs['parametre'] == kwargs['condition']:
|
||||||
|
return "192.168.1.0"
|
||||||
|
return calc_network(kwargs['ip'], kwargs['netmask'])
|
||||||
|
|
||||||
|
|
||||||
|
def calc_or_auto_broadcast(*args, **kwargs):
|
||||||
|
if kwargs['parametre'] == kwargs['condition']:
|
||||||
|
return "192.168.1.255"
|
||||||
|
|
||||||
|
|
||||||
|
def auto_defaultgw_ip(*args, **kwargs):
|
||||||
|
if args[0] != 'statique':
|
||||||
|
return "192.168.1.254"
|
||||||
|
|
||||||
|
|
||||||
|
def calc_network(ip, netmask):
|
||||||
|
if None not in (ip, netmask):
|
||||||
|
try:
|
||||||
|
return str(ipaddress.ip_interface('{}/{}'.format(ip, netmask)).network.network_address)
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def calc_broadcast(ip, netmask):
|
||||||
|
if None not in (ip, netmask):
|
||||||
|
return str(ipaddress.ip_interface('{}/{}'.format(ip, netmask)).network.broadcast_address)
|
||||||
|
|
||||||
|
|
||||||
|
def calc_ssl_country_name(*args, **kwargs):
|
||||||
|
return 'FR'
|
||||||
|
|
||||||
|
|
||||||
|
def valid_country(*args, **kwargs):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def valid_regexp(data, exp_reg, err_msg=u"Invalid syntax"):
|
||||||
|
if data == "":
|
||||||
|
return True
|
||||||
|
match = re.match(exp_reg, data)
|
||||||
|
if match is None:
|
||||||
|
raise ValueError(err_msg)
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def gen_random(*args, **kwargs):
|
||||||
|
return '4444444444444444444'
|
||||||
|
|
||||||
|
|
||||||
|
def check_name_uniq(value, values, index):
|
||||||
|
values.pop(index)
|
||||||
|
if value in values:
|
||||||
|
raise ValueError('le nom {} est déjà attribué à une autre plage'.format(value))
|
||||||
|
|
||||||
|
|
||||||
|
def calc_classe(*args, **kwargs):
|
||||||
|
return "24"
|
||||||
|
|
||||||
|
|
||||||
|
def calc_webredirection(*args, **kwargs):
|
||||||
|
return "/toto"
|
||||||
|
|
||||||
|
|
||||||
|
def calc_container(mode_container, container_info, mode_zephir='non'):
|
||||||
|
return container_info
|
||||||
|
|
||||||
|
|
||||||
|
def calc_libelle_annuaire(*args, **kwargs):
|
||||||
|
return "LDAP label"
|
||||||
|
|
||||||
|
|
||||||
|
def valid_differ(data, value):
|
||||||
|
if data == value:
|
||||||
|
raise ValueError(u"Value must be different from {0}".format(value))
|
||||||
|
|
||||||
|
|
||||||
|
def device_type(*args, **kwargs):
|
||||||
|
return 'b'
|
||||||
|
|
||||||
|
|
||||||
|
def random_int(vmin, vmax, exclude=None):
|
||||||
|
values = list(range(int(vmin), int(vmax)+1))
|
||||||
|
if exclude != None and exclude in values:
|
||||||
|
values.remove(exclude)
|
||||||
|
return random.choice(values)
|
32
examples/AmonEcole/example.py
Normal file
32
examples/AmonEcole/example.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""AmonEcole example
|
||||||
|
"""
|
||||||
|
|
||||||
|
from examples.AmonEcole import amonecole
|
||||||
|
from tiramisu_cmdline_parser import TiramisuCmdlineParser
|
||||||
|
from tiramisu import default_storage
|
||||||
|
|
||||||
|
|
||||||
|
def display_name(option, dyn_name):
|
||||||
|
return "--" + option.impl_getpath()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""AmonEcole
|
||||||
|
"""
|
||||||
|
default_storage.setting(engine='sqlite3', name='amonecole_cmdline_parser')
|
||||||
|
config = amonecole.get_config(display_name=display_name)
|
||||||
|
config.property.read_write()
|
||||||
|
config.property.pop('expert')
|
||||||
|
config.property.pop('normal')
|
||||||
|
config.property.add('expert')
|
||||||
|
config.property.add('normal')
|
||||||
|
config.permissive.add('expert')
|
||||||
|
config.permissive.add('normal')
|
||||||
|
parser = TiramisuCmdlineParser(config, root='creole')
|
||||||
|
#parser.parse_args(valid_mandatory=False)
|
||||||
|
parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -145,7 +145,7 @@ def main():
|
||||||
#descr = OptionDescription('proposals',
|
#descr = OptionDescription('proposals',
|
||||||
# 'Suggesting letters',
|
# 'Suggesting letters',
|
||||||
# options)
|
# options)
|
||||||
default_storage.setting(engine='sqlite3')
|
default_storage.setting(engine='sqlite3', name='hangman_cmdline_parser')
|
||||||
config = Config(OptionDescription('root', 'root', [word, proposal_word, misses, proposals_left] + options), persistent=True, session_id='hangman')
|
config = Config(OptionDescription('root', 'root', [word, proposal_word, misses, proposals_left] + options), persistent=True, session_id='hangman')
|
||||||
config.property.read_write()
|
config.property.read_write()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -50,6 +50,8 @@ optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
leader:
|
leader:
|
||||||
|
leader
|
||||||
|
|
||||||
--leader.leader [LEADER [LEADER ...]]
|
--leader.leader [LEADER [LEADER ...]]
|
||||||
Leader var
|
Leader var
|
||||||
--leader.follower INDEX [FOLLOWER]
|
--leader.follower INDEX [FOLLOWER]
|
||||||
|
|
121
test/test_optiondescription.py
Normal file
121
test/test_optiondescription.py
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
from io import StringIO
|
||||||
|
from contextlib import redirect_stdout, redirect_stderr
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
from tiramisu_cmdline_parser import TiramisuCmdlineParser
|
||||||
|
from tiramisu import IntOption, StrOption, BoolOption, ChoiceOption, \
|
||||||
|
SymLinkOption, OptionDescription, Config
|
||||||
|
from tiramisu_json_api import Config as JsonConfig
|
||||||
|
|
||||||
|
|
||||||
|
def get_config(json, has_tree=False, default_verbosity=False, add_long=False, add_store_false=False):
|
||||||
|
choiceoption = ChoiceOption('cmd',
|
||||||
|
'choice the sub argument',
|
||||||
|
('str', 'list', 'int', 'none'),
|
||||||
|
properties=('mandatory',
|
||||||
|
'positional'))
|
||||||
|
booloption = BoolOption('verbosity',
|
||||||
|
'increase output verbosity',
|
||||||
|
default=default_verbosity)
|
||||||
|
short_booloption = SymLinkOption('v', booloption)
|
||||||
|
|
||||||
|
od0 = OptionDescription('od0',
|
||||||
|
'Sub-Tree 1',
|
||||||
|
[choiceoption,
|
||||||
|
booloption,
|
||||||
|
short_booloption,
|
||||||
|
])
|
||||||
|
od1 = OptionDescription('od1',
|
||||||
|
'First OptionDescription',
|
||||||
|
[od0])
|
||||||
|
before = StrOption('before',
|
||||||
|
'Before',
|
||||||
|
properties=('mandatory',))
|
||||||
|
after = StrOption('after',
|
||||||
|
'After',
|
||||||
|
properties=('mandatory',))
|
||||||
|
str_ = StrOption('str',
|
||||||
|
'string option 2',
|
||||||
|
properties=('mandatory',))
|
||||||
|
subtree = OptionDescription('subtree',
|
||||||
|
'Sub-Tree 2',
|
||||||
|
[str_])
|
||||||
|
od2 = OptionDescription('od2',
|
||||||
|
'Second OptionDescription',
|
||||||
|
[before, subtree, after])
|
||||||
|
root = OptionDescription('root',
|
||||||
|
'root',
|
||||||
|
[od1, od2])
|
||||||
|
config = Config(root)
|
||||||
|
config.property.read_write()
|
||||||
|
if json == 'tiramisu':
|
||||||
|
return config
|
||||||
|
jconfig = JsonConfig(config.option.dict())
|
||||||
|
return jconfig
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=['tiramisu', 'tiramisu-json'])
|
||||||
|
def json(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
def test_optiondescription_help(json):
|
||||||
|
output = """usage: prog.py [-h] [-v] [-nv] --od2.subtree.str STR --od2.before BEFORE
|
||||||
|
--od2.after AFTER
|
||||||
|
{str,list,int,none}
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
|
od1:
|
||||||
|
First OptionDescription
|
||||||
|
|
||||||
|
od1.od0:
|
||||||
|
Sub-Tree 1
|
||||||
|
|
||||||
|
{str,list,int,none} choice the sub argument
|
||||||
|
-v, --od1.od0.verbosity
|
||||||
|
increase output verbosity
|
||||||
|
-nv, --od1.od0.no-verbosity
|
||||||
|
|
||||||
|
od2:
|
||||||
|
Second OptionDescription
|
||||||
|
|
||||||
|
--od2.before BEFORE Before
|
||||||
|
--od2.after AFTER After
|
||||||
|
|
||||||
|
od2.subtree:
|
||||||
|
Sub-Tree 2
|
||||||
|
|
||||||
|
--od2.subtree.str STR
|
||||||
|
string option 2
|
||||||
|
"""
|
||||||
|
parser = TiramisuCmdlineParser(get_config(json), 'prog.py')
|
||||||
|
f = StringIO()
|
||||||
|
with redirect_stdout(f):
|
||||||
|
parser.print_help()
|
||||||
|
assert f.getvalue() == output
|
||||||
|
|
||||||
|
|
||||||
|
def test_optiondescription_help_subtree(json):
|
||||||
|
output = """usage: prog.py [-h] --od2.subtree.str STR --od2.before BEFORE --od2.after
|
||||||
|
AFTER
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--od2.before BEFORE Before
|
||||||
|
--od2.after AFTER After
|
||||||
|
|
||||||
|
od2.subtree:
|
||||||
|
Sub-Tree 2
|
||||||
|
|
||||||
|
--od2.subtree.str STR
|
||||||
|
string option 2
|
||||||
|
"""
|
||||||
|
config = get_config(json)
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py', root='od2')
|
||||||
|
f = StringIO()
|
||||||
|
with redirect_stdout(f):
|
||||||
|
parser.print_help()
|
||||||
|
assert f.getvalue() == output
|
|
@ -97,6 +97,8 @@ optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
root:
|
root:
|
||||||
|
root
|
||||||
|
|
||||||
{str,list,int,none} choice the sub argument
|
{str,list,int,none} choice the sub argument
|
||||||
-v, --root.verbosity increase output verbosity
|
-v, --root.verbosity increase output verbosity
|
||||||
-nv, --root.no-verbosity
|
-nv, --root.no-verbosity
|
||||||
|
@ -115,6 +117,8 @@ optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
root:
|
root:
|
||||||
|
root
|
||||||
|
|
||||||
{str,list,int,none} choice the sub argument
|
{str,list,int,none} choice the sub argument
|
||||||
-v, --verbosity increase output verbosity
|
-v, --verbosity increase output verbosity
|
||||||
-nv, --no-verbosity
|
-nv, --no-verbosity
|
||||||
|
|
|
@ -14,6 +14,86 @@ def json(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
def test_short(json):
|
||||||
|
def get_config():
|
||||||
|
list_ = StrOption('list',
|
||||||
|
'list string option')
|
||||||
|
slist_ = SymLinkOption('l', list_)
|
||||||
|
root = OptionDescription('root',
|
||||||
|
'root',
|
||||||
|
[list_,
|
||||||
|
slist_,
|
||||||
|
])
|
||||||
|
config = Config(root)
|
||||||
|
config.property.read_write()
|
||||||
|
if json != 'tiramisu':
|
||||||
|
config = JsonConfig(config.option.dict())
|
||||||
|
return config
|
||||||
|
#
|
||||||
|
output = {'list': None, 'l': None}
|
||||||
|
config = get_config()
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py')
|
||||||
|
parser.parse_args([])
|
||||||
|
assert config.value.dict() == output
|
||||||
|
#
|
||||||
|
output = {'list': 'a', 'l': 'a'}
|
||||||
|
config = get_config()
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py')
|
||||||
|
parser.parse_args(['--list', 'a'])
|
||||||
|
assert config.value.dict() == output
|
||||||
|
#
|
||||||
|
output = {'list': 'a', 'l': 'a'}
|
||||||
|
config = get_config()
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py')
|
||||||
|
parser.parse_args(['-l', 'a'])
|
||||||
|
assert config.value.dict() == output
|
||||||
|
|
||||||
|
|
||||||
|
def test_short_mandatory(json):
|
||||||
|
def get_config():
|
||||||
|
list_ = StrOption('list',
|
||||||
|
'list string option',
|
||||||
|
properties=('mandatory',))
|
||||||
|
slist_ = SymLinkOption('l', list_)
|
||||||
|
root = OptionDescription('root',
|
||||||
|
'root',
|
||||||
|
[list_,
|
||||||
|
slist_,
|
||||||
|
])
|
||||||
|
config = Config(root)
|
||||||
|
config.property.read_write()
|
||||||
|
if json != 'tiramisu':
|
||||||
|
config = JsonConfig(config.option.dict())
|
||||||
|
return config
|
||||||
|
#
|
||||||
|
output = """usage: prog.py [-h] -l LIST
|
||||||
|
prog.py: error: the following arguments are required: --list
|
||||||
|
"""
|
||||||
|
config = get_config()
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py')
|
||||||
|
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
|
||||||
|
#
|
||||||
|
output = {'list': 'a', 'l': 'a'}
|
||||||
|
config = get_config()
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py')
|
||||||
|
parser.parse_args(['--list', 'a'])
|
||||||
|
assert config.value.dict() == output
|
||||||
|
#
|
||||||
|
output = {'list': 'a', 'l': 'a'}
|
||||||
|
config = get_config()
|
||||||
|
parser = TiramisuCmdlineParser(config, 'prog.py')
|
||||||
|
parser.parse_args(['-l', 'a'])
|
||||||
|
assert config.value.dict() == output
|
||||||
|
|
||||||
|
|
||||||
def test_short_multi(json):
|
def test_short_multi(json):
|
||||||
def get_config():
|
def get_config():
|
||||||
list_ = StrOption('list',
|
list_ = StrOption('list',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2018 Team tiramisu (see AUTHORS for all contributors)
|
# Copyright (C) 2018-2019 Team tiramisu (see AUTHORS for all contributors)
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
# under the terms of the GNU Lesser General Public License as published by the
|
# under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
@ -35,19 +35,25 @@ except ModuleNotFoundError:
|
||||||
|
|
||||||
class TiramisuNamespace(Namespace):
|
class TiramisuNamespace(Namespace):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
config: Config) -> None:
|
config: Config,
|
||||||
|
root: Optional[str]) -> None:
|
||||||
super().__setattr__('_config', config)
|
super().__setattr__('_config', config)
|
||||||
|
super().__setattr__('_root', root)
|
||||||
super().__setattr__('list_force_no', {})
|
super().__setattr__('list_force_no', {})
|
||||||
self._populate()
|
self._populate()
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def _populate(self) -> None:
|
def _populate(self) -> None:
|
||||||
for tiramisu_key, tiramisu_value in self._config.value.dict().items():
|
if self._root is None:
|
||||||
|
config = self._config
|
||||||
|
else:
|
||||||
|
config = self._config.option(self._root)
|
||||||
|
for tiramisu_key, tiramisu_value in config.value.dict(fullpath=True).items():
|
||||||
option = self._config.option(tiramisu_key)
|
option = self._config.option(tiramisu_key)
|
||||||
if not option.option.issymlinkoption():
|
if not option.option.issymlinkoption():
|
||||||
if tiramisu_value == [] and \
|
if tiramisu_value == [] and \
|
||||||
option.option.ismulti() and \
|
option.option.ismulti(): # and \
|
||||||
option.owner.isdefault():
|
# option.owner.isdefault():
|
||||||
tiramisu_value = None
|
tiramisu_value = None
|
||||||
super().__setattr__(tiramisu_key, tiramisu_value)
|
super().__setattr__(tiramisu_key, tiramisu_value)
|
||||||
|
|
||||||
|
@ -106,7 +112,7 @@ class TiramisuHelpFormatter(HelpFormatter):
|
||||||
action):
|
action):
|
||||||
ret = super()._get_default_metavar_for_optional(action)
|
ret = super()._get_default_metavar_for_optional(action)
|
||||||
if '.' in ret:
|
if '.' in ret:
|
||||||
ret = ret.split('.', 1)[1]
|
ret = ret.rsplit('.', 1)[1]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,17 +185,24 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
config: Union[Config, ConfigJson],
|
config: Union[Config, ConfigJson],
|
||||||
*args,
|
*args,
|
||||||
|
root: str=None,
|
||||||
fullpath: bool=True,
|
fullpath: bool=True,
|
||||||
_forhelp: bool=False,
|
_forhelp: bool=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
self.fullpath = fullpath
|
self.fullpath = fullpath
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.root = root
|
||||||
kwargs['formatter_class'] = TiramisuHelpFormatter
|
kwargs['formatter_class'] = TiramisuHelpFormatter
|
||||||
self.namespace = TiramisuNamespace(self.config)
|
if self.root is None:
|
||||||
|
subconfig = self.config.option
|
||||||
|
else:
|
||||||
|
subconfig = self.config.option(self.root)
|
||||||
|
self.namespace = TiramisuNamespace(self.config, self.root)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.register('action', 'help', _TiramisuHelpAction)
|
self.register('action', 'help', _TiramisuHelpAction)
|
||||||
self._config_to_argparser(_forhelp,
|
self._config_to_argparser(_forhelp,
|
||||||
self.config.option)
|
subconfig,
|
||||||
|
self.root)
|
||||||
|
|
||||||
def _pop_action_class(self, kwargs, default=None):
|
def _pop_action_class(self, kwargs, default=None):
|
||||||
ret = super()._pop_action_class(kwargs, default)
|
ret = super()._pop_action_class(kwargs, default)
|
||||||
|
@ -231,6 +244,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
# so create a new parser
|
# so create a new parser
|
||||||
new_parser = TiramisuCmdlineParser(self.config,
|
new_parser = TiramisuCmdlineParser(self.config,
|
||||||
self.prog,
|
self.prog,
|
||||||
|
root=self.root,
|
||||||
fullpath=self.fullpath)
|
fullpath=self.fullpath)
|
||||||
namespace_, args_ = new_parser._parse_known_args(args_, namespace)
|
namespace_, args_ = new_parser._parse_known_args(args_, namespace)
|
||||||
else:
|
else:
|
||||||
|
@ -275,12 +289,14 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
continue
|
continue
|
||||||
if obj.option.isoptiondescription():
|
if obj.option.isoptiondescription():
|
||||||
if _forhelp:
|
if _forhelp:
|
||||||
group = self.add_argument_group(obj.option.doc())
|
newgroup = self.add_argument_group(obj.option.path(), obj.option.doc())
|
||||||
|
else:
|
||||||
|
newgroup = group
|
||||||
if prefix:
|
if prefix:
|
||||||
prefix_ = prefix + '.' + obj.option.name()
|
prefix_ = prefix + '.' + obj.option.name()
|
||||||
else:
|
else:
|
||||||
prefix_ = obj.option.path()
|
prefix_ = obj.option.path()
|
||||||
self._config_to_argparser(_forhelp, obj, prefix_, group)
|
self._config_to_argparser(_forhelp, obj, prefix_, newgroup)
|
||||||
elif obj.option.type() == 'boolean' and not obj.option.issymlinkoption():
|
elif obj.option.type() == 'boolean' and not obj.option.issymlinkoption():
|
||||||
yield obj, False
|
yield obj, False
|
||||||
yield obj, True
|
yield obj, True
|
||||||
|
@ -290,7 +306,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
def _config_to_argparser(self,
|
def _config_to_argparser(self,
|
||||||
_forhelp: bool,
|
_forhelp: bool,
|
||||||
config,
|
config,
|
||||||
prefix: Optional[str]=None,
|
prefix: Optional[str],
|
||||||
group=None) -> None:
|
group=None) -> None:
|
||||||
if group is None:
|
if group is None:
|
||||||
group = super()
|
group = super()
|
||||||
|
@ -298,6 +314,15 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
leadership_len = None
|
leadership_len = None
|
||||||
for obj, force_no in self._config_list(config, prefix, _forhelp, group):
|
for obj, force_no in self._config_list(config, prefix, _forhelp, group):
|
||||||
option = obj.option
|
option = obj.option
|
||||||
|
name = option.name()
|
||||||
|
if name.startswith(self.prefix_chars):
|
||||||
|
raise ValueError(_('name cannot startswith "{}"').format(self.prefix_chars))
|
||||||
|
if option.issymlinkoption():
|
||||||
|
symlink_name = option.name(follow_symlink=True)
|
||||||
|
if symlink_name in actions:
|
||||||
|
for action in actions[symlink_name]:
|
||||||
|
action.add_argument(option)
|
||||||
|
continue
|
||||||
if option.isleader():
|
if option.isleader():
|
||||||
value = obj.value.get()
|
value = obj.value.get()
|
||||||
leadership_len = len(value)
|
leadership_len = len(value)
|
||||||
|
@ -307,21 +332,12 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
value.append(self.config.option(obj.option.path(), index).value.get())
|
value.append(self.config.option(obj.option.path(), index).value.get())
|
||||||
else:
|
else:
|
||||||
value = obj.value.get()
|
value = obj.value.get()
|
||||||
name = option.name()
|
|
||||||
if name.startswith(self.prefix_chars):
|
|
||||||
raise ValueError(_('name cannot startswith "{}"').format(self.prefix_chars))
|
|
||||||
if self.fullpath and prefix:
|
if self.fullpath and prefix:
|
||||||
name = prefix + '.' + name
|
name = prefix + '.' + name
|
||||||
if option.isfollower():
|
if option.isfollower():
|
||||||
properties = obj.option.properties()
|
properties = obj.option.properties()
|
||||||
else:
|
else:
|
||||||
properties = obj.property.get()
|
properties = obj.property.get()
|
||||||
if option.issymlinkoption():
|
|
||||||
symlink_name = option.name(follow_symlink=True)
|
|
||||||
if symlink_name in actions:
|
|
||||||
for action in actions[symlink_name]:
|
|
||||||
action.add_argument(option)
|
|
||||||
continue
|
|
||||||
kwargs = _BuildKwargs(name, option, self, properties, force_no)
|
kwargs = _BuildKwargs(name, option, self, properties, force_no)
|
||||||
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:
|
||||||
if not force_no:
|
if not force_no:
|
||||||
|
@ -462,6 +478,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
**kwargs):
|
**kwargs):
|
||||||
help_formatter = TiramisuCmdlineParser(self.config,
|
help_formatter = TiramisuCmdlineParser(self.config,
|
||||||
self.prog,
|
self.prog,
|
||||||
|
root=self.root,
|
||||||
fullpath=self.fullpath,
|
fullpath=self.fullpath,
|
||||||
_forhelp=True)
|
_forhelp=True)
|
||||||
return super(TiramisuCmdlineParser, help_formatter).format_usage(*args, **kwargs)
|
return super(TiramisuCmdlineParser, help_formatter).format_usage(*args, **kwargs)
|
||||||
|
@ -469,6 +486,7 @@ class TiramisuCmdlineParser(ArgumentParser):
|
||||||
def format_help(self):
|
def format_help(self):
|
||||||
help_formatter = TiramisuCmdlineParser(self.config,
|
help_formatter = TiramisuCmdlineParser(self.config,
|
||||||
self.prog,
|
self.prog,
|
||||||
|
root=self.root,
|
||||||
fullpath=self.fullpath,
|
fullpath=self.fullpath,
|
||||||
_forhelp=True)
|
_forhelp=True)
|
||||||
return super(TiramisuCmdlineParser, help_formatter).format_help()
|
return super(TiramisuCmdlineParser, help_formatter).format_help()
|
||||||
|
|
Loading…
Reference in a new issue