works now with tiramisu-json-api

This commit is contained in:
Emmanuel Garette 2019-01-10 09:55:45 +01:00
parent d66ffeb4ac
commit d313e9a41d
2 changed files with 63 additions and 38 deletions

View File

@ -14,23 +14,31 @@
# 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 from typing import Union, List, Optional
from argparse import ArgumentParser, Namespace, SUPPRESS from argparse import ArgumentParser, Namespace, SUPPRESS
from tiramisu import Option, OptionDescription, Config, BoolOption, StrOption, IntOption, \
ChoiceOption, SymLinkOption
from tiramisu.error import PropertiesOptionError from tiramisu.error import PropertiesOptionError
try:
from tiramisu import Config
except ImportError:
Config = None
try:
from tiramisu_json_api import Config as ConfigJson
if Config is None:
Config = ConfigJson
except ImportError:
ConfigJson = Config
class TiramisuNamespace(Namespace): class TiramisuNamespace(Namespace):
def _populate(self): def _populate(self):
self._config.property.read_only() #self._config.property.read_only()
for tiramisu_key, tiramisu_value in self._config.value.dict().items(): for tiramisu_key, tiramisu_value in self._config.value.dict().items():
option = self._config.option(tiramisu_key) option = self._config.option(tiramisu_key)
if not isinstance(option.option.get(), SymLinkOption): if not option.option.issymlinkoption():
if tiramisu_value == [] and option.option.ismulti() and option.owner.isdefault(): if tiramisu_value == [] and option.option.ismulti() and option.owner.isdefault():
tiramisu_value = None tiramisu_value = None
super().__setattr__(tiramisu_key, tiramisu_value) super().__setattr__(tiramisu_key, tiramisu_value)
self._config.property.read_write() #self._config.property.read_write()
def __init__(self, config): def __init__(self, config):
self._config = config self._config = config
@ -40,7 +48,7 @@ class TiramisuNamespace(Namespace):
if key == '_config': if key == '_config':
super().__setattr__(key, value) super().__setattr__(key, value)
return return
self._config.property.read_write() # self._config.property.read_write()
option = self._config.option(key) option = self._config.option(key)
if option.option.ismulti() and value is not None and not isinstance(value, list): if option.option.ismulti() and value is not None and not isinstance(value, list):
value = [value] value = [value]
@ -53,17 +61,21 @@ class TiramisuNamespace(Namespace):
class TiramisuCmdlineParser(ArgumentParser): class TiramisuCmdlineParser(ArgumentParser):
def __init__(self, *args, **kwargs): def __init__(self,
*args,
**kwargs):
self.config = None self.config = None
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def _match_arguments_partial(self, actions, arg_string_pattern): def _match_arguments_partial(self,
actions,
arg_string_pattern):
# used only when check first proposal for first value # used only when check first proposal for first value
# we have to remove all actions with propertieserror # we have to remove all actions with propertieserror
# so only first settable option will be returned # so only first settable option will be returned
actions_pop = [] actions_pop = []
for idx, action in enumerate(actions): for idx, action in enumerate(actions):
if self.config.unrestraint.option(action.dest).property.get(only_raises=True): if self.config.option(action.dest).property.get(only_raises=True):
actions_pop.append(idx) actions_pop.append(idx)
else: else:
break break
@ -81,44 +93,59 @@ class TiramisuCmdlineParser(ArgumentParser):
raise NotImplementedError('do not use add_subparsers') raise NotImplementedError('do not use add_subparsers')
def _config_to_argparser(self, def _config_to_argparser(self,
_forhelp: bool): _forhelp: bool,
config,
prefix: Optional[str]=None,
group=None) -> None:
if group is None:
group = super()
actions = {} actions = {}
for obj in self.config.unrestraint.option.list(): for obj in config.list(type='all'):
if obj.option.properties(only_raises=True) or 'frozen' in obj.option.properties():
continue
option = obj.option option = obj.option
tiramisu_option = option.get() if option.isoptiondescription():
if _forhelp:
group = self.add_argument_group(option.doc())
if prefix:
prefix_ = prefix + '.' + option.name()
else:
prefix_ = option.path()
self._config_to_argparser(_forhelp, obj, prefix_, group)
continue
if 'frozen' in option.properties():
continue
name = option.name() name = option.name()
if name.startswith(self.prefix_chars): if name.startswith(self.prefix_chars):
raise ValueError('name cannot startswith "{}"'.format(self.prefix_chars)) raise ValueError('name cannot startswith "{}"'.format(self.prefix_chars))
properties = obj.property.get() properties = obj.property.get()
kwargs = {'help': option.doc(), kwargs = {'help': option.doc().replace('%', '%%'),
'default': SUPPRESS} 'default': SUPPRESS}
if 'positional' in properties: if 'positional' in properties:
#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 = [name] args = [name]
if option.requires(): #if option.requires():
kwargs['nargs'] = '?' kwargs['nargs'] = '?'
else: else:
if prefix:
name = prefix + '.' + name
if len(name) == 1 and 'longargument' not in properties: if len(name) == 1 and 'longargument' not in properties:
args = [self.prefix_chars + name] args = [self.prefix_chars + name]
else: else:
args = [self.prefix_chars * 2 + name] args = [self.prefix_chars * 2 + name]
if _forhelp and 'mandatory' in properties: if _forhelp and 'mandatory' in properties:
kwargs['required'] = True kwargs['required'] = True
if isinstance(tiramisu_option, BoolOption): if option.type() == 'bool':
if 'mandatory' in properties: if 'mandatory' in properties:
raise ValueError('"mandatory" property is not allowed for BoolOption') raise ValueError('"mandatory" property is not allowed for BoolOption')
#if not isinstance(option.default(), bool): #if not isinstance(option.default(), bool):
# raise ValueError('default value is mandatory for BoolOption') # raise ValueError('default value is mandatory for BoolOption')
if option.default() is False: if obj.value.get() is False:
action = 'store_true' action = 'store_true'
else: else:
action = 'store_false' action = 'store_false'
kwargs['action'] = action kwargs['action'] = action
else: else:
if option.default() not in [None, []]: if obj.value.get() not in [None, []]:
#kwargs['default'] = kwargs['const'] = option.default() #kwargs['default'] = kwargs['const'] = option.default()
#kwargs['action'] = 'store_const' #kwargs['action'] = 'store_const'
kwargs['nargs'] = '?' kwargs['nargs'] = '?'
@ -127,34 +154,29 @@ class TiramisuCmdlineParser(ArgumentParser):
kwargs['nargs'] = '+' kwargs['nargs'] = '+'
else: else:
kwargs['nargs'] = '*' kwargs['nargs'] = '*'
if isinstance(tiramisu_option, StrOption): if option.type() == 'str':
pass pass
elif isinstance(tiramisu_option, IntOption): elif option.type() == 'int':
kwargs['type'] = int kwargs['type'] = int
elif isinstance(tiramisu_option, SymLinkOption): elif option.issymlinkoption():
tiramisu_option = tiramisu_option.impl_getopt() option = option.impl_getopt()
actions[tiramisu_option.impl_getname()][0].insert(0, args[0]) actions[option.impl_getname()][0].insert(0, args[0])
continue continue
elif isinstance(tiramisu_option, ChoiceOption): elif option.type() == 'choice':
kwargs['choices'] = obj.value.list() kwargs['choices'] = obj.value.list()
else: else:
pass pass
#raise NotImplementedError('not supported yet') #raise NotImplementedError('not supported yet')
actions[option.name()] = (args, kwargs) actions[option.name()] = (args, kwargs)
for args, kwargs in actions.values(): for args, kwargs in actions.values():
super().add_argument(*args, **kwargs) group.add_argument(*args, **kwargs)
def add_arguments(self, def add_arguments(self,
tiramisu: Union[Config, Option, List[Option], OptionDescription], tiramisu: Union[Config, ConfigJson],
_forhelp: bool=False) -> None: _forhelp: bool=False) -> None:
if not isinstance(tiramisu, Config):
if not isinstance(tiramisu, OptionDescription):
if isinstance(tiramisu, Option):
tiramisu = [tiramisu]
tiramisu = OptionDescription('root', 'root', tiramisu)
tiramisu = Config(tiramisu)
self.config = tiramisu self.config = tiramisu
self._config_to_argparser(_forhelp) self._config_to_argparser(_forhelp,
self.config.option)
def parse_args(self, *args, **kwargs): def parse_args(self, *args, **kwargs):
kwargs['namespace'] = TiramisuNamespace(self.config) kwargs['namespace'] = TiramisuNamespace(self.config)
@ -163,7 +185,7 @@ class TiramisuCmdlineParser(ArgumentParser):
del namespaces.__dict__['_config'] del namespaces.__dict__['_config']
except PropertiesOptionError as err: except PropertiesOptionError as err:
name = err._option_bag.option.impl_getname() name = err._option_bag.option.impl_getname()
properties = self.config.unrestraint.option(name).property.get() properties = self.config.option(name).property.get()
if 'positional' not in properties: if 'positional' not in properties:
if len(name) == 1 and 'longargument' not in properties: if len(name) == 1 and 'longargument' not in properties:
name = self.prefix_chars + name name = self.prefix_chars + name
@ -175,7 +197,10 @@ class TiramisuCmdlineParser(ArgumentParser):
self.error('unrecognized arguments: {}'.format(name)) self.error('unrecognized arguments: {}'.format(name))
return namespaces return namespaces
def format_usage(self, *args, _forhelp=False, **kwargs): def format_usage(self,
*args,
_forhelp=False,
**kwargs):
if _forhelp: if _forhelp:
return super().format_usage(*args, **kwargs) return super().format_usage(*args, **kwargs)
help_formatter = TiramisuCmdlineParser(self.prog) help_formatter = TiramisuCmdlineParser(self.prog)