feat: identifier could be calculated

This commit is contained in:
egarette@silique.fr 2026-03-02 09:12:47 +01:00
parent 9cd46c7c10
commit e23479ae97
10 changed files with 177 additions and 47 deletions

View file

@ -1000,19 +1000,17 @@ class ParserVariable:
calculation_object["namespace"] = self.namespace
calculation_object["xmlfiles"] = xmlfiles
#
if attribute == "default" and "identifier" in calculation_object:
identifier = calculation_object["identifier"]
if isinstance(identifier, dict):
self.set_calculation(calculation_object, "identifier", identifier, path, family_is_dynamic, xmlfiles, inside_list=inside_list, index=index)
if "params" in calculation_object:
if not isinstance(calculation_object["params"], dict):
raise Exception("params must be a dict")
params = []
for key, val in calculation_object["params"].items():
if isinstance(val, dict) and "type" not in val:
# auto set type
param_typ = set(CALCULATION_TYPES) & set(val)
# XXX variable is also set to information
if param_typ == {"variable", "information"}:
param_typ = {"information"}
if len(param_typ) == 1:
val["type"] = list(param_typ)[0]
self.set_param_type(val)
if not isinstance(val, dict) or "type" not in val:
param_typ = "any"
val = {
@ -1064,6 +1062,15 @@ class ParserVariable:
else:
obj[attribute][index] = calc
def set_param_type(self, val):
# auto set type
param_typ = set(CALCULATION_TYPES) & set(val)
# XXX variable is also set to information
if param_typ == {"variable", "information"}:
param_typ = {"information"}
if len(param_typ) == 1:
val["type"] = list(param_typ)[0]
class RougailConvert(ParserVariable):
"""Main Rougail conversion"""

View file

@ -81,8 +81,8 @@ def get_convert_option_types():
class Param(BaseModel):
key: str
namespace: Optional[str]
key: StrictStr
namespace: Optional[StrictStr]
model_config = ConfigDict(extra="forbid")
def __init__(
@ -102,13 +102,13 @@ class Param(BaseModel):
class AnyParam(Param):
type: str
type: StrictStr
value: Union[BASETYPE, List[BASETYPE]]
class VariableParam(Param):
type: str
variable: str
type: StrictStr
variable: StrictStr
propertyerror: bool = True
whole: bool = False
# dynamic: bool = True
@ -151,7 +151,7 @@ class VariableParam(Param):
class IdentifierParam(Param):
type: str
type: StrictStr
identifier: Optional[int] = None
def __init__(
@ -167,9 +167,9 @@ class IdentifierParam(Param):
class InformationParam(Param):
type: str
information: str
variable: Optional[str] = None
type: StrictStr
information: StrictStr
variable: Optional[StrictStr] = None
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
@ -202,7 +202,7 @@ class InformationParam(Param):
class IndexParam(Param):
type: str
type: StrictStr
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
@ -220,8 +220,8 @@ class IndexParam(Param):
class NamespaceParam(Param):
type: str
namespace: str
type: StrictStr
namespace: StrictStr
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
@ -247,15 +247,15 @@ PARAM_TYPES = {
class Calculation(BaseModel):
# path: str
# path: StrictStr
inside_list: bool
version: str
ori_path: Optional[str] = None
version: StrictStr
ori_path: Optional[StrictStr] = None
default_values: Any = None
namespace: Optional[str]
namespace: Optional[StrictStr]
warnings: Optional[bool] = None
description: Optional[StrictStr] = None
xmlfiles: List[str]
xmlfiles: List[StrictStr]
model_config = ConfigDict(extra="forbid")
@ -492,6 +492,7 @@ class _VariableCalculation(Calculation):
propertyerror: bool = True,
allow_none: bool = False
optional: bool = False
identifier: Optional[Calculation] = None
def get_variable(
self,
@ -540,6 +541,15 @@ class _VariableCalculation(Calculation):
variable_in_calculation_identifier: Optional[str],
key: str = None,
):
identifier_is_a_calculation = False
if self.identifier:
if variable_in_calculation_identifier:
msg = _(
'variable "{0}" has an attribute "{1}" with an identifier "{2}" but the path has also the identifier "{3}"'
).format(path, self.attribute_name, self.variable, self.identifier, variable_in_calculation_identifier)
raise DictConsistencyError(msg, 89, self.xmlfiles)
variable_in_calculation_identifier = self.identifier
identifier_is_a_calculation = True
if not variable_in_calculation:
if not objectspace.force_optional:
msg = _(
@ -564,8 +574,9 @@ class _VariableCalculation(Calculation):
params["__default_value"] = self.default_values
if self.allow_none:
params["allow_none"] = True
if not identifier_is_a_calculation:
self.check_multi(
objectspace, path, variable_in_calculation_path, variable_in_calculation
objectspace, path, variable_in_calculation_path, variable_in_calculation,
)
if path in objectspace.followers:
multi = objectspace.multis[path] == "submulti"
@ -718,7 +729,7 @@ class _VariableCalculation(Calculation):
class VariableCalculation(_VariableCalculation):
attribute_name: Literal["default", "choices", "dynamic"]
attribute_name: Literal["default", "choices", "dynamic", "identifier"]
default: Any = undefined
def to_function(
@ -1039,7 +1050,7 @@ SECRET_BASETYPE_CALC = Union[StrictStr, JinjaCalculation]
class Family(BaseModel):
name: str
name: StrictStr
# informations
description: Optional[StrictStr] = None
help: Optional[StrictStr] = None
@ -1060,12 +1071,12 @@ class Family(BaseModel):
class Dynamic(Family):
# None only for format 1.0
variable: str = None
variable: StrictStr = None
dynamic: Union[List[Union[StrictStr, Calculation]], Calculation]
class Variable(BaseModel):
name: str
name: StrictStr
# user informations
description: Optional[StrictStr] = None
help: Optional[StrictStr] = None
@ -1075,7 +1086,7 @@ class Variable(BaseModel):
test: Optional[list] = None
# validations
## type will be set dynamically in `annotator/value.py`, default is None
type: str = None
type: StrictStr = None
params: Optional[List[Param]] = None
regexp: Optional[StrictStr] = None
choices: Optional[Union[List[BASETYPE_CALC], Calculation]] = None
@ -1094,21 +1105,21 @@ class Variable(BaseModel):
disabled: Union[bool, Calculation] = False
frozen: Union[bool, Calculation] = False
# others
path: str
namespace: Optional[str]
version: str
xmlfiles: List[str] = []
path: StrictStr
namespace: Optional[StrictStr]
version: StrictStr
xmlfiles: List[StrictStr] = []
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class SymLink(BaseModel):
type: Literal["symlink"] = "symlink"
name: str
path: str
name: StrictStr
path: StrictStr
opt: Variable
namespace: Optional[str]
version: str
xmlfiles: List[str] = []
namespace: Optional[StrictStr]
version: StrictStr
xmlfiles: List[StrictStr] = []
model_config = ConfigDict(extra="forbid")

View file

@ -362,12 +362,16 @@ class Common:
)
raise DictConsistencyError(msg, 49, self.elt.xmlfiles)
param_type = "ParamDynOption"
if isinstance(identifier, Calculation):
identifiers = self.populate_calculation(identifier)
else:
identifiers = []
for ident in identifier:
if isinstance(ident, str):
ident = self.convert_str(ident)
identifiers.append(str(ident))
params.append("[" + ", ".join(identifiers) + "]")
identifiers = "[" + ", ".join(identifiers) + "]"
params.append(identifiers)
if param.get("optional", False):
params.append("optional=True")
else:

View file

@ -0,0 +1,25 @@
{
"rougail.var1": {
"owner": "default",
"value": [
"val1",
"val2"
]
},
"rougail.var2": {
"owner": "default",
"value": "val1"
},
"rougail.dynval1.var": {
"owner": "default",
"value": "val1"
},
"rougail.dynval2.var": {
"owner": "default",
"value": "val2"
},
"rougail.var3": {
"owner": "default",
"value": "val1"
}
}

View file

@ -0,0 +1,10 @@
{
"rougail.var1": [
"val1",
"val2"
],
"rougail.var2": "val1",
"rougail.dynval1.var": "val1",
"rougail.dynval2.var": "val2",
"rougail.var3": "val1"
}

View file

@ -0,0 +1,25 @@
{
"rougail.var1": {
"owner": "default",
"value": [
"val1",
"val2"
]
},
"rougail.var2": {
"owner": "default",
"value": "val1"
},
"rougail.dynval1.var": {
"owner": "default",
"value": "val1"
},
"rougail.dynval2.var": {
"owner": "default",
"value": "val2"
},
"rougail.var3": {
"owner": "default",
"value": "val1"
}
}

View file

@ -0,0 +1,10 @@
{
"rougail.var1": [
"val1",
"val2"
],
"rougail.var2": "val1",
"rougail.dynval1.var": "val1",
"rougail.dynval2.var": "val2",
"rougail.var3": "val1"
}

View file

@ -0,0 +1,19 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = StrOption(name="var1", doc="A suffix variable", multi=True, default=["val1", "val2"], default_multi="val1", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
option_3 = StrOption(name="var2", doc="A suffix variable2", default="val1", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
option_5 = StrOption(name="var", doc="A dynamic variable", default=Calculation(func['calc_value'], Params((ParamIdentifier()))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
optiondescription_4 = ConvertDynOptionDescription(name="dyn{{ identifier }}", doc="dyn{{ identifier }}", identifiers=Calculation(func['calc_value'], Params((ParamOption(option_2)))), children=[option_5], properties=frozenset({"standard"}), informations={'dynamic_variable': 'rougail.var1', 'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml']})
option_6 = StrOption(name="var3", doc="A variable calculated", default=Calculation(func['calc_value'], Params((ParamDynOption(option_5, Calculation(func['calc_value'], Params((ParamOption(option_3)))))))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3, optiondescription_4, option_6], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,18 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="var1", doc="A suffix variable", multi=True, default=["val1", "val2"], default_multi="val1", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
option_2 = StrOption(name="var2", doc="A suffix variable2", default="val1", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
option_4 = StrOption(name="var", doc="A dynamic variable", default=Calculation(func['calc_value'], Params((ParamIdentifier()))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
optiondescription_3 = ConvertDynOptionDescription(name="dyn{{ identifier }}", doc="dyn{{ identifier }}", identifiers=Calculation(func['calc_value'], Params((ParamOption(option_1)))), children=[option_4], properties=frozenset({"standard"}), informations={'dynamic_variable': 'var1', 'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml']})
option_5 = StrOption(name="var3", doc="A variable calculated", default=Calculation(func['calc_value'], Params((ParamDynOption(option_4, Calculation(func['calc_value'], Params((ParamOption(option_2)))))))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/60_5family_dynamic_calc_identifier/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2, optiondescription_3, option_5])