WIP: Expand the developer documentation #27
20 changed files with 649 additions and 422 deletions
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
from tiramisu import Config, undefined
|
||||
from tiramisu.error import PropertiesOptionError, LeadershipError, ConfigError
|
||||
from warnings import warn
|
||||
|
@ -40,18 +41,21 @@ from .object_model import CONVERT_OPTION
|
|||
from .utils import normalize_family
|
||||
|
||||
|
||||
def tiramisu_display_name(kls,
|
||||
subconfig,
|
||||
with_quote: bool=False,
|
||||
) -> str:
|
||||
def tiramisu_display_name(
|
||||
kls,
|
||||
subconfig,
|
||||
with_quote: bool = False,
|
||||
) -> str:
|
||||
"""Replace the Tiramisu display_name function to display path + description"""
|
||||
doc = kls._get_information(subconfig, "doc", None)
|
||||
comment = f" ({doc})" if doc and doc != kls.impl_getname() else ""
|
||||
if "{{ identifier }}" in comment:
|
||||
comment = comment.replace('{{ identifier }}', str(subconfig.identifiers[-1]))
|
||||
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
|
||||
path = kls.impl_getpath()
|
||||
if "{{ identifier }}" in path and subconfig.identifiers:
|
||||
path = path.replace('{{ identifier }}', normalize_family(str(subconfig.identifiers[-1])))
|
||||
path = path.replace(
|
||||
"{{ identifier }}", normalize_family(str(subconfig.identifiers[-1]))
|
||||
)
|
||||
if with_quote:
|
||||
return f'"{path}"{comment}'
|
||||
return f"{path}{comment}"
|
||||
|
@ -83,7 +87,10 @@ class Rougail:
|
|||
if not self.config:
|
||||
tiram_obj = self.converted.save(self.rougailconfig["tiramisu_cache"])
|
||||
optiondescription = {}
|
||||
custom_types = {custom.__name__: custom for custom in self.rougailconfig["custom_types"].values()}
|
||||
custom_types = {
|
||||
custom.__name__: custom
|
||||
for custom in self.rougailconfig["custom_types"].values()
|
||||
}
|
||||
exec(tiram_obj, custom_types, optiondescription) # pylint: disable=W0122
|
||||
self.config = Config(
|
||||
optiondescription["option_0"],
|
||||
|
@ -93,22 +100,26 @@ class Rougail:
|
|||
return self.config
|
||||
|
||||
def get_config(self):
|
||||
warn("get_config is deprecated, use run instead", DeprecationWarning, stacklevel=2)
|
||||
warn(
|
||||
"get_config is deprecated, use run instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.run()
|
||||
|
||||
def user_datas(self,
|
||||
user_datas: List[dict]):
|
||||
def user_datas(self, user_datas: List[dict]):
|
||||
values = {}
|
||||
errors = []
|
||||
warnings = []
|
||||
for datas in user_datas:
|
||||
options = datas.get('options', {})
|
||||
for name, data in datas.get('values', {}).items():
|
||||
values[name] = {'values': data,
|
||||
'options': options.copy(),
|
||||
}
|
||||
errors.extend(datas.get('errors', []))
|
||||
warnings.extend(datas.get('warnings', []))
|
||||
options = datas.get("options", {})
|
||||
for name, data in datas.get("values", {}).items():
|
||||
values[name] = {
|
||||
"values": data,
|
||||
"options": options.copy(),
|
||||
}
|
||||
errors.extend(datas.get("errors", []))
|
||||
warnings.extend(datas.get("warnings", []))
|
||||
self._auto_configure_dynamics(values)
|
||||
while values:
|
||||
value_is_set = False
|
||||
|
@ -116,21 +127,21 @@ class Rougail:
|
|||
path = option.path()
|
||||
if path not in values:
|
||||
path = path.upper()
|
||||
options = values.get(path, {}).get('options', {})
|
||||
if path not in values or options.get('upper') is not True:
|
||||
options = values.get(path, {}).get("options", {})
|
||||
if path not in values or options.get("upper") is not True:
|
||||
continue
|
||||
else:
|
||||
options = values[path].get('options', {})
|
||||
options = values[path].get("options", {})
|
||||
value = values[path]["values"]
|
||||
if option.ismulti():
|
||||
if options.get('multi_separator') and not isinstance(value, list):
|
||||
value = value.split(options['multi_separator'])
|
||||
if options.get("multi_separator") and not isinstance(value, list):
|
||||
value = value.split(options["multi_separator"])
|
||||
values[path]["values"] = value
|
||||
if options.get('needs_convert'):
|
||||
if options.get("needs_convert"):
|
||||
value = [convert_value(option, val) for val in value]
|
||||
values[path]["values"] = value
|
||||
values[path]["options"]["needs_convert"] = False
|
||||
elif options.get('needs_convert'):
|
||||
elif options.get("needs_convert"):
|
||||
value = convert_value(option, value)
|
||||
index = option.index()
|
||||
if index is not None:
|
||||
|
@ -141,8 +152,8 @@ class Rougail:
|
|||
option.value.set(value)
|
||||
value_is_set = True
|
||||
if index is not None:
|
||||
values[path]['values'][index] = undefined
|
||||
if set(values[path]['values']) == {undefined}:
|
||||
values[path]["values"][index] = undefined
|
||||
if set(values[path]["values"]) == {undefined}:
|
||||
values.pop(path)
|
||||
else:
|
||||
values.pop(path)
|
||||
|
@ -154,7 +165,7 @@ class Rougail:
|
|||
for path, data in values.items():
|
||||
try:
|
||||
option = self.config.option(path)
|
||||
value = data['values']
|
||||
value = data["values"]
|
||||
if option.isfollower():
|
||||
for index, val in enumerate(value):
|
||||
if val is undefined:
|
||||
|
@ -165,14 +176,15 @@ class Rougail:
|
|||
except AttributeError as err:
|
||||
errors.append(str(err))
|
||||
except (ValueError, LeadershipError) as err:
|
||||
#errors.append(str(err).replace('"', "'"))
|
||||
# errors.append(str(err).replace('"', "'"))
|
||||
errors.append(str(err))
|
||||
except PropertiesOptionError as err:
|
||||
# warnings.append(f'"{err}" but is defined in "{self.filename}"')
|
||||
# warnings.append(f'"{err}" but is defined in "{self.filename}"')
|
||||
warnings.append(str(err))
|
||||
return {'errors': errors,
|
||||
'warnings': warnings,
|
||||
}
|
||||
return {
|
||||
"errors": errors,
|
||||
"warnings": warnings,
|
||||
}
|
||||
|
||||
def _get_variable(self, config):
|
||||
for subconfig in config:
|
||||
|
@ -181,14 +193,15 @@ class Rougail:
|
|||
else:
|
||||
yield subconfig
|
||||
|
||||
def _auto_configure_dynamics(self,
|
||||
values,
|
||||
):
|
||||
def _auto_configure_dynamics(
|
||||
self,
|
||||
values,
|
||||
):
|
||||
cache = {}
|
||||
added = []
|
||||
for path, data in list(values.items()):
|
||||
value = data['values']
|
||||
# for value in data['values'].items():
|
||||
value = data["values"]
|
||||
# for value in data['values'].items():
|
||||
try:
|
||||
option = self.config.option(path)
|
||||
option.name()
|
||||
|
@ -196,11 +209,11 @@ class Rougail:
|
|||
pass
|
||||
except AttributeError:
|
||||
config = self.config
|
||||
current_path = ''
|
||||
current_path = ""
|
||||
identifiers = []
|
||||
for name in path.split('.')[:-1]:
|
||||
for name in path.split(".")[:-1]:
|
||||
if current_path:
|
||||
current_path += '.'
|
||||
current_path += "."
|
||||
current_path += name
|
||||
if current_path in cache:
|
||||
config, identifier = cache[current_path]
|
||||
|
@ -213,66 +226,86 @@ class Rougail:
|
|||
except AttributeError:
|
||||
for tconfig in config.list(uncalculated=True):
|
||||
if tconfig.isdynamic(only_self=True):
|
||||
identifier = self._get_identifier(tconfig.name(), name)
|
||||
identifier = self._get_identifier(
|
||||
tconfig.name(), name
|
||||
)
|
||||
if identifier is None:
|
||||
continue
|
||||
dynamic_variable = tconfig.information.get('dynamic_variable',
|
||||
None,
|
||||
)
|
||||
dynamic_variable = tconfig.information.get(
|
||||
"dynamic_variable",
|
||||
None,
|
||||
)
|
||||
if not dynamic_variable:
|
||||
continue
|
||||
option_type = self.config.option(dynamic_variable).information.get('type')
|
||||
option_type = self.config.option(
|
||||
dynamic_variable
|
||||
).information.get("type")
|
||||
if identifiers:
|
||||
for s in identifiers:
|
||||
dynamic_variable = dynamic_variable.replace('{{ identifier }}', str(s), 1)
|
||||
dynamic_variable = dynamic_variable.replace(
|
||||
"{{ identifier }}", str(s), 1
|
||||
)
|
||||
if dynamic_variable not in values:
|
||||
values[dynamic_variable] = {'values': []}
|
||||
values[dynamic_variable] = {"values": []}
|
||||
added.append(dynamic_variable)
|
||||
elif dynamic_variable not in added:
|
||||
continue
|
||||
config = tconfig
|
||||
# option_type = option.information.get('type')
|
||||
typ = CONVERT_OPTION.get(option_type, {}).get("func")
|
||||
# option_type = option.information.get('type')
|
||||
typ = CONVERT_OPTION.get(option_type, {}).get(
|
||||
"func"
|
||||
)
|
||||
if typ:
|
||||
identifier = typ(identifier)
|
||||
if identifier not in values[dynamic_variable]['values']:
|
||||
values[dynamic_variable]['values'].append(identifier)
|
||||
if (
|
||||
identifier
|
||||
not in values[dynamic_variable]["values"]
|
||||
):
|
||||
values[dynamic_variable]["values"].append(
|
||||
identifier
|
||||
)
|
||||
identifiers.append(identifier)
|
||||
cache[current_path] = config, identifier
|
||||
break
|
||||
else:
|
||||
if option.isdynamic():
|
||||
parent_option = self.config.option(path.rsplit('.', 1)[0])
|
||||
identifiers = self._get_identifier(parent_option.name(uncalculated=True),
|
||||
parent_option.name(),
|
||||
)
|
||||
parent_option = self.config.option(path.rsplit(".", 1)[0])
|
||||
identifiers = self._get_identifier(
|
||||
parent_option.name(uncalculated=True),
|
||||
parent_option.name(),
|
||||
)
|
||||
dynamic_variable = None
|
||||
while True:
|
||||
dynamic_variable = parent_option.information.get('dynamic_variable',
|
||||
None,
|
||||
)
|
||||
dynamic_variable = parent_option.information.get(
|
||||
"dynamic_variable",
|
||||
None,
|
||||
)
|
||||
if dynamic_variable:
|
||||
break
|
||||
parent_option = self.config.option(parent_option.path().rsplit('.', 1)[0])
|
||||
if '.' not in parent_option.path():
|
||||
parent_option = self.config.option(
|
||||
parent_option.path().rsplit(".", 1)[0]
|
||||
)
|
||||
if "." not in parent_option.path():
|
||||
parent_option = None
|
||||
break
|
||||
if not parent_option:
|
||||
continue
|
||||
identifiers = parent_option.identifiers()
|
||||
for identifier in identifiers:
|
||||
dynamic_variable = dynamic_variable.replace('{{ identifier }}', str(identifier), 1)
|
||||
dynamic_variable = dynamic_variable.replace(
|
||||
"{{ identifier }}", str(identifier), 1
|
||||
)
|
||||
if dynamic_variable not in values:
|
||||
values[dynamic_variable] = {'values': []}
|
||||
values[dynamic_variable] = {"values": []}
|
||||
added.append(dynamic_variable)
|
||||
elif dynamic_variable not in added:
|
||||
continue
|
||||
option_type = option.information.get('type')
|
||||
option_type = option.information.get("type")
|
||||
typ = CONVERT_OPTION.get(option_type, {}).get("func")
|
||||
if typ:
|
||||
identifier = typ(identifier)
|
||||
if identifier not in values[dynamic_variable]['values']:
|
||||
values[dynamic_variable]['values'].append(identifier)
|
||||
if identifier not in values[dynamic_variable]["values"]:
|
||||
values[dynamic_variable]["values"].append(identifier)
|
||||
cache[option.path()] = option, identifier
|
||||
|
||||
def _get_identifier(self, true_name, name) -> str:
|
||||
|
@ -282,10 +315,11 @@ class Rougail:
|
|||
return
|
||||
return finded[0]
|
||||
|
||||
|
||||
def convert_value(option, value):
|
||||
if value == '':
|
||||
if value == "":
|
||||
return None
|
||||
option_type = option.information.get('type')
|
||||
option_type = option.information.get("type")
|
||||
func = CONVERT_OPTION.get(option_type, {}).get("func")
|
||||
if func:
|
||||
return func(value)
|
||||
|
|
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
import importlib.resources
|
||||
from os.path import isfile
|
||||
from ..utils import load_modules
|
||||
|
@ -68,20 +69,20 @@ class SpaceAnnotator: # pylint: disable=R0903
|
|||
get_annotators(ANNOTATORS, extra_annotator)
|
||||
for plugin in objectspace.plugins:
|
||||
try:
|
||||
get_annotators(ANNOTATORS, f'rougail.{plugin}.annotator')
|
||||
get_annotators(ANNOTATORS, f"rougail.{plugin}.annotator")
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
annotators = ANNOTATORS["rougail.annotator"].copy()
|
||||
for extra_annotator in objectspace.extra_annotators:
|
||||
annotators.extend(ANNOTATORS[extra_annotator])
|
||||
for plugin in objectspace.plugins:
|
||||
annotators.extend(ANNOTATORS[f'rougail.{plugin}.annotator'])
|
||||
annotators.extend(ANNOTATORS[f"rougail.{plugin}.annotator"])
|
||||
annotators = sorted(annotators, key=get_level)
|
||||
functions = {}
|
||||
functions_files = objectspace.functions_files
|
||||
for functions_file in functions_files:
|
||||
if isfile(functions_file):
|
||||
loaded_modules = load_modules('function_file', functions_file)
|
||||
loaded_modules = load_modules("function_file", functions_file)
|
||||
for function in dir(loaded_modules):
|
||||
if function.startswith("_"):
|
||||
continue
|
||||
|
|
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
@ -69,8 +70,7 @@ class Annotator(Walk):
|
|||
self.family_description()
|
||||
if self.objectspace.modes_level:
|
||||
self.modes = {
|
||||
name: Mode(idx)
|
||||
for idx, name in enumerate(self.objectspace.modes_level)
|
||||
name: Mode(idx) for idx, name in enumerate(self.objectspace.modes_level)
|
||||
}
|
||||
self.default_variable_mode = self.objectspace.default_variable_mode
|
||||
self.default_family_mode = self.objectspace.default_family_mode
|
||||
|
@ -100,7 +100,10 @@ class Annotator(Walk):
|
|||
if isinstance(family, self.objectspace.family) and not self._has_variable(
|
||||
family.path
|
||||
):
|
||||
if self.objectspace.paths.default_namespace is None or "." in family.path:
|
||||
if (
|
||||
self.objectspace.paths.default_namespace is None
|
||||
or "." in family.path
|
||||
):
|
||||
removed_families.append(family.path)
|
||||
removed_families.reverse()
|
||||
for family in removed_families:
|
||||
|
@ -122,10 +125,13 @@ class Annotator(Walk):
|
|||
for family in self.get_families():
|
||||
if not family.description:
|
||||
family.description = family.name
|
||||
if family.type == 'dynamic' and isinstance(family.dynamic, VariableCalculation):
|
||||
path = self.objectspace.paths.get_full_path(family.dynamic.variable,
|
||||
family.path,
|
||||
)
|
||||
if family.type == "dynamic" and isinstance(
|
||||
family.dynamic, VariableCalculation
|
||||
):
|
||||
path = self.objectspace.paths.get_full_path(
|
||||
family.dynamic.variable,
|
||||
family.path,
|
||||
)
|
||||
self.objectspace.informations.add(family.path, "dynamic_variable", path)
|
||||
|
||||
def change_modes(self):
|
||||
|
@ -153,14 +159,18 @@ class Annotator(Walk):
|
|||
for family in families:
|
||||
self._change_family_mode(family)
|
||||
if self.objectspace.paths.default_namespace is None:
|
||||
for variable_path in self.objectspace.parents['.']:
|
||||
for variable_path in self.objectspace.parents["."]:
|
||||
variable = self.objectspace.paths[variable_path]
|
||||
if variable.type == "symlink" or variable_path in self.objectspace.families:
|
||||
if (
|
||||
variable.type == "symlink"
|
||||
or variable_path in self.objectspace.families
|
||||
):
|
||||
continue
|
||||
self._set_default_mode_variable(variable,
|
||||
self.default_variable_mode,
|
||||
check_level=False,
|
||||
)
|
||||
self._set_default_mode_variable(
|
||||
variable,
|
||||
self.default_variable_mode,
|
||||
check_level=False,
|
||||
)
|
||||
|
||||
def valid_mode(
|
||||
self,
|
||||
|
@ -220,7 +230,7 @@ class Annotator(Walk):
|
|||
self,
|
||||
variable: "self.objectspace.variable",
|
||||
family_mode: Optional[str],
|
||||
check_level: bool=True,
|
||||
check_level: bool = True,
|
||||
) -> None:
|
||||
# auto_save variable is set to 'basic' mode
|
||||
# if its mode is not defined by the user
|
||||
|
@ -234,7 +244,11 @@ class Annotator(Walk):
|
|||
and variable.path not in self.objectspace.default_multi
|
||||
):
|
||||
variable_mode = self.objectspace.modes_level[0]
|
||||
if check_level and family_mode and self.modes[variable_mode] < self.modes[family_mode]:
|
||||
if (
|
||||
check_level
|
||||
and family_mode
|
||||
and self.modes[variable_mode] < self.modes[family_mode]
|
||||
):
|
||||
msg = _(
|
||||
f'the variable "{variable.name}" is mandatory so in "{variable_mode}" mode '
|
||||
f'but family has the higher family mode "{family_mode}"'
|
||||
|
@ -261,9 +275,7 @@ class Annotator(Walk):
|
|||
if leader == follower:
|
||||
# it's a leader
|
||||
if not leader.mode:
|
||||
self._set_auto_mode(
|
||||
leader, self.default_variable_mode
|
||||
)
|
||||
self._set_auto_mode(leader, self.default_variable_mode)
|
||||
return
|
||||
if self._has_mode(follower):
|
||||
follower_mode = follower.mode
|
||||
|
@ -310,8 +322,10 @@ class Annotator(Walk):
|
|||
# set the lower variable mode to family
|
||||
self._set_auto_mode(family, min_variable_mode)
|
||||
if self.modes[family.mode] < self.modes[min_variable_mode]:
|
||||
msg = _(f'the family "{family.name}" is in "{family.mode}" mode but variables and '
|
||||
f'families inside have the higher modes "{min_variable_mode}"')
|
||||
msg = _(
|
||||
f'the family "{family.name}" is in "{family.mode}" mode but variables and '
|
||||
f'families inside have the higher modes "{min_variable_mode}"'
|
||||
)
|
||||
raise DictConsistencyError(msg, 62, family.xmlfiles)
|
||||
|
||||
def _change_variable_mode(
|
||||
|
|
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
@ -123,7 +124,7 @@ class Annotator(Walk):
|
|||
value = []
|
||||
for calculation in frozen:
|
||||
calculation_copy = calculation.copy()
|
||||
calculation_copy.attribute_name = 'frozen'
|
||||
calculation_copy.attribute_name = "frozen"
|
||||
calculation_copy.ori_path = calculation_copy.path
|
||||
calculation_copy.path = path
|
||||
value.append(calculation_copy)
|
||||
|
|
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
from rougail.i18n import _
|
||||
|
@ -55,7 +56,7 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
for variable in self.get_variables():
|
||||
if variable.type == "symlink":
|
||||
continue
|
||||
if variable.version != '1.0' and variable.type == 'port':
|
||||
if variable.version != "1.0" and variable.type == "port":
|
||||
self._convert_port(variable)
|
||||
self._convert_value(variable)
|
||||
|
||||
|
@ -85,16 +86,16 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
else:
|
||||
if variable.path not in self.objectspace.leaders:
|
||||
if multi == "submulti":
|
||||
self.objectspace.default_multi[
|
||||
variable.path
|
||||
] = variable.default
|
||||
self.objectspace.default_multi[variable.path] = variable.default
|
||||
variable.default = None
|
||||
else:
|
||||
self.objectspace.default_multi[variable.path] = variable.default[
|
||||
0
|
||||
]
|
||||
self.objectspace.default_multi[variable.path] = (
|
||||
variable.default[0]
|
||||
)
|
||||
elif variable.multi:
|
||||
msg = _(f'the variable "{variable.name}" is multi but has a non list default value')
|
||||
msg = _(
|
||||
f'the variable "{variable.name}" is multi but has a non list default value'
|
||||
)
|
||||
raise DictConsistencyError(msg, 12, variable.xmlfiles)
|
||||
elif variable.path in self.objectspace.followers:
|
||||
self.objectspace.default_multi[variable.path] = variable.default
|
||||
|
|
|
@ -66,15 +66,18 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
return
|
||||
self.objectspace = objectspace
|
||||
if self.objectspace.main_namespace:
|
||||
self.forbidden_name = [
|
||||
self.objectspace.main_namespace
|
||||
]
|
||||
self.forbidden_name = [self.objectspace.main_namespace]
|
||||
for extra in self.objectspace.extra_dictionaries:
|
||||
self.forbidden_name.append(extra)
|
||||
else:
|
||||
self.forbidden_name = []
|
||||
# default type inference from a default value with :term:`basic types`
|
||||
self.basic_types = {str: "string", int: "number", bool: "boolean", float: "float"}
|
||||
self.basic_types = {
|
||||
str: "string",
|
||||
int: "number",
|
||||
bool: "boolean",
|
||||
float: "float",
|
||||
}
|
||||
self.verify_choices()
|
||||
self.convert_variable()
|
||||
self.convert_test()
|
||||
|
@ -97,14 +100,12 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
variable.multi = False
|
||||
if variable.type is None:
|
||||
variable.type = "string"
|
||||
self.objectspace.informations.add(
|
||||
variable.path, "type", variable.type
|
||||
)
|
||||
self.objectspace.informations.add(variable.path, "type", variable.type)
|
||||
self._convert_variable(variable)
|
||||
|
||||
def _convert_variable_inference(
|
||||
self,
|
||||
variable,
|
||||
self,
|
||||
variable,
|
||||
) -> None:
|
||||
# variable has no type
|
||||
if variable.type is None:
|
||||
|
@ -120,23 +121,32 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
tested_value = variable.default
|
||||
variable.type = self.basic_types.get(type(tested_value), None)
|
||||
# variable has no multi attribute
|
||||
if variable.multi is None and not (variable.type is None and isinstance(variable.default, VariableCalculation)):
|
||||
if variable.multi is None and not (
|
||||
variable.type is None and isinstance(variable.default, VariableCalculation)
|
||||
):
|
||||
if variable.path in self.objectspace.leaders:
|
||||
variable.multi = True
|
||||
else:
|
||||
variable.multi = isinstance(variable.default, list)
|
||||
|
||||
def _default_variable_copy_informations(
|
||||
self,
|
||||
variable,
|
||||
self,
|
||||
variable,
|
||||
) -> None:
|
||||
# if a variable has a variable as default value, that means the type/params or multi should has same value
|
||||
if variable.type is not None or not isinstance(variable.default, VariableCalculation):
|
||||
if variable.type is not None or not isinstance(
|
||||
variable.default, VariableCalculation
|
||||
):
|
||||
return
|
||||
# copy type and params
|
||||
calculated_variable_path = variable.default.variable
|
||||
calculated_variable, identifier = self.objectspace.paths.get_with_dynamic(
|
||||
calculated_variable_path, variable.default.path_prefix, variable.path, variable.version, variable.namespace, variable.xmlfiles
|
||||
calculated_variable_path,
|
||||
variable.default.path_prefix,
|
||||
variable.path,
|
||||
variable.version,
|
||||
variable.namespace,
|
||||
variable.xmlfiles,
|
||||
)
|
||||
if calculated_variable is None:
|
||||
return
|
||||
|
@ -146,7 +156,11 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
# copy multi attribut
|
||||
if variable.multi is None:
|
||||
calculated_path = calculated_variable.path
|
||||
if calculated_path in self.objectspace.leaders and variable.path in self.objectspace.followers and calculated_path.rsplit('.')[0] == variable.path.rsplit('.')[0]:
|
||||
if (
|
||||
calculated_path in self.objectspace.leaders
|
||||
and variable.path in self.objectspace.followers
|
||||
and calculated_path.rsplit(".")[0] == variable.path.rsplit(".")[0]
|
||||
):
|
||||
variable.multi = False
|
||||
else:
|
||||
variable.multi = calculated_variable.multi
|
||||
|
@ -171,11 +185,13 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
family = self.objectspace.paths[variable.path.rsplit(".", 1)[0]]
|
||||
if variable.hidden:
|
||||
family.hidden = variable.hidden
|
||||
# elif family.hidden:
|
||||
# variable.hidden = family.hidden
|
||||
# elif family.hidden:
|
||||
# variable.hidden = family.hidden
|
||||
variable.hidden = None
|
||||
if variable.regexp is not None and variable.type != 'regexp':
|
||||
msg = _(f'the variable "{variable.path}" has regexp attribut but has not the "regexp" type')
|
||||
if variable.regexp is not None and variable.type != "regexp":
|
||||
msg = _(
|
||||
f'the variable "{variable.path}" has regexp attribut but has not the "regexp" type'
|
||||
)
|
||||
raise DictConsistencyError(msg, 37, variable.xmlfiles)
|
||||
if variable.mandatory is None:
|
||||
variable.mandatory = True
|
||||
|
@ -215,10 +231,12 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
if variable.type is None and variable.choices:
|
||||
# choice type inference from the `choices` attribute
|
||||
variable.type = "choice"
|
||||
if variable.choices is not None and variable.type != 'choice':
|
||||
msg = _(f'the variable "{variable.path}" has choices attribut but has not the "choice" type')
|
||||
if variable.choices is not None and variable.type != "choice":
|
||||
msg = _(
|
||||
f'the variable "{variable.path}" has choices attribut but has not the "choice" type'
|
||||
)
|
||||
raise DictConsistencyError(msg, 11, variable.xmlfiles)
|
||||
if variable.type != 'choice':
|
||||
if variable.type != "choice":
|
||||
continue
|
||||
if variable.default is None:
|
||||
continue
|
||||
|
@ -242,5 +260,7 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
if isinstance(value, Calculation):
|
||||
continue
|
||||
if value not in choices:
|
||||
msg = _(f'the variable "{variable.path}" has an unvalid default value "{value}" should be in {display_list(choices, separator="or", add_quote=True)}')
|
||||
msg = _(
|
||||
f'the variable "{variable.path}" has an unvalid default value "{value}" should be in {display_list(choices, separator="or", add_quote=True)}'
|
||||
)
|
||||
raise DictConsistencyError(msg, 26, variable.xmlfiles)
|
||||
|
|
|
@ -28,6 +28,7 @@ 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
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from tiramisu import Config
|
||||
from ruamel.yaml import YAML
|
||||
|
@ -35,12 +36,14 @@ from .utils import _, load_modules, normalize_family
|
|||
from .convert import RougailConvert
|
||||
|
||||
|
||||
RENAMED = {'dictionaries_dir': 'main_dictionaries',
|
||||
'variable_namespace': 'main_namespace',
|
||||
'functions_file': 'functions_files',
|
||||
}
|
||||
NOT_IN_TIRAMISU = {'custom_types': {},
|
||||
}
|
||||
RENAMED = {
|
||||
"dictionaries_dir": "main_dictionaries",
|
||||
"variable_namespace": "main_namespace",
|
||||
"functions_file": "functions_files",
|
||||
}
|
||||
NOT_IN_TIRAMISU = {
|
||||
"custom_types": {},
|
||||
}
|
||||
SUBMODULES = None
|
||||
|
||||
|
||||
|
@ -49,29 +52,27 @@ def get_sub_modules():
|
|||
if SUBMODULES is None:
|
||||
SUBMODULES = {}
|
||||
for submodule in Path(__file__).parent.iterdir():
|
||||
if submodule.name.startswith('_') or not submodule.is_dir():
|
||||
if submodule.name.startswith("_") or not submodule.is_dir():
|
||||
continue
|
||||
config_file = submodule / 'config.py'
|
||||
config_file = submodule / "config.py"
|
||||
if config_file.is_file():
|
||||
SUBMODULES[submodule.name] = load_modules('rougail.' + submodule.name + '.config', str(config_file))
|
||||
SUBMODULES[submodule.name] = load_modules(
|
||||
"rougail." + submodule.name + ".config", str(config_file)
|
||||
)
|
||||
return SUBMODULES
|
||||
|
||||
|
||||
def get_level(module):
|
||||
return module['level']
|
||||
return module["level"]
|
||||
|
||||
|
||||
class _RougailConfig:
|
||||
def __init__(self,
|
||||
backward_compatibility: bool,
|
||||
root,
|
||||
extra_vars: dict
|
||||
):
|
||||
def __init__(self, backward_compatibility: bool, root, extra_vars: dict):
|
||||
self.backward_compatibility = backward_compatibility
|
||||
self.root = root
|
||||
self.config = Config(
|
||||
self.root,
|
||||
)
|
||||
self.root,
|
||||
)
|
||||
self.config.property.read_only()
|
||||
self.extra_vars = extra_vars
|
||||
self.not_in_tiramisu = NOT_IN_TIRAMISU | extra_vars
|
||||
|
@ -81,7 +82,9 @@ class _RougailConfig:
|
|||
setattr(self, variable, default_value)
|
||||
|
||||
def copy(self):
|
||||
rougailconfig = _RougailConfig(self.backward_compatibility, self.root, self.extra_vars)
|
||||
rougailconfig = _RougailConfig(
|
||||
self.backward_compatibility, self.root, self.extra_vars
|
||||
)
|
||||
rougailconfig.config.value.importation(self.config.value.exportation())
|
||||
rougailconfig.config.property.importation(self.config.property.exportation())
|
||||
rougailconfig.config.property.read_only()
|
||||
|
@ -92,16 +95,17 @@ class _RougailConfig:
|
|||
setattr(rougailconfig, variable, value)
|
||||
return rougailconfig
|
||||
|
||||
def __setitem__(self,
|
||||
key,
|
||||
value,
|
||||
) -> None:
|
||||
def __setitem__(
|
||||
self,
|
||||
key,
|
||||
value,
|
||||
) -> None:
|
||||
if key in self.not_in_tiramisu:
|
||||
setattr(self, key, value)
|
||||
else:
|
||||
self.config.property.read_write()
|
||||
if key == 'export_with_import':
|
||||
key = 'not_export_with_import'
|
||||
if key == "export_with_import":
|
||||
key = "not_export_with_import"
|
||||
key = RENAMED.get(key, key)
|
||||
option = self.config.option(key)
|
||||
if option.isoptiondescription() and option.isleadership():
|
||||
|
@ -111,30 +115,29 @@ class _RougailConfig:
|
|||
follower = option.followers()[0]
|
||||
for idx, val in enumerate(value.values()):
|
||||
self.config.option(follower.path(), idx).value.set(val)
|
||||
elif key == 'not_export_with_import':
|
||||
elif key == "not_export_with_import":
|
||||
option.value.set(not value)
|
||||
else:
|
||||
option.value.set(value)
|
||||
self.config.property.read_only()
|
||||
|
||||
def __getitem__(self,
|
||||
key,
|
||||
) -> None:
|
||||
def __getitem__(
|
||||
self,
|
||||
key,
|
||||
) -> None:
|
||||
if key in self.not_in_tiramisu:
|
||||
return getattr(self, key)
|
||||
if key == 'export_with_import':
|
||||
key = 'not_export_with_import'
|
||||
if key == "export_with_import":
|
||||
key = "not_export_with_import"
|
||||
option = self.config.option(key)
|
||||
if option.isoptiondescription() and option.isleadership():
|
||||
return self.get_leadership(option)
|
||||
ret = self.config.option(key).value.get()
|
||||
if key == 'not_export_with_import':
|
||||
if key == "not_export_with_import":
|
||||
return not ret
|
||||
return ret
|
||||
|
||||
def get_leadership(self,
|
||||
option
|
||||
) -> dict:
|
||||
def get_leadership(self, option) -> dict:
|
||||
leader = None
|
||||
followers = []
|
||||
for opt, value in option.value.get().items():
|
||||
|
@ -151,7 +154,7 @@ class _RougailConfig:
|
|||
if option.isoptiondescription():
|
||||
yield from self.parse(option)
|
||||
elif not option.issymlinkoption():
|
||||
yield f'{option.path()}: {option.value.get()}'
|
||||
yield f"{option.path()}: {option.value.get()}"
|
||||
|
||||
def __repr__(self):
|
||||
self.config.property.read_write()
|
||||
|
@ -164,16 +167,17 @@ class _RougailConfig:
|
|||
|
||||
|
||||
class FakeRougailConvert(RougailConvert):
|
||||
def __init__(self,
|
||||
add_extra_options: bool,
|
||||
) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
add_extra_options: bool,
|
||||
) -> None:
|
||||
self.add_extra_options = add_extra_options
|
||||
super().__init__({})
|
||||
|
||||
def load_config(self) -> None:
|
||||
self.sort_dictionaries_all = False
|
||||
self.main_namespace = None
|
||||
self.suffix = ''
|
||||
self.suffix = ""
|
||||
self.custom_types = {}
|
||||
self.functions_files = []
|
||||
self.modes_level = []
|
||||
|
@ -181,18 +185,19 @@ class FakeRougailConvert(RougailConvert):
|
|||
self.base_option_name = "baseoption"
|
||||
self.export_with_import = True
|
||||
self.internal_functions = []
|
||||
self.plugins = ['structural_commandline']
|
||||
self.plugins = ["structural_commandline"]
|
||||
self.add_extra_options = self.add_extra_options
|
||||
|
||||
|
||||
def get_rougail_config(*,
|
||||
backward_compatibility: bool=True,
|
||||
add_extra_options: bool=True,
|
||||
) -> _RougailConfig:
|
||||
def get_rougail_config(
|
||||
*,
|
||||
backward_compatibility: bool = True,
|
||||
add_extra_options: bool = True,
|
||||
) -> _RougailConfig:
|
||||
if backward_compatibility:
|
||||
main_namespace_default = 'rougail'
|
||||
main_namespace_default = "rougail"
|
||||
else:
|
||||
main_namespace_default = 'null'
|
||||
main_namespace_default = "null"
|
||||
rougail_options = f"""default_dictionary_format_version:
|
||||
description: Dictionary format version by default, if not specified in dictionary file
|
||||
alternative_name: v
|
||||
|
@ -386,13 +391,14 @@ suffix:
|
|||
mandatory: false
|
||||
commandline: false
|
||||
"""
|
||||
processes = {'structural': [],
|
||||
'output': [],
|
||||
'user data': [],
|
||||
}
|
||||
processes = {
|
||||
"structural": [],
|
||||
"output": [],
|
||||
"user data": [],
|
||||
}
|
||||
for module in get_sub_modules().values():
|
||||
data = module.get_rougail_config()
|
||||
processes[data['process']].append(data)
|
||||
processes[data["process"]].append(data)
|
||||
# reorder
|
||||
for process in processes:
|
||||
processes[process] = list(sorted(processes[process], key=get_level))
|
||||
|
@ -407,17 +413,22 @@ suffix:
|
|||
description: Select for {NAME}
|
||||
alternative_name: {NAME[0]}
|
||||
choices:
|
||||
""".format(NAME=normalize_family(process),
|
||||
)
|
||||
""".format(
|
||||
NAME=normalize_family(process),
|
||||
)
|
||||
for obj in objects:
|
||||
rougail_process += f" - {obj['name']}\n"
|
||||
if process == 'structural':
|
||||
if process == "structural":
|
||||
rougail_process += " commandline: false"
|
||||
elif process == 'user data':
|
||||
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)]
|
||||
hidden_outputs = [
|
||||
process["name"]
|
||||
for process in processes["output"]
|
||||
if not process.get("allow_user_data", True)
|
||||
]
|
||||
if hidden_outputs:
|
||||
rougail_process += """ hidden:
|
||||
type: jinja
|
||||
|
@ -427,9 +438,13 @@ suffix:
|
|||
rougail_process += """ {% if _.output == 'NAME' %}
|
||||
Cannot load user data for NAME output
|
||||
{% endif %}
|
||||
""".replace('NAME', hidden_output)
|
||||
""".replace(
|
||||
"NAME", hidden_output
|
||||
)
|
||||
else:
|
||||
rougail_process += ' default: {DEFAULT}'.format(DEFAULT=objects[0]['name'])
|
||||
rougail_process += " default: {DEFAULT}".format(
|
||||
DEFAULT=objects[0]["name"]
|
||||
)
|
||||
else:
|
||||
rougail_process += """
|
||||
{NAME}:
|
||||
|
@ -437,39 +452,41 @@ suffix:
|
|||
hidden: true
|
||||
mandatory: false
|
||||
multi: true
|
||||
""".format(NAME=normalize_family(process),
|
||||
)
|
||||
""".format(
|
||||
NAME=normalize_family(process),
|
||||
)
|
||||
rougail_options += rougail_process
|
||||
convert = FakeRougailConvert(add_extra_options)
|
||||
convert._init()
|
||||
convert.namespace = None
|
||||
convert.parse_root_file(
|
||||
'rougail.config',
|
||||
'',
|
||||
'1.1',
|
||||
"rougail.config",
|
||||
"",
|
||||
"1.1",
|
||||
YAML().load(rougail_options),
|
||||
)
|
||||
extra_vars = {}
|
||||
for process in processes:
|
||||
for obj in processes[process]:
|
||||
if 'extra_vars' in obj:
|
||||
extra_vars |= obj['extra_vars']
|
||||
if not 'options' in obj:
|
||||
if "extra_vars" in obj:
|
||||
extra_vars |= obj["extra_vars"]
|
||||
if not "options" in obj:
|
||||
continue
|
||||
convert.parse_root_file(
|
||||
f'rougail.config.{obj["name"]}',
|
||||
'',
|
||||
'1.1',
|
||||
YAML().load(obj['options']),
|
||||
"",
|
||||
"1.1",
|
||||
YAML().load(obj["options"]),
|
||||
)
|
||||
|
||||
tiram_obj = convert.save(None)
|
||||
optiondescription = {}
|
||||
exec(tiram_obj, {}, optiondescription) # pylint: disable=W0122
|
||||
return _RougailConfig(backward_compatibility,
|
||||
optiondescription["option_0"],
|
||||
extra_vars=extra_vars,
|
||||
)
|
||||
return _RougailConfig(
|
||||
backward_compatibility,
|
||||
optiondescription["option_0"],
|
||||
extra_vars=extra_vars,
|
||||
)
|
||||
|
||||
|
||||
RougailConfig = get_rougail_config()
|
||||
|
|
|
@ -28,6 +28,7 @@ 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
|
||||
"""
|
||||
|
||||
import logging
|
||||
from itertools import chain
|
||||
from pathlib import Path
|
||||
|
@ -133,10 +134,11 @@ class Paths:
|
|||
if not force and is_dynamic:
|
||||
self._dynamics[path] = dynamic
|
||||
|
||||
def get_full_path(self,
|
||||
path: str,
|
||||
current_path: str,
|
||||
):
|
||||
def get_full_path(
|
||||
self,
|
||||
path: str,
|
||||
current_path: str,
|
||||
):
|
||||
relative, subpath = path.split(".", 1)
|
||||
relative_len = len(relative)
|
||||
path_len = current_path.count(".")
|
||||
|
@ -155,15 +157,20 @@ class Paths:
|
|||
xmlfiles: List[str],
|
||||
) -> Any:
|
||||
identifier = None
|
||||
if version != '1.0' and self.regexp_relative.search(path):
|
||||
path = self.get_full_path(path,
|
||||
current_path,
|
||||
)
|
||||
if version != "1.0" and self.regexp_relative.search(path):
|
||||
path = self.get_full_path(
|
||||
path,
|
||||
current_path,
|
||||
)
|
||||
else:
|
||||
path = get_realpath(path, identifier_path)
|
||||
dynamic = None
|
||||
# version 1.0
|
||||
if version == "1.0" and not path in self._data and "{{ identifier }}" not in path:
|
||||
if (
|
||||
version == "1.0"
|
||||
and not path in self._data
|
||||
and "{{ identifier }}" not in path
|
||||
):
|
||||
new_path = None
|
||||
current_path = None
|
||||
for name in path.split("."):
|
||||
|
@ -179,7 +186,7 @@ class Paths:
|
|||
new_path = name
|
||||
continue
|
||||
for dynamic_path in self._dynamics:
|
||||
if '.' in dynamic_path:
|
||||
if "." in dynamic_path:
|
||||
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
|
||||
else:
|
||||
parent_dynamic = None
|
||||
|
@ -216,7 +223,7 @@ class Paths:
|
|||
parent_path = current_path
|
||||
continue
|
||||
for dynamic_path in self._dynamics:
|
||||
if '.' in dynamic_path:
|
||||
if "." in dynamic_path:
|
||||
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
|
||||
else:
|
||||
parent_dynamic = None
|
||||
|
@ -350,7 +357,7 @@ class ParserVariable:
|
|||
|
||||
def load_config(self) -> None:
|
||||
rougailconfig = self.rougailconfig
|
||||
self.sort_dictionaries_all = rougailconfig['sort_dictionaries_all']
|
||||
self.sort_dictionaries_all = rougailconfig["sort_dictionaries_all"]
|
||||
try:
|
||||
self.main_dictionaries = rougailconfig["main_dictionaries"]
|
||||
except:
|
||||
|
@ -359,7 +366,9 @@ class ParserVariable:
|
|||
if self.main_namespace:
|
||||
self.extra_dictionaries = rougailconfig["extra_dictionaries"]
|
||||
self.suffix = rougailconfig["suffix"]
|
||||
self.default_dictionary_format_version = rougailconfig["default_dictionary_format_version"]
|
||||
self.default_dictionary_format_version = rougailconfig[
|
||||
"default_dictionary_format_version"
|
||||
]
|
||||
self.custom_types = rougailconfig["custom_types"]
|
||||
self.functions_files = rougailconfig["functions_files"]
|
||||
self.modes_level = rougailconfig["modes_level"]
|
||||
|
@ -370,7 +379,9 @@ 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.add_extra_options = rougailconfig[
|
||||
"structural_commandline.add_extra_options"
|
||||
]
|
||||
self.plugins = rougailconfig["plugins"]
|
||||
|
||||
def _init(self):
|
||||
|
@ -381,14 +392,22 @@ class ParserVariable:
|
|||
if self.plugins:
|
||||
root = Path(__file__).parent
|
||||
for plugin in self.plugins:
|
||||
module_path = root / plugin / 'object_model.py'
|
||||
module_path = root / plugin / "object_model.py"
|
||||
if not module_path.is_file():
|
||||
continue
|
||||
module = load_modules(f'rougail.{plugin}.object_model', str(module_path))
|
||||
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), {})
|
||||
module = load_modules(
|
||||
f"rougail.{plugin}.object_model", str(module_path)
|
||||
)
|
||||
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), {})
|
||||
|
@ -604,26 +623,26 @@ class ParserVariable:
|
|||
obj_type = self.get_family_or_variable_type(family_obj)
|
||||
if obj_type is None:
|
||||
# auto set type
|
||||
if '_dynamic' in family_obj:
|
||||
dynamic = family_obj['_dynamic']
|
||||
elif 'dynamic' in family_obj:
|
||||
dynamic = family_obj['dynamic']
|
||||
if "_dynamic" in family_obj:
|
||||
dynamic = family_obj["_dynamic"]
|
||||
elif "dynamic" in family_obj:
|
||||
dynamic = family_obj["dynamic"]
|
||||
else:
|
||||
dynamic = None
|
||||
if isinstance(dynamic, (list, dict)):
|
||||
family_obj['type'] = obj_type = 'dynamic'
|
||||
family_obj["type"] = obj_type = "dynamic"
|
||||
if obj_type == "dynamic":
|
||||
family_is_dynamic = True
|
||||
parent_dynamic = path
|
||||
if '{{ identifier }}' not in name:
|
||||
if "{{ identifier }}" not in name:
|
||||
if "variable" in family_obj:
|
||||
name += '{{ identifier }}'
|
||||
path += '{{ identifier }}'
|
||||
name += "{{ identifier }}"
|
||||
path += "{{ identifier }}"
|
||||
else:
|
||||
msg = f'dynamic family name must have "{{{{ identifier }}}}" in his name for "{path}"'
|
||||
raise DictConsistencyError(msg, 13, [filename])
|
||||
if version != '1.0' and not family_obj and comment:
|
||||
family_obj['description'] = comment
|
||||
if version != "1.0" and not family_obj and comment:
|
||||
family_obj["description"] = comment
|
||||
self.add_family(
|
||||
path,
|
||||
name,
|
||||
|
@ -679,7 +698,11 @@ class ParserVariable:
|
|||
# it's a dict, so a new variables!
|
||||
continue
|
||||
# 'variable' for compatibility to format 1.0
|
||||
if key == "variable" and obj.get("type") != "dynamic" and obj.get("_type") != "dynamic":
|
||||
if (
|
||||
key == "variable"
|
||||
and obj.get("type") != "dynamic"
|
||||
and obj.get("_type") != "dynamic"
|
||||
):
|
||||
continue
|
||||
if key in self.family_attrs:
|
||||
yield key
|
||||
|
@ -724,17 +747,20 @@ class ParserVariable:
|
|||
del family["variable"]
|
||||
# FIXME only for 1.0
|
||||
if "variable" in family:
|
||||
family['dynamic'] = {'type': 'variable',
|
||||
'variable': family['variable'],
|
||||
'propertyerror': False,
|
||||
'allow_none': True,
|
||||
}
|
||||
del family['variable']
|
||||
family["dynamic"] = {
|
||||
"type": "variable",
|
||||
"variable": family["variable"],
|
||||
"propertyerror": False,
|
||||
"allow_none": True,
|
||||
}
|
||||
del family["variable"]
|
||||
if version != "1.0":
|
||||
warning = f'"variable" attribute in dynamic family "{ path }" is depreciated in {filename}'
|
||||
warn(warning)
|
||||
if "variable" in family:
|
||||
raise Exception(f'dynamic family must not have "variable" attribute for "{family["path"]}" in {family["xmlfiles"]}')
|
||||
raise Exception(
|
||||
f'dynamic family must not have "variable" attribute for "{family["path"]}" in {family["xmlfiles"]}'
|
||||
)
|
||||
else:
|
||||
family_obj = self.family
|
||||
# convert to Calculation objects
|
||||
|
@ -969,7 +995,7 @@ class ParserVariable:
|
|||
parent_dynamic,
|
||||
)
|
||||
self.variables.append(variable["path"])
|
||||
if '.' in variable["path"]:
|
||||
if "." in variable["path"]:
|
||||
parent_path = variable["path"].rsplit(".", 1)[0]
|
||||
else:
|
||||
parent_path = "."
|
||||
|
@ -987,10 +1013,10 @@ class ParserVariable:
|
|||
del self.paths[path]
|
||||
self.families.remove(path)
|
||||
del self.parents[path]
|
||||
if '.' in path:
|
||||
if "." in path:
|
||||
parent = path.rsplit(".", 1)[0]
|
||||
else:
|
||||
parent = '.'
|
||||
parent = "."
|
||||
self.parents[parent].remove(path)
|
||||
|
||||
###############################################################################################
|
||||
|
@ -1003,9 +1029,7 @@ class ParserVariable:
|
|||
):
|
||||
"""Set Tiramisu object name"""
|
||||
self.index += 1
|
||||
self.reflector_names[
|
||||
obj.path
|
||||
] = f'{option_prefix}{self.index}{self.suffix}'
|
||||
self.reflector_names[obj.path] = f"{option_prefix}{self.index}{self.suffix}"
|
||||
|
||||
###############################################################################################
|
||||
# calculations
|
||||
|
@ -1024,12 +1048,12 @@ class ParserVariable:
|
|||
calculations = calculations[1]
|
||||
if not isinstance(value, dict) or attribute not in calculations:
|
||||
return False
|
||||
if 'type' in value:
|
||||
return value['type'] in CALCULATION_TYPES
|
||||
if "type" in value:
|
||||
return value["type"] in CALCULATION_TYPES
|
||||
# auto set type
|
||||
typ = set(CALCULATION_TYPES) & set(value)
|
||||
if len(typ) == 1:
|
||||
value['type'] = list(typ)[0]
|
||||
value["type"] = list(typ)[0]
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -1068,7 +1092,7 @@ class ParserVariable:
|
|||
# auto set type
|
||||
param_typ = set(CALCULATION_TYPES) & set(val)
|
||||
if len(param_typ) == 1:
|
||||
val['type'] = list(param_typ)[0]
|
||||
val["type"] = list(param_typ)[0]
|
||||
if not isinstance(val, dict) or "type" not in val:
|
||||
param_typ = "any"
|
||||
val = {
|
||||
|
@ -1087,7 +1111,9 @@ class ParserVariable:
|
|||
params.append(PARAM_TYPES[param_typ](**val))
|
||||
except ValidationError as err:
|
||||
raise DictConsistencyError(
|
||||
f'"{attribute}" has an invalid "{key}" for {path}: {err}', 29, xmlfiles
|
||||
f'"{attribute}" has an invalid "{key}" for {path}: {err}',
|
||||
29,
|
||||
xmlfiles,
|
||||
) from err
|
||||
calculation_object["params"] = params
|
||||
#
|
||||
|
@ -1164,11 +1190,11 @@ class RougailConvert(ParserVariable):
|
|||
self.add_family(
|
||||
n_path_prefix,
|
||||
n_path_prefix,
|
||||
{'description': path_prefix},
|
||||
{"description": path_prefix},
|
||||
"",
|
||||
False,
|
||||
None,
|
||||
'',
|
||||
"",
|
||||
)
|
||||
else:
|
||||
root_parent = "."
|
||||
|
@ -1196,12 +1222,13 @@ class RougailConvert(ParserVariable):
|
|||
for idx, filename in enumerate(self.get_sorted_filename(extra_dirs)):
|
||||
if not idx:
|
||||
self.parse_family(
|
||||
'',
|
||||
"",
|
||||
self.namespace,
|
||||
namespace_path,
|
||||
{'description': namespace,
|
||||
},
|
||||
'',
|
||||
{
|
||||
"description": namespace,
|
||||
},
|
||||
"",
|
||||
)
|
||||
self.parse_variable_file(
|
||||
filename,
|
||||
|
@ -1210,7 +1237,7 @@ class RougailConvert(ParserVariable):
|
|||
else:
|
||||
self.namespace = None
|
||||
if root_parent == ".":
|
||||
namespace_path = ''
|
||||
namespace_path = ""
|
||||
else:
|
||||
namespace_path = f"{root_parent}"
|
||||
if namespace_path in self.parents:
|
||||
|
@ -1250,18 +1277,20 @@ class RougailConvert(ParserVariable):
|
|||
)
|
||||
if objects is None:
|
||||
return
|
||||
self.parse_root_file(filename,
|
||||
path,
|
||||
version,
|
||||
objects,
|
||||
)
|
||||
self.parse_root_file(
|
||||
filename,
|
||||
path,
|
||||
version,
|
||||
objects,
|
||||
)
|
||||
|
||||
def parse_root_file(self,
|
||||
filename: str,
|
||||
path: str,
|
||||
version: str,
|
||||
objects: dict,
|
||||
) -> None:
|
||||
def parse_root_file(
|
||||
self,
|
||||
filename: str,
|
||||
path: str,
|
||||
version: str,
|
||||
objects: dict,
|
||||
) -> None:
|
||||
for name, obj in objects.items():
|
||||
comment = self.get_comment(name, objects)
|
||||
self.family_or_variable(
|
||||
|
@ -1367,5 +1396,5 @@ class RougailConvert(ParserVariable):
|
|||
if filename:
|
||||
with open(filename, "w", encoding="utf-8") as tiramisu:
|
||||
tiramisu.write(output)
|
||||
#print(output)
|
||||
# print(output)
|
||||
return output
|
||||
|
|
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
from .i18n import _
|
||||
|
||||
|
||||
|
@ -74,13 +75,17 @@ class DictConsistencyError(Exception):
|
|||
class UpgradeError(Exception):
|
||||
"""Error during XML upgrade"""
|
||||
|
||||
|
||||
## ---- generic exceptions ----
|
||||
|
||||
|
||||
class NotFoundError(Exception):
|
||||
"not found error"
|
||||
pass
|
||||
|
||||
|
||||
## ---- specific exceptions ----
|
||||
|
||||
|
||||
class VariableCalculationDependencyError(Exception):
|
||||
pass
|
||||
|
|
|
@ -26,6 +26,7 @@ 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
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import os
|
||||
import sys
|
||||
|
|
|
@ -333,7 +333,7 @@ class JinjaCalculation(Calculation):
|
|||
"type": "variable",
|
||||
"variable": sub_variable,
|
||||
}
|
||||
if self.version != '1.0':
|
||||
if self.version != "1.0":
|
||||
default["params"][true_path]["propertyerror"] = False
|
||||
if identifier:
|
||||
default["params"][true_path]["identifier"] = identifier
|
||||
|
@ -405,9 +405,10 @@ class _VariableCalculation(Calculation):
|
|||
propertyerror: bool = True
|
||||
allow_none: bool = False
|
||||
|
||||
def get_variable(self,
|
||||
objectspace,
|
||||
) -> "Variable":
|
||||
def get_variable(
|
||||
self,
|
||||
objectspace,
|
||||
) -> "Variable":
|
||||
if self.ori_path is None:
|
||||
path = self.path
|
||||
else:
|
||||
|
@ -467,7 +468,7 @@ class _VariableCalculation(Calculation):
|
|||
calc_variable_is_multi = True
|
||||
else:
|
||||
calc_variable_is_multi = True
|
||||
elif identifier and '{{ identifier }}' in identifier:
|
||||
elif identifier and "{{ identifier }}" in identifier:
|
||||
calc_variable_is_multi = True
|
||||
if needs_multi:
|
||||
if calc_variable_is_multi:
|
||||
|
@ -481,12 +482,15 @@ class _VariableCalculation(Calculation):
|
|||
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", it\'s a list'
|
||||
raise DictConsistencyError(msg, 23, self.xmlfiles)
|
||||
elif calc_variable_is_multi:
|
||||
if variable.multi or variable.path.rsplit('.', 1)[0] != self.path.rsplit('.', 1)[0]:
|
||||
if (
|
||||
variable.multi
|
||||
or variable.path.rsplit(".", 1)[0] != self.path.rsplit(".", 1)[0]
|
||||
):
|
||||
# it's not a follower or not in same leadership
|
||||
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is a multi'
|
||||
raise DictConsistencyError(msg, 21, self.xmlfiles)
|
||||
else:
|
||||
params[None][0]['index'] = {'index': {'type': 'index'}}
|
||||
params[None][0]["index"] = {"index": {"type": "index"}}
|
||||
return params
|
||||
|
||||
|
||||
|
@ -504,10 +508,11 @@ class VariableCalculation(_VariableCalculation):
|
|||
variable, identifier = self.get_variable(objectspace)
|
||||
if not variable and self.optional:
|
||||
raise VariableCalculationDependencyError()
|
||||
params = self.get_params(objectspace,
|
||||
variable,
|
||||
identifier,
|
||||
)
|
||||
params = self.get_params(
|
||||
objectspace,
|
||||
variable,
|
||||
identifier,
|
||||
)
|
||||
return {
|
||||
"function": "calc_value",
|
||||
"params": params,
|
||||
|
@ -524,10 +529,12 @@ class VariablePropertyCalculation(_VariableCalculation):
|
|||
objectspace,
|
||||
) -> dict:
|
||||
variable, identifier = self.get_variable(objectspace)
|
||||
params = self.get_params(objectspace,
|
||||
variable,
|
||||
identifier,
|
||||
needs_multi=False,)
|
||||
params = self.get_params(
|
||||
objectspace,
|
||||
variable,
|
||||
identifier,
|
||||
needs_multi=False,
|
||||
)
|
||||
variable = params[None][0]["variable"]
|
||||
if self.when is not undefined:
|
||||
if self.version == "1.0":
|
||||
|
@ -650,10 +657,11 @@ class IdentifierPropertyCalculation(_IdentifierCalculation):
|
|||
else:
|
||||
msg = f'the identifier has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
|
||||
raise DictConsistencyError
|
||||
params = {None: [self.attribute_name, self.get_identifier()],
|
||||
"when": when,
|
||||
"inverse": inverse,
|
||||
}
|
||||
params = {
|
||||
None: [self.attribute_name, self.get_identifier()],
|
||||
"when": when,
|
||||
"inverse": inverse,
|
||||
}
|
||||
return {
|
||||
"function": "variable_to_property",
|
||||
"params": params,
|
||||
|
|
|
@ -16,6 +16,7 @@ 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
|
||||
"""
|
||||
|
||||
# from rougail.objspace import get_variables
|
||||
# from rougail.utils import normalize_family
|
||||
#
|
||||
|
|
|
@ -19,12 +19,15 @@ 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
|
||||
"""
|
||||
|
||||
from rougail.annotator.variable import Walk
|
||||
from rougail.utils import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
class Annotator(Walk):
|
||||
"""Annotate value"""
|
||||
|
||||
level = 80
|
||||
|
||||
def __init__(self, objectspace, *args) -> None:
|
||||
|
@ -37,9 +40,9 @@ class Annotator(Walk):
|
|||
if family.commandline:
|
||||
continue
|
||||
self.not_for_commandline(family)
|
||||
not_for_commandlines.append(family.path + '.')
|
||||
not_for_commandlines.append(family.path + ".")
|
||||
for variable in self.get_variables():
|
||||
if variable.type == 'symlink':
|
||||
if variable.type == "symlink":
|
||||
continue
|
||||
variable_path = variable.path
|
||||
for family_path in not_for_commandlines:
|
||||
|
@ -53,37 +56,58 @@ class Annotator(Walk):
|
|||
self.manage_negative_description(variable)
|
||||
|
||||
def not_for_commandline(self, variable) -> None:
|
||||
self.objectspace.properties.add(variable.path, 'not_for_commandline', True)
|
||||
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
|
||||
all_letters = ''
|
||||
all_letters = ""
|
||||
for letter in alternative_name:
|
||||
all_letters += letter
|
||||
if all_letters == 'h':
|
||||
if all_letters == "h":
|
||||
msg = _(f'alternative_name "{alternative_name}" conflict with "--help"')
|
||||
raise DictConsistencyError(msg, 202, variable.xmlfiles)
|
||||
if all_letters in self.alternative_names:
|
||||
msg = _(f'conflict alternative_name "{alternative_name}": "{variable_path}" and "{self.alternative_names[all_letters]}"')
|
||||
msg = _(
|
||||
f'conflict alternative_name "{alternative_name}": "{variable_path}" and "{self.alternative_names[all_letters]}"'
|
||||
)
|
||||
raise DictConsistencyError(msg, 202, variable.xmlfiles)
|
||||
|
||||
self.alternative_names[alternative_name] = variable_path
|
||||
if '.' not in 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)
|
||||
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)
|
||||
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}"'), 201, variable.xmlfiles)
|
||||
if variable.type != "boolean":
|
||||
raise DictConsistencyError(
|
||||
_(
|
||||
f'negative_description is only available for boolean variable, but "{variable.path}" is "{variable.type}"'
|
||||
),
|
||||
201,
|
||||
variable.xmlfiles,
|
||||
)
|
||||
self.objectspace.informations.add(
|
||||
variable.path, "negative_description", variable.negative_description
|
||||
)
|
||||
|
|
|
@ -20,9 +20,12 @@ 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:
|
||||
|
||||
|
||||
def get_rougail_config(
|
||||
*,
|
||||
backward_compatibility=True,
|
||||
) -> dict:
|
||||
options = """
|
||||
structural_commandline:
|
||||
description: Configuration rougail-structural_commandline
|
||||
|
@ -31,12 +34,12 @@ structural_commandline:
|
|||
description: Add extra options to tiramisu-cmdline-parser
|
||||
default: true
|
||||
"""
|
||||
return {'name': 'exporter',
|
||||
'process': 'structural',
|
||||
'options': options,
|
||||
'level': 20,
|
||||
}
|
||||
return {
|
||||
"name": "exporter",
|
||||
"process": "structural",
|
||||
"options": options,
|
||||
"level": 20,
|
||||
}
|
||||
|
||||
|
||||
__all__ = ('get_rougail_config')
|
||||
|
||||
__all__ = "get_rougail_config"
|
||||
|
|
|
@ -19,18 +19,19 @@ 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
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Variable(BaseModel):
|
||||
alternative_name: Optional[str]=None
|
||||
commandline: bool=True
|
||||
negative_description: Optional[str]=None
|
||||
alternative_name: Optional[str] = None
|
||||
commandline: bool = True
|
||||
negative_description: Optional[str] = None
|
||||
|
||||
|
||||
class Family(BaseModel):
|
||||
commandline: bool=True
|
||||
commandline: bool = True
|
||||
|
||||
|
||||
__all__ = ('Variable', 'Family')
|
||||
__all__ = ("Variable", "Family")
|
||||
|
|
|
@ -27,13 +27,18 @@ 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
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
from tiramisu5 import DynOptionDescription, calc_value
|
||||
except ModuleNotFoundError:
|
||||
from tiramisu import DynOptionDescription, calc_value
|
||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
from importlib.util import (
|
||||
spec_from_loader as _spec_from_loader,
|
||||
module_from_spec as _module_from_spec,
|
||||
)
|
||||
from jinja2 import StrictUndefined, DictLoader
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
from rougail.object_model import CONVERT_OPTION
|
||||
|
@ -43,16 +48,16 @@ from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError
|
|||
from .utils import normalize_family
|
||||
|
||||
|
||||
|
||||
global func
|
||||
dict_env = {}
|
||||
ENV = SandboxedEnvironment(loader=DictLoader(dict_env), undefined=StrictUndefined)
|
||||
func = ENV.filters
|
||||
ENV.compile_templates('jinja_caches', zip=None)
|
||||
ENV.compile_templates("jinja_caches", zip=None)
|
||||
|
||||
|
||||
class JinjaError:
|
||||
__slot__ = ('_err',)
|
||||
__slot__ = ("_err",)
|
||||
|
||||
def __init__(self, err):
|
||||
self._err = err
|
||||
|
||||
|
@ -85,17 +90,17 @@ def test_propertyerror(value: Any) -> bool:
|
|||
return isinstance(value, JinjaError)
|
||||
|
||||
|
||||
ENV.tests['propertyerror'] = test_propertyerror
|
||||
ENV.tests["propertyerror"] = test_propertyerror
|
||||
|
||||
|
||||
def load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
loader = _SourceFileLoader('func', path)
|
||||
loader = _SourceFileLoader("func", path)
|
||||
spec = _spec_from_loader(loader.name, loader)
|
||||
func_ = _module_from_spec(spec)
|
||||
loader.exec_module(func_)
|
||||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
if function.startswith("_"):
|
||||
continue
|
||||
func[function] = getattr(func_, function)
|
||||
|
||||
|
@ -108,37 +113,52 @@ def rougail_calc_value(*args, __default_value=None, **kwargs):
|
|||
|
||||
|
||||
@function_waiting_for_error
|
||||
def jinja_to_function(__internal_variable, __internal_attribute, __internal_jinja, __internal_type, __internal_multi, __internal_files, __default_value=None, **kwargs):
|
||||
def jinja_to_function(
|
||||
__internal_variable,
|
||||
__internal_attribute,
|
||||
__internal_jinja,
|
||||
__internal_type,
|
||||
__internal_multi,
|
||||
__internal_files,
|
||||
__default_value=None,
|
||||
**kwargs,
|
||||
):
|
||||
global ENV, CONVERT_OPTION
|
||||
kw = {}
|
||||
for key, value in kwargs.items():
|
||||
if isinstance(value, PropertiesOptionError):
|
||||
value = JinjaError(value)
|
||||
if '.' in key:
|
||||
if "." in key:
|
||||
c_kw = kw
|
||||
path, var = key.rsplit('.', 1)
|
||||
for subkey in path.split('.'):
|
||||
path, var = key.rsplit(".", 1)
|
||||
for subkey in path.split("."):
|
||||
c_kw = c_kw.setdefault(subkey, {})
|
||||
c_kw[var] = value
|
||||
else:
|
||||
if key in kw:
|
||||
raise ConfigError(f'internal error, multi key for "{key}" in jinja_to_function')
|
||||
raise ConfigError(
|
||||
f'internal error, multi key for "{key}" in jinja_to_function'
|
||||
)
|
||||
kw[key] = value
|
||||
try:
|
||||
values = ENV.get_template(__internal_jinja).render(kw, **func).strip()
|
||||
except Exception as err:
|
||||
raise ConfigError(f'cannot calculating "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}') from err
|
||||
convert = CONVERT_OPTION[__internal_type].get('func', str)
|
||||
raise ConfigError(
|
||||
f'cannot calculating "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}'
|
||||
) from err
|
||||
convert = CONVERT_OPTION[__internal_type].get("func", str)
|
||||
if __internal_multi:
|
||||
values = [convert(val) for val in values.split('\n') if val != ""]
|
||||
values = [convert(val) for val in values.split("\n") if val != ""]
|
||||
if not values and __default_value is not None:
|
||||
return __default_value
|
||||
return values
|
||||
try:
|
||||
values = convert(values)
|
||||
except Exception as err:
|
||||
raise ConfigError(f'cannot converting "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}') from err
|
||||
values = values if values != '' and values != 'None' else None
|
||||
raise ConfigError(
|
||||
f'cannot converting "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}'
|
||||
) from err
|
||||
values = values if values != "" and values != "None" else None
|
||||
if values is None and __default_value is not None:
|
||||
return __default_value
|
||||
return values
|
||||
|
@ -156,33 +176,33 @@ def variable_to_property(prop, value, when, inverse):
|
|||
|
||||
@function_waiting_for_error
|
||||
def jinja_to_property(prop, when, inverse, **kwargs):
|
||||
value = func['jinja_to_function'](**kwargs)
|
||||
return func['variable_to_property'](prop, value is not None, when, inverse)
|
||||
value = func["jinja_to_function"](**kwargs)
|
||||
return func["variable_to_property"](prop, value is not None, when, inverse)
|
||||
|
||||
|
||||
@function_waiting_for_error
|
||||
def jinja_to_property_help(prop, **kwargs):
|
||||
value = func['jinja_to_function'](**kwargs)
|
||||
return (prop, f'\"{prop}\" ({value})')
|
||||
value = func["jinja_to_function"](**kwargs)
|
||||
return (prop, f'"{prop}" ({value})')
|
||||
|
||||
|
||||
@function_waiting_for_error
|
||||
def valid_with_jinja(warnings_only=False, **kwargs):
|
||||
global ValueWarning
|
||||
value = func['jinja_to_function'](**kwargs)
|
||||
value = func["jinja_to_function"](**kwargs)
|
||||
if value:
|
||||
if warnings_only:
|
||||
raise ValueWarning(value)
|
||||
else:
|
||||
raise ValueError(value)
|
||||
if warnings_only:
|
||||
raise ValueWarning(value)
|
||||
else:
|
||||
raise ValueError(value)
|
||||
|
||||
|
||||
func['calc_value'] = rougail_calc_value
|
||||
func['jinja_to_function'] = jinja_to_function
|
||||
func['jinja_to_property'] = jinja_to_property
|
||||
func['jinja_to_property_help'] = jinja_to_property_help
|
||||
func['variable_to_property'] = variable_to_property
|
||||
func['valid_with_jinja'] = valid_with_jinja
|
||||
func["calc_value"] = rougail_calc_value
|
||||
func["jinja_to_function"] = jinja_to_function
|
||||
func["jinja_to_property"] = jinja_to_property
|
||||
func["jinja_to_property_help"] = jinja_to_property_help
|
||||
func["variable_to_property"] = variable_to_property
|
||||
func["valid_with_jinja"] = valid_with_jinja
|
||||
|
||||
|
||||
class ConvertDynOptionDescription(DynOptionDescription):
|
||||
|
@ -213,9 +233,12 @@ class ConvertDynOptionDescription(DynOptionDescription):
|
|||
def impl_get_display_name(
|
||||
self,
|
||||
subconfig,
|
||||
with_quote: bool=False,
|
||||
) -> str:
|
||||
with_quote: bool = False,
|
||||
) -> str:
|
||||
display = super().impl_get_display_name(subconfig, with_quote=with_quote)
|
||||
if "{{ identifier }}" in display:
|
||||
return display.replace("{{ identifier }}", self.convert_identifier_to_path(self.get_identifiers(subconfig)[-1]))
|
||||
return display.replace(
|
||||
"{{ identifier }}",
|
||||
self.convert_identifier_to_path(self.get_identifiers(subconfig)[-1]),
|
||||
)
|
||||
return display
|
||||
|
|
|
@ -28,6 +28,7 @@ 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
|
||||
"""
|
||||
|
||||
from typing import Optional, Union
|
||||
from json import dumps
|
||||
from os.path import isfile, basename
|
||||
|
@ -90,11 +91,14 @@ class TiramisuReflector:
|
|||
self.text["header"].append(f"load_functions('{funcs_path}')")
|
||||
if self.objectspace.export_with_import:
|
||||
if objectspace.main_namespace:
|
||||
self.text["header"].extend(["try:",
|
||||
" groups.namespace",
|
||||
"except:",
|
||||
" groups.addgroup('namespace')",
|
||||
])
|
||||
self.text["header"].extend(
|
||||
[
|
||||
"try:",
|
||||
" groups.namespace",
|
||||
"except:",
|
||||
" groups.addgroup('namespace')",
|
||||
]
|
||||
)
|
||||
for mode in self.objectspace.modes_level:
|
||||
self.text["header"].append(f'ALLOWED_LEADER_PROPERTIES.add("{mode}")')
|
||||
self.make_tiramisu_objects()
|
||||
|
@ -112,9 +116,9 @@ class TiramisuReflector:
|
|||
def make_tiramisu_objects(self) -> None:
|
||||
"""make tiramisu objects"""
|
||||
baseelt = BaseElt()
|
||||
self.objectspace.reflector_names[
|
||||
baseelt.path
|
||||
] = f"option_0{self.objectspace.suffix}"
|
||||
self.objectspace.reflector_names[baseelt.path] = (
|
||||
f"option_0{self.objectspace.suffix}"
|
||||
)
|
||||
basefamily = Family(
|
||||
baseelt,
|
||||
self,
|
||||
|
@ -340,7 +344,7 @@ class Common:
|
|||
param.get("propertyerror", True),
|
||||
param.get("identifier"),
|
||||
param.get("dynamic"),
|
||||
param.get('whole', False),
|
||||
param.get("whole", False),
|
||||
)
|
||||
if param["type"] == "any":
|
||||
if isinstance(param["value"], str):
|
||||
|
@ -375,7 +379,7 @@ class Common:
|
|||
if isinstance(ident, str):
|
||||
ident = self.convert_str(ident)
|
||||
identifiers.append(str(ident))
|
||||
params.append('[' + ', '.join(identifiers) + ']')
|
||||
params.append("[" + ", ".join(identifiers) + "]")
|
||||
else:
|
||||
param_type = "ParamOption"
|
||||
if not propertyerror:
|
||||
|
@ -475,13 +479,15 @@ class Variable(Common):
|
|||
keys["values"] = self.populate_calculation(
|
||||
self.elt.choices, return_a_tuple=True
|
||||
)
|
||||
if self.elt.type == 'regexp':
|
||||
self.object_type = 'Regexp_' + self.option_name
|
||||
self.tiramisu.text['header'].append(f'''class {self.object_type}(RegexpOption):
|
||||
if self.elt.type == "regexp":
|
||||
self.object_type = "Regexp_" + self.option_name
|
||||
self.tiramisu.text["header"].append(
|
||||
f"""class {self.object_type}(RegexpOption):
|
||||
__slots__ = tuple()
|
||||
_type = 'value'
|
||||
{self.object_type}._regexp = re_compile(r"{self.elt.regexp}")
|
||||
''')
|
||||
"""
|
||||
)
|
||||
if self.elt.path in self.objectspace.multis:
|
||||
keys["multi"] = self.objectspace.multis[self.elt.path]
|
||||
if hasattr(self.elt, "default") and self.elt.default is not None:
|
||||
|
@ -528,8 +534,8 @@ class Family(Common):
|
|||
self.object_type = "Leadership"
|
||||
else:
|
||||
self.object_type = "OptionDescription"
|
||||
if hasattr(self.elt, 'name') and self.elt.name == self.elt.namespace:
|
||||
self.group_type = 'groups.namespace'
|
||||
if hasattr(self.elt, "name") and self.elt.name == self.elt.namespace:
|
||||
self.group_type = "groups.namespace"
|
||||
else:
|
||||
self.group_type = None
|
||||
self.children = []
|
||||
|
|
|
@ -18,4 +18,5 @@ 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
|
||||
"""
|
||||
|
||||
from .update import RougailUpgrade
|
||||
|
|
|
@ -63,7 +63,7 @@ class upgrade_010_to_10:
|
|||
xmlsrc: str,
|
||||
) -> None:
|
||||
self.xmlsrc = xmlsrc
|
||||
self.paths = {"family": {}, "variable": {}, 'dynamic': {}}
|
||||
self.paths = {"family": {}, "variable": {}, "dynamic": {}}
|
||||
self.lists = {
|
||||
"service": {},
|
||||
"ip": {},
|
||||
|
@ -82,18 +82,26 @@ class upgrade_010_to_10:
|
|||
sub_path: str,
|
||||
true_sub_path: str,
|
||||
*,
|
||||
root: bool=False,
|
||||
is_dynamic: bool=False,
|
||||
root: bool = False,
|
||||
is_dynamic: bool = False,
|
||||
) -> dict:
|
||||
new_families = {}
|
||||
if root:
|
||||
new_families['version'] = None
|
||||
new_families["version"] = None
|
||||
if "variables" in family:
|
||||
for subelt in family["variables"]:
|
||||
for typ, obj in subelt.items():
|
||||
for subobj in obj:
|
||||
local_is_dynamic = is_dynamic or subobj.get('dynamic') is not None
|
||||
getattr(self, f"convert_{typ}")(subobj, new_families, sub_path, true_sub_path, local_is_dynamic)
|
||||
local_is_dynamic = (
|
||||
is_dynamic or subobj.get("dynamic") is not None
|
||||
)
|
||||
getattr(self, f"convert_{typ}")(
|
||||
subobj,
|
||||
new_families,
|
||||
sub_path,
|
||||
true_sub_path,
|
||||
local_is_dynamic,
|
||||
)
|
||||
family.pop("variables")
|
||||
return new_families
|
||||
|
||||
|
@ -112,7 +120,7 @@ class upgrade_010_to_10:
|
|||
else:
|
||||
true_sub_path = name
|
||||
if is_dynamic:
|
||||
name += '{{ suffix }}'
|
||||
name += "{{ suffix }}"
|
||||
if sub_path:
|
||||
sub_path = sub_path + "." + name
|
||||
else:
|
||||
|
@ -126,7 +134,9 @@ class upgrade_010_to_10:
|
|||
if typ == "dynamic":
|
||||
family["variable"] = self.get_variable_path(value)
|
||||
# add sub families and sub variables
|
||||
sub_families = self.parse_variables(family, sub_path, true_sub_path, is_dynamic=is_dynamic)
|
||||
sub_families = self.parse_variables(
|
||||
family, sub_path, true_sub_path, is_dynamic=is_dynamic
|
||||
)
|
||||
for sub_name, sub_family in sub_families.copy().items():
|
||||
if sub_name not in family:
|
||||
continue
|
||||
|
@ -152,13 +162,13 @@ class upgrade_010_to_10:
|
|||
else:
|
||||
true_sub_path = name
|
||||
self.flatten_paths["variable"][name] = true_sub_path
|
||||
name += '{{ suffix }}'
|
||||
name += "{{ suffix }}"
|
||||
if sub_path:
|
||||
sub_path = sub_path + "." + name
|
||||
else:
|
||||
sub_path = name
|
||||
if is_dynamic:
|
||||
self.paths['dynamic'][true_sub_path] = sub_path
|
||||
self.paths["dynamic"][true_sub_path] = sub_path
|
||||
new_families[name] = variable
|
||||
self.flatten_paths["variable"][name] = sub_path
|
||||
self.paths["variable"][sub_path] = variable
|
||||
|
@ -198,17 +208,18 @@ class upgrade_010_to_10:
|
|||
)(test)
|
||||
)
|
||||
variable["test"] = tests
|
||||
if variable.get('mandatory', False):
|
||||
if variable.get("mandatory", False):
|
||||
del variable["mandatory"]
|
||||
CONVERT_TYPE = {'filename': 'unix_filename',
|
||||
'password': 'secret',
|
||||
}
|
||||
if variable.get('type') in CONVERT_TYPE:
|
||||
variable['type'] = CONVERT_TYPE[variable['type']]
|
||||
CONVERT_TYPE = {
|
||||
"filename": "unix_filename",
|
||||
"password": "secret",
|
||||
}
|
||||
if variable.get("type") in CONVERT_TYPE:
|
||||
variable["type"] = CONVERT_TYPE[variable["type"]]
|
||||
|
||||
def parse_variables_with_path(self):
|
||||
for variable in self.paths["variable"].values():
|
||||
multi = variable.get('multi', False)
|
||||
multi = variable.get("multi", False)
|
||||
if "value" in variable:
|
||||
default = variable.pop("value")
|
||||
if default is not None:
|
||||
|
@ -223,7 +234,8 @@ class upgrade_010_to_10:
|
|||
variable["choices"] = variable.pop("choice")
|
||||
else:
|
||||
variable["choices"] = [
|
||||
self.get_value(choice, multi) for choice in variable.pop("choice")
|
||||
self.get_value(choice, multi)
|
||||
for choice in variable.pop("choice")
|
||||
]
|
||||
|
||||
def parse_services(
|
||||
|
@ -350,21 +362,21 @@ class upgrade_010_to_10:
|
|||
if variable_path is None:
|
||||
continue
|
||||
variable = self.paths["variable"][variable_path]
|
||||
if variable.get('multi', False):
|
||||
if variable.get("multi", False):
|
||||
multi = True
|
||||
if apply_on_fallback:
|
||||
condition_value = True
|
||||
else:
|
||||
if "{{ suffix }}" in source:
|
||||
force_param = {'__var': source}
|
||||
source = '__var'
|
||||
force_param = {"__var": source}
|
||||
source = "__var"
|
||||
else:
|
||||
force_param = None
|
||||
condition_value = self.params_condition_to_jinja(
|
||||
prop, source, condition["param"], name.endswith("if_in"), multi
|
||||
)
|
||||
if force_param:
|
||||
condition_value.setdefault('params', {}).update(force_param)
|
||||
condition_value.setdefault("params", {}).update(force_param)
|
||||
for target in condition["target"]:
|
||||
typ = target.get("type", "variable")
|
||||
if typ == "variable":
|
||||
|
@ -417,7 +429,9 @@ class upgrade_010_to_10:
|
|||
check["param"] = [
|
||||
{"text": variable_path, "type": "variable"}
|
||||
] + check.get("param", [])
|
||||
check_value = self.convert_param_function(check, variable.get('multi', False))
|
||||
check_value = self.convert_param_function(
|
||||
check, variable.get("multi", False)
|
||||
)
|
||||
variable.setdefault("validators", []).append(check_value)
|
||||
|
||||
def parse_fill(
|
||||
|
@ -437,12 +451,16 @@ class upgrade_010_to_10:
|
|||
"jinja": fill["name"],
|
||||
}
|
||||
else:
|
||||
fill_value = self.convert_param_function(fill, variable.get('multi', False))
|
||||
fill_value = self.convert_param_function(
|
||||
fill, variable.get("multi", False)
|
||||
)
|
||||
variable["default"] = fill_value
|
||||
if variable.get('mandatory') is False:
|
||||
del variable['mandatory']
|
||||
if variable.get("mandatory") is False:
|
||||
del variable["mandatory"]
|
||||
else:
|
||||
raise Exception(f'cannot set fill to unknown variable "{variable_path}"')
|
||||
raise Exception(
|
||||
f'cannot set fill to unknown variable "{variable_path}"'
|
||||
)
|
||||
|
||||
def params_condition_to_jinja(
|
||||
self,
|
||||
|
@ -538,8 +556,8 @@ class upgrade_010_to_10:
|
|||
new_param = {attr_name: value}
|
||||
value = attr_name
|
||||
elif typ in ["index", "suffix"]:
|
||||
if 'name' in value:
|
||||
attr_name = value['name']
|
||||
if "name" in value:
|
||||
attr_name = value["name"]
|
||||
else:
|
||||
attr_name = f"__{typ}"
|
||||
new_param = {attr_name: {"type": typ}}
|
||||
|
@ -550,7 +568,7 @@ class upgrade_010_to_10:
|
|||
value = value[typ]
|
||||
elif "{{ suffix }}" in value[typ]:
|
||||
path = value[typ]
|
||||
attr_name = path.split('.')[-1][:-12] # remove {{ suffix }}
|
||||
attr_name = path.split(".")[-1][:-12] # remove {{ suffix }}
|
||||
new_param = {attr_name: value}
|
||||
value = attr_name
|
||||
else:
|
||||
|
@ -568,16 +586,22 @@ class upgrade_010_to_10:
|
|||
) -> str:
|
||||
text = param["name"]
|
||||
params = {}
|
||||
# multi = False
|
||||
# multi = False
|
||||
if "param" in param and param["param"]:
|
||||
if text == 'calc_value' and len(param["param"]) == 1 and isinstance(param["param"][0], dict) and param["param"][0].get('type') == 'variable' and param["param"][0].get("text"):
|
||||
if (
|
||||
text == "calc_value"
|
||||
and len(param["param"]) == 1
|
||||
and isinstance(param["param"][0], dict)
|
||||
and param["param"][0].get("type") == "variable"
|
||||
and param["param"][0].get("text")
|
||||
):
|
||||
value = param["param"][0]["text"]
|
||||
path = self.get_variable_path(value)
|
||||
if not path:
|
||||
path = value
|
||||
ret = {"type": "variable", "variable": path}
|
||||
if 'optional' in param["param"][0]:
|
||||
ret['optional'] = param["param"][0]["optional"]
|
||||
if "optional" in param["param"][0]:
|
||||
ret["optional"] = param["param"][0]["optional"]
|
||||
return ret
|
||||
first, *others = param["param"]
|
||||
new_param, first = self.get_jinja_param_and_value(first, multi)
|
||||
|
@ -604,9 +628,13 @@ class upgrade_010_to_10:
|
|||
if not multi:
|
||||
text = "{{ " + text + " }}"
|
||||
else:
|
||||
text = """{% for __variable in """ + text + """ %}
|
||||
text = (
|
||||
"""{% for __variable in """
|
||||
+ text
|
||||
+ """ %}
|
||||
{{ __variable }}
|
||||
{% endfor %}"""
|
||||
)
|
||||
ret = {"type": "jinja", "jinja": text}
|
||||
if params:
|
||||
ret["params"] = params
|
||||
|
@ -659,16 +687,21 @@ class RougailUpgrade:
|
|||
def run(
|
||||
self,
|
||||
):
|
||||
for dict_dir, dest_dir in zip(self.rougailconfig["main_dictionaries"], self.rougailconfig["upgrade_options.main_dictionaries"]):
|
||||
for dict_dir, dest_dir in zip(
|
||||
self.rougailconfig["main_dictionaries"],
|
||||
self.rougailconfig["upgrade_options.main_dictionaries"],
|
||||
):
|
||||
self._load_dictionaries(
|
||||
dict_dir,
|
||||
dest_dir,
|
||||
normalize_family(self.rougailconfig["main_namespace"]),
|
||||
)
|
||||
if self.rougailconfig['main_namespace']:
|
||||
if self.rougailconfig["main_namespace"]:
|
||||
if self.rougailconfig["extra_dictionaries"]:
|
||||
dst_extra_dir = self.rougailconfig["upgrade_options.extra_dictionary"]
|
||||
for namespace, extra_dirs in self.rougailconfig["extra_dictionaries"].items():
|
||||
for namespace, extra_dirs in self.rougailconfig[
|
||||
"extra_dictionaries"
|
||||
].items():
|
||||
extra_dstsubfolder = Path(dst_extra_dir) / namespace
|
||||
if not extra_dstsubfolder.is_dir():
|
||||
extra_dstsubfolder.mkdir()
|
||||
|
@ -677,7 +710,7 @@ class RougailUpgrade:
|
|||
str(extra_dir),
|
||||
str(extra_dstsubfolder),
|
||||
normalize_family(namespace),
|
||||
)
|
||||
)
|
||||
|
||||
def _load_dictionaries(
|
||||
self,
|
||||
|
@ -697,7 +730,7 @@ class RougailUpgrade:
|
|||
for filename in filenames:
|
||||
xmlsrc = Path(srcfolder) / Path(filename)
|
||||
|
||||
ymldst = Path(dstfolder) / (Path(filename).stem + '.yml')
|
||||
ymldst = Path(dstfolder) / (Path(filename).stem + ".yml")
|
||||
if filename.endswith(".xml"):
|
||||
if parse is None:
|
||||
raise Exception("XML module is not installed")
|
||||
|
@ -732,7 +765,7 @@ class RougailUpgrade:
|
|||
root_services = root_services_
|
||||
if function_version == search_function_name:
|
||||
function_found = True
|
||||
if root != {'version': None}:
|
||||
if root != {"version": None}:
|
||||
root["version"] = float(version)
|
||||
with ymldst.open("w") as ymlfh:
|
||||
yaml = YAML()
|
||||
|
@ -923,25 +956,26 @@ class RougailUpgrade:
|
|||
"variable": value.pop("variable"),
|
||||
"propertyerror": False,
|
||||
}
|
||||
if '{{ suffix }}' not in key:
|
||||
new_root[key + '{{ suffix }}'] = new_root.pop(key)
|
||||
if "{{ suffix }}" not in key:
|
||||
new_root[key + "{{ suffix }}"] = new_root.pop(key)
|
||||
update_root = True
|
||||
self._update_1_1(value)
|
||||
for typ, obj in {'boolean': bool,
|
||||
'number': int,
|
||||
'string': str,
|
||||
'float': float,
|
||||
}.items():
|
||||
if value.get('type') == typ:
|
||||
default = value.get('default')
|
||||
for typ, obj in {
|
||||
"boolean": bool,
|
||||
"number": int,
|
||||
"string": str,
|
||||
"float": float,
|
||||
}.items():
|
||||
if value.get("type") == typ:
|
||||
default = value.get("default")
|
||||
if default is None or default == []:
|
||||
continue
|
||||
if isinstance(default, obj):
|
||||
del value['type']
|
||||
del value["type"]
|
||||
elif isinstance(default, list) and isinstance(default[0], obj):
|
||||
del value['type']
|
||||
if value.get('multi') and isinstance(value.get('default'), list):
|
||||
del value['multi']
|
||||
del value["type"]
|
||||
if value.get("multi") and isinstance(value.get("default"), list):
|
||||
del value["multi"]
|
||||
if update_root:
|
||||
root.clear()
|
||||
root.update(new_root)
|
||||
|
|
|
@ -27,6 +27,7 @@ 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
|
||||
"""
|
||||
|
||||
from typing import List, Union
|
||||
from unicodedata import normalize, combining
|
||||
import re
|
||||
|
@ -117,7 +118,9 @@ def get_jinja_variable_to_param(
|
|||
for g in parsed_content.find_all(Getattr):
|
||||
variables.add(recurse_getattr(g))
|
||||
except TemplateSyntaxError as err:
|
||||
msg = _(f'error in jinja "{jinja_text}" for the variable "{ current_path }": {err}')
|
||||
msg = _(
|
||||
f'error in jinja "{jinja_text}" for the variable "{ current_path }": {err}'
|
||||
)
|
||||
raise DictConsistencyError(msg, 39, xmlfiles) from err
|
||||
variables = list(variables)
|
||||
variables.sort(reverse=True)
|
||||
|
@ -135,7 +138,7 @@ def get_jinja_variable_to_param(
|
|||
if variable and variable.path in objectspace.variables:
|
||||
founded_variables[variable_path] = (identifier, variable)
|
||||
else:
|
||||
sub_family = variable_path + '.'
|
||||
sub_family = variable_path + "."
|
||||
for founded_variable in chain(founded_variables, unknown_variables):
|
||||
if founded_variable.startswith(sub_family):
|
||||
break
|
||||
|
@ -149,8 +152,8 @@ def get_jinja_variable_to_param(
|
|||
else:
|
||||
root_path = None
|
||||
vpath = variable_path
|
||||
while '.' in vpath:
|
||||
vpath = vpath.rsplit('.', 1)[0]
|
||||
while "." in vpath:
|
||||
vpath = vpath.rsplit(".", 1)[0]
|
||||
variable, identifier = objectspace.paths.get_with_dynamic(
|
||||
vpath,
|
||||
path_prefix,
|
||||
|
|
Loading…
Reference in a new issue