diff --git a/src/rougail/config.py b/src/rougail/config.py index 1ae5e9304..d75250f80 100644 --- a/src/rougail/config.py +++ b/src/rougail/config.py @@ -136,6 +136,12 @@ class _RougailConfig: class FakeRougailConvert(RougailConvert): + def __init__(self, + add_extra_options: bool, + ) -> None: + self.add_extra_options = add_extra_options + super().__init__({}) + def load_config(self, rougailconfig: 'RougailConfig', ) -> None: @@ -149,10 +155,12 @@ class FakeRougailConvert(RougailConvert): self.export_with_import = True self.internal_functions = [] self.plugins = ['structural_commandline'] + self.add_extra_options = self.add_extra_options def get_rougail_config(*, - backward_compatibility=True, + backward_compatibility: bool=True, + add_extra_options: bool=True, ) -> _RougailConfig: if backward_compatibility: main_namespace_default = 'rougail' @@ -178,7 +186,7 @@ main_dictionaries: main_namespace: description: Main namespace name default: MAIN_MAMESPACE_DEFAULT - alternative_name: n + alternative_name: s mandatory: false extra_dictionaries: description: Extra namespaces @@ -191,12 +199,12 @@ extra_dictionaries: {% endif %} names: description: 'Extra namespace name' -# alternative_name: e + alternative_name: e multi: true mandatory: false directories: description: Directories where extra dictionary files are placed -# alternative_name: d + alternative_name: d type: unix_filename params: allow_relative: true @@ -279,9 +287,9 @@ suffix: mandatory: false commandline: false """.replace('MAIN_MAMESPACE_DEFAULT', main_namespace_default) - processes = {'output': [], + processes = {'structural': [], + 'output': [], 'user data': [], - # 'structural': [], } for module in get_sub_modules().values(): data = module.get_rougail_config() @@ -302,13 +310,15 @@ suffix: ) for obj in objects: rougail_process += f" - {obj['name']}\n" - if process == 'user data': - rougail_process +=""" multi: true + if process == 'structural': + rougail_process += " commandline: false" + elif process == 'user data': + rougail_process += """ multi: true mandatory: false """ hidden_outputs = [process['name'] for process in processes['output'] if not process.get('allow_user_data', True)] if hidden_outputs: - rougail_process +=""" hidden: + rougail_process += """ hidden: type: jinja jinja: | """ @@ -329,7 +339,7 @@ suffix: """.format(NAME=normalize_family(process), ) rougail_options += rougail_process - convert = FakeRougailConvert({}) + convert = FakeRougailConvert(add_extra_options) convert._init() convert.namespace = None convert.parse_root_file( diff --git a/src/rougail/convert.py b/src/rougail/convert.py index d267b20f9..200a2cd3a 100644 --- a/src/rougail/convert.py +++ b/src/rougail/convert.py @@ -317,6 +317,7 @@ class Informations: class ParserVariable: def __init__(self, rougailconfig): self.load_config(rougailconfig) + self.rougailconfig = rougailconfig self.paths = Paths(self.main_namespace) self.families = [] self.variables = [] @@ -332,9 +333,6 @@ class ParserVariable: self.convert_options = list(CONVERT_OPTION) self.convert_options.extend(self.custom_types) # - self.family = Family - self.dynamic = Dynamic - # self.exclude_imports = [] self.informations = Informations() self.properties = Property() @@ -363,12 +361,14 @@ class ParserVariable: self.base_option_name = rougailconfig["base_option_name"] self.export_with_import = rougailconfig["export_with_import"] self.internal_functions = rougailconfig["internal_functions"] + self.add_extra_options = rougailconfig["structural_commandline.add_extra_options"] self.plugins = [] def _init(self): if self.is_init: return variable = Variable + family = Family if self.plugins: root = Path(__file__).parent for plugin in self.plugins: @@ -376,13 +376,15 @@ class ParserVariable: if not module_path.is_file(): continue module = load_modules(f'rougail.{plugin}.object_model', str(module_path)) - if 'Variable' not in module.__all__: - continue - variable = type(variable.__name__ + '_' + plugin, (Variable, module.Variable), {}) + if 'Variable' in module.__all__: + variable = type(variable.__name__ + '_' + plugin, (variable, module.Variable), {}) + if 'Family' in module.__all__: + family = type(family.__name__ + '_' + plugin, (family, module.Family), {}) self.variable = variable + self.family = family + self.dynamic = type(Dynamic.__name__, (Dynamic, family), {}) hint = get_type_hints(self.dynamic) # FIXME: only for format 1.0 - hint["variable"] = str self.family_types = hint["type"].__args__ # pylint: disable=W0201 self.family_attrs = frozenset( # pylint: disable=W0201 set(hint) - {"name", "path", "xmlfiles"} | {"redefine"} diff --git a/src/rougail/structural_commandline/annotator.py b/src/rougail/structural_commandline/annotator.py index 2e5ad8f46..812eb082c 100644 --- a/src/rougail/structural_commandline/annotator.py +++ b/src/rougail/structural_commandline/annotator.py @@ -20,34 +20,58 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ from rougail.annotator.variable import Walk +from rougail.utils import _ +from rougail.error import DictConsistencyError class Annotator(Walk): """Annotate value""" - level = 5 + level = 80 def __init__(self, objectspace, *args) -> None: if not objectspace.paths: return self.objectspace = objectspace - self.manage_alternative_name() - self.not_for_commandline() - - def manage_alternative_name(self) -> None: + not_for_commandlines = [] + for family in self.get_families(): + if family.commandline: + continue + self.not_for_commandline(family) + not_for_commandlines.append(family.path + '.') for variable in self.get_variables(): if variable.type == 'symlink': continue - if not variable.alternative_name: - continue - alternative_name = variable.alternative_name variable_path = variable.path - if '.' not in variable_path: - path = alternative_name + for family_path in not_for_commandlines: + if variable_path.startswith(family_path): + break else: - path = variable_path.rsplit('.', 1)[0] + '.' + alternative_name - self.objectspace.add_variable(alternative_name, {'type': 'symlink', 'path': path, 'opt': variable}, variable.xmlfiles, False, False, variable.version) + if not variable.commandline: + self.not_for_commandline(variable) + else: + self.manage_alternative_name(variable) + self.manage_negative_description(variable) - def not_for_commandline(self) -> None: - for variable in self.get_variables(): - if not hasattr(variable, 'commandline') or variable.commandline: - continue - self.objectspace.properties.add(variable.path, 'not_for_commandline', True) + def not_for_commandline(self, variable) -> None: + self.objectspace.properties.add(variable.path, 'not_for_commandline', True) + + def manage_alternative_name(self, variable) -> None: + if not variable.alternative_name: + return + alternative_name = variable.alternative_name + variable_path = variable.path + if '.' not in variable_path: + path = alternative_name + else: + path = variable_path.rsplit('.', 1)[0] + '.' + alternative_name + self.objectspace.add_variable(alternative_name, {'type': 'symlink', 'path': path, 'opt': variable}, variable.xmlfiles, False, False, variable.version) + + def manage_negative_description(self, variable) -> None: + if not variable.negative_description: + if variable.type == 'boolean' and not self.objectspace.add_extra_options: + raise DictConsistencyError(_(f'negative_description is mandatory for boolean variable, but "{variable.path}" hasn\'t'), 200, variable.xmlfiles) + return + if variable.type != 'boolean': + raise DictConsistencyError(_(f'negative_description is only available for boolean variable, but "{variable.path}" is "{variable.type}"'), 200, variable.xmlfiles) + self.objectspace.informations.add( + variable.path, "negative_description", variable.negative_description + ) diff --git a/src/rougail/structural_commandline/config.py b/src/rougail/structural_commandline/config.py new file mode 100644 index 000000000..a8ac11264 --- /dev/null +++ b/src/rougail/structural_commandline/config.py @@ -0,0 +1,42 @@ +""" +Config file for Rougail-structural_commandline + +Silique (https://www.silique.fr) +Copyright (C) 2024 + +distribued with GPL-2 or later license + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +def get_rougail_config(*, + backward_compatibility=True, + ) -> dict: + options = """ +structural_commandline: + description: Configuration rougail-structural_commandline + commandline: false + add_extra_options: + description: Add extra options to tiramisu-cmdline-parser + default: true +""" + return {'name': 'exporter', + 'process': 'structural', + 'options': options, + 'level': 20, + } + + +__all__ = ('get_rougail_config') + diff --git a/src/rougail/structural_commandline/object_model.py b/src/rougail/structural_commandline/object_model.py index 21318e6f6..fe5446e2e 100644 --- a/src/rougail/structural_commandline/object_model.py +++ b/src/rougail/structural_commandline/object_model.py @@ -26,6 +26,11 @@ from pydantic import BaseModel class Variable(BaseModel): alternative_name: Optional[str]=None commandline: bool=True + negative_description: Optional[str]=None -__all__ = ('Variable',) +class Family(BaseModel): + commandline: bool=True + + +__all__ = ('Variable', 'Family')