for bool, generate --no-xxxx option

This commit is contained in:
Emmanuel Garette 2019-04-02 21:02:08 +02:00
parent 075de80f73
commit 266cef224e
2 changed files with 120 additions and 65 deletions

View File

@ -59,7 +59,7 @@ def get_config(has_tree=False):
def test_readme_help(): def test_readme_help():
output = """usage: prog.py [-h] [-v] {str,list,int,none} output = """usage: prog.py [-h] [-v] [-nv] {str,list,int,none}
positional arguments: positional arguments:
{str,list,int,none} choice the sub argument {str,list,int,none} choice the sub argument
@ -67,6 +67,7 @@ positional arguments:
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
""" """
parser = TiramisuCmdlineParser(get_config(), 'prog.py') parser = TiramisuCmdlineParser(get_config(), 'prog.py')
f = StringIO() f = StringIO()
@ -76,7 +77,7 @@ optional arguments:
def test_readme_help_tree(): def test_readme_help_tree():
output = """usage: prog.py [-h] [-v] {str,list,int,none} output = """usage: prog.py [-h] [-v] [-nv] {str,list,int,none}
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -84,6 +85,7 @@ optional arguments:
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
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py') parser = TiramisuCmdlineParser(get_config(True), 'prog.py')
f = StringIO() f = StringIO()
@ -93,7 +95,7 @@ root:
def test_readme_help_tree_flatten(): def test_readme_help_tree_flatten():
output = """usage: prog.py [-h] [-v] {str,list,int,none} output = """usage: prog.py [-h] [-v] [-nv] {str,list,int,none}
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -101,6 +103,7 @@ optional arguments:
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
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False) parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False)
f = StringIO() f = StringIO()
@ -110,12 +113,13 @@ root:
def test_readme_help_modif_positional(): def test_readme_help_modif_positional():
output = """usage: prog.py str [-h] [-v] --str STR output = """usage: prog.py str [-h] [-v] [-nv] --str STR
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
--str STR string option -nv, --no-verbosity
--str STR string option
""" """
parser = TiramisuCmdlineParser(get_config(), 'prog.py') parser = TiramisuCmdlineParser(get_config(), 'prog.py')
f = StringIO() f = StringIO()
@ -130,11 +134,12 @@ optional arguments:
def test_readme_help_modif(): def test_readme_help_modif():
output = """usage: prog.py str --str toto [-h] [-v] output = """usage: prog.py str --str toto [-h] [-v] [-nv]
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
""" """
parser = TiramisuCmdlineParser(get_config(), 'prog.py') parser = TiramisuCmdlineParser(get_config(), 'prog.py')
f = StringIO() f = StringIO()
@ -167,8 +172,27 @@ optional arguments:
assert f.getvalue() == output assert f.getvalue() == output
def test_readme_help_modif_short_no():
output = """usage: prog.py str --verbosity [-h] --str STR
optional arguments:
-h, --help show this help message and exit
--str STR string option
"""
parser = TiramisuCmdlineParser(get_config(), 'prog.py')
f = StringIO()
with redirect_stdout(f):
try:
parser.parse_args(['str', '-nv', '--help'])
except SystemExit as err:
assert str(err) == "0"
else:
raise Exception('must raises')
assert f.getvalue() == output
def test_readme_positional_mandatory(): def test_readme_positional_mandatory():
output = """usage: prog.py [-h] [-v] {str,list,int,none} output = """usage: prog.py [-h] [-v] [-nv] {str,list,int,none}
prog.py: error: the following arguments are required: cmd prog.py: error: the following arguments are required: cmd
""" """
parser = TiramisuCmdlineParser(get_config(), 'prog.py') parser = TiramisuCmdlineParser(get_config(), 'prog.py')
@ -184,7 +208,7 @@ prog.py: error: the following arguments are required: cmd
def test_readme_positional_mandatory_tree(): def test_readme_positional_mandatory_tree():
output = """usage: prog.py [-h] [-v] {str,list,int,none} output = """usage: prog.py [-h] [-v] [-nv] {str,list,int,none}
prog.py: error: the following arguments are required: root.cmd prog.py: error: the following arguments are required: root.cmd
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py') parser = TiramisuCmdlineParser(get_config(True), 'prog.py')
@ -200,7 +224,7 @@ prog.py: error: the following arguments are required: root.cmd
def test_readme_positional_mandatory_tree_flatten(): def test_readme_positional_mandatory_tree_flatten():
output = """usage: prog.py [-h] [-v] {str,list,int,none} output = """usage: prog.py [-h] [-v] [-nv] {str,list,int,none}
prog.py: error: the following arguments are required: cmd prog.py: error: the following arguments are required: cmd
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False) parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False)
@ -216,7 +240,7 @@ prog.py: error: the following arguments are required: cmd
def test_readme_mandatory(): def test_readme_mandatory():
output = """usage: prog.py str [-h] [-v] --str STR output = """usage: prog.py str [-h] [-v] [-nv] --str STR
prog.py: error: the following arguments are required: --str prog.py: error: the following arguments are required: --str
""" """
parser = TiramisuCmdlineParser(get_config(), 'prog.py') parser = TiramisuCmdlineParser(get_config(), 'prog.py')
@ -232,7 +256,7 @@ prog.py: error: the following arguments are required: --str
def test_readme_mandatory_tree(): def test_readme_mandatory_tree():
output = """usage: prog.py str [-h] [-v] --root.str STR output = """usage: prog.py str [-h] [-v] [-nv] --root.str STR
prog.py: error: the following arguments are required: --root.str prog.py: error: the following arguments are required: --root.str
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py') parser = TiramisuCmdlineParser(get_config(True), 'prog.py')
@ -248,7 +272,7 @@ prog.py: error: the following arguments are required: --root.str
def test_readme_mandatory_tree_flatten(): def test_readme_mandatory_tree_flatten():
output = """usage: prog.py str [-h] [-v] --str STR output = """usage: prog.py str [-h] [-v] [-nv] --str STR
prog.py: error: the following arguments are required: --str prog.py: error: the following arguments are required: --str
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False) parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False)
@ -264,7 +288,7 @@ prog.py: error: the following arguments are required: --str
def test_readme_cross(): def test_readme_cross():
output = """usage: prog.py none [-h] [-v] output = """usage: prog.py none [-h] [-v] [-nv]
prog.py: error: unrecognized arguments: --int prog.py: error: unrecognized arguments: --int
""" """
parser = TiramisuCmdlineParser(get_config(), 'prog.py') parser = TiramisuCmdlineParser(get_config(), 'prog.py')
@ -280,7 +304,7 @@ prog.py: error: unrecognized arguments: --int
def test_readme_cross_tree(): def test_readme_cross_tree():
output = """usage: prog.py none [-h] [-v] output = """usage: prog.py none [-h] [-v] [-nv]
prog.py: error: unrecognized arguments: --int prog.py: error: unrecognized arguments: --int
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py') parser = TiramisuCmdlineParser(get_config(True), 'prog.py')
@ -296,7 +320,7 @@ prog.py: error: unrecognized arguments: --int
def test_readme_cross_tree_flatten(): def test_readme_cross_tree_flatten():
output = """usage: prog.py none [-h] [-v] output = """usage: prog.py none [-h] [-v] [-nv]
prog.py: error: unrecognized arguments: --int prog.py: error: unrecognized arguments: --int
""" """
parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False) parser = TiramisuCmdlineParser(get_config(True), 'prog.py', fullpath=False)
@ -388,6 +412,17 @@ def test_readme_int_verbosity_short():
assert config.value.dict() == output assert config.value.dict() == output
def test_readme_int_verbosity_short_no():
output = {'cmd': 'int',
'int': 3,
'verbosity': False,
'v': False}
config = get_config()
parser = TiramisuCmdlineParser(config, 'prog.py')
parser.parse_args(['int', '--int', '3', '-nv'])
assert config.value.dict() == output
def test_readme_int_verbosity_short_tree(): def test_readme_int_verbosity_short_tree():
output = {'root.cmd': 'int', output = {'root.cmd': 'int',
'root.int': 3, 'root.int': 3,

View File

@ -12,10 +12,11 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Union, List, Optional from typing import Union, List, Optional
from argparse import ArgumentParser, Namespace, SUPPRESS, _HelpAction, HelpFormatter from argparse import ArgumentParser, Namespace, SUPPRESS, _HelpAction, HelpFormatter
from copy import copy
try: try:
from tiramisu import Config from tiramisu import Config
from tiramisu.error import PropertiesOptionError from tiramisu.error import PropertiesOptionError
@ -146,8 +147,19 @@ class TiramisuCmdlineParser(ArgumentParser):
def add_subparsers(self, *args, **kwargs): def add_subparsers(self, *args, **kwargs):
raise NotImplementedError('do not use add_subparsers') raise NotImplementedError('do not use add_subparsers')
def _gen_argument(self, name, properties): def _gen_argument(self, name, longargument, no_prefix=False):
if len(name) == 1 and 'longargument' not in properties: shortarg = len(name) == 1 and not longargument
if no_prefix:
if shortarg:
prefix = 'n'
else:
prefix = 'no-'
if '.' in name:
sname = name.rsplit('.', 1)
name = sname[0] + '.' + prefix + sname[1]
else:
name = prefix + name
if shortarg:
return self.prefix_chars + name return self.prefix_chars + name
return self.prefix_chars * 2 + name return self.prefix_chars * 2 + name
@ -182,67 +194,75 @@ class TiramisuCmdlineParser(ArgumentParser):
if option.issymlinkoption(): if option.issymlinkoption():
symlink_name = option.name(follow_symlink=True) symlink_name = option.name(follow_symlink=True)
if symlink_name in actions: if symlink_name in actions:
actions[symlink_name][0].insert(0, self._gen_argument(option.name(), properties)) actions[symlink_name][0][0].insert(0, self._gen_argument(option.name(), 'longargument' in properties))
if len(actions[symlink_name]) == 2:
actions[symlink_name][1][0].insert(0, self._gen_argument(option.name(), False, True))
continue continue
if _forhelp and not obj.owner.isdefault() and obj.value.get(): if _forhelp and not obj.owner.isdefault() and obj.value.get() is not None:
if 'positional' not in properties: if 'positional' not in properties:
self.prog += ' {}'.format(self._gen_argument(name, properties)) self.prog += ' {}'.format(self._gen_argument(name, 'longargument' in properties))
if option.type() != 'boolean': if option.type() != 'boolean':
self.prog += ' {}'.format(obj.value.get()) self.prog += ' {}'.format(obj.value.get())
else: else:
if 'positional' in properties: if 'positional' in properties:
if option.type() == 'boolean':
raise ValueError('boolean option must not be positional')
if not 'mandatory' in properties: if not 'mandatory' in properties:
raise ValueError('"positional" argument must be "mandatory" too') raise ValueError('"positional" argument must be "mandatory" too')
args = [option.path()]
if _forhelp: if _forhelp:
args = [option.path()]
kwargs['default'] = obj.value.default() kwargs['default'] = obj.value.default()
else: else:
args = [option.path()]
kwargs['default'] = obj.value.get() kwargs['default'] = obj.value.get()
kwargs['nargs'] = '?' kwargs['nargs'] = '?'
else: else:
kwargs['dest'] = option.path() kwargs['dest'] = option.path()
kwargs['default'] = SUPPRESS kwargs['default'] = SUPPRESS
args = [self._gen_argument(name, properties)]
if _forhelp and 'mandatory' in properties: if _forhelp and 'mandatory' in properties:
kwargs['required'] = True kwargs['required'] = True
if option.type() == 'boolean':
if option.type() == 'boolean': if obj.value.get() is False:
if 'mandatory' in properties: action = 'store_true'
raise ValueError('"mandatory" property is not allowed for BoolOption') no_action = 'store_false'
#if not isinstance(option.default(), bool):
# raise ValueError('default value is mandatory for BoolOption')
if obj.value.get() is False:
action = 'store_true'
else:
action = 'store_false'
kwargs['action'] = action
else:
if _forhelp:
value = obj.value.default()
else:
value = obj.value.get()
if value not in [None, []]:
#kwargs['default'] = kwargs['const'] = option.default()
#kwargs['action'] = 'store_const'
kwargs['nargs'] = '?'
if option.ismulti():
if _forhelp and 'mandatory' in properties:
kwargs['nargs'] = '+'
else: else:
kwargs['nargs'] = '*' action = 'store_false'
if option.type() == 'string': no_action = 'store_true'
pass kwargs['action'] = action
elif option.type() == 'integer': args = [self._gen_argument(name, 'longargument' in properties)]
kwargs['type'] = int #
elif option.type() == 'choice': nkwargs = copy(kwargs)
kwargs['choices'] = obj.value.list() nkwargs['action'] = no_action
del nkwargs['help']
nargs = [self._gen_argument(name, 'longargument' in properties, True)]
actions[option.name()] = [(args, kwargs), (nargs, nkwargs)]
continue
args = [self._gen_argument(name, 'longargument' in properties)]
if _forhelp:
value = obj.value.default()
else:
value = obj.value.get()
if value not in [None, []]:
#kwargs['default'] = kwargs['const'] = option.default()
#kwargs['action'] = 'store_const'
kwargs['nargs'] = '?'
if option.ismulti():
if _forhelp and 'mandatory' in properties:
kwargs['nargs'] = '+'
else: else:
pass kwargs['nargs'] = '*'
#raise NotImplementedError('not supported yet') if option.type() == 'string':
actions[option.name()] = (args, kwargs) pass
for args, kwargs in actions.values(): elif option.type() == 'integer':
group.add_argument(*args, **kwargs) kwargs['type'] = int
elif option.type() == 'choice':
kwargs['choices'] = obj.value.list()
else:
pass
#raise NotImplementedError('not supported yet')
actions[option.name()] = [(args, kwargs)]
for values in actions.values():
for args, kwargs in values:
group.add_argument(*args, **kwargs)
def parse_args(self, def parse_args(self,
*args, *args,
@ -272,7 +292,7 @@ class TiramisuCmdlineParser(ArgumentParser):
name = key name = key
else: else:
name = key.rsplit('.', 1)[1] name = key.rsplit('.', 1)[1]
args = self._gen_argument(name, self.config.option(key).property.get()) args = self._gen_argument(name, 'longargument' in self.config.option(key).property.get())
else: else:
args = key args = key
if not self.fullpath and '.' in args: if not self.fullpath and '.' in args: