feat: add 'document_a_type' option

This commit is contained in:
egarette@silique.fr 2026-01-14 13:56:51 +01:00
parent 8d305c96fb
commit 8e2e70486c
5 changed files with 105 additions and 63 deletions

View file

@ -49,13 +49,17 @@ class Annotator(Walk):
self, self,
objectspace, objectspace,
*args, # pylint: disable=unused-argument *args, # pylint: disable=unused-argument
**kwargs,
) -> None: ) -> None:
if not objectspace.paths: if not objectspace.paths:
return return
self.objectspace = objectspace self.objectspace = objectspace
self.default_values = self.objectspace.rougailconfig[ if "force_default_value" in kwargs:
"doc.default_values" self.default_values = kwargs["force_default_value"]
] else:
self.default_values = self.objectspace.rougailconfig[
"doc.default_values"
]
self.regexp_description_get_paths = None self.regexp_description_get_paths = None
self.populate_family() self.populate_family()
self.populate_variable() self.populate_variable()
@ -283,12 +287,16 @@ class Annotator(Walk):
"propertyerror": values.propertyerror, "propertyerror": values.propertyerror,
} }
if isinstance(values, InformationCalculation): if isinstance(values, InformationCalculation):
return { value, variable_path = self.calculation_to_information_information(
"type": "information",
"value": self.calculation_to_information_information(
values, version, path values, version, path
), )
ret = {
"type": "information",
"value": value,
} }
if variable_path:
ret["path"] = variable_path
return ret
if isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)): if isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)):
return { return {
"type": "identifier", "type": "identifier",
@ -347,10 +355,10 @@ class Annotator(Walk):
) -> Union[str, bool]: ) -> Union[str, bool]:
if values.variable: if values.variable:
variable_path = self.get_path_from_variable(values, version, path) variable_path = self.get_path_from_variable(values, version, path)
return _('the value of the information "{0}" of the variable "{1}"').format( return _('the value of the information "{0}" of the variable "{{0}}"').format(
values.information, variable_path values.information
) ), variable_path
return _('the value of the global information "{0}"').format(values.information) return _('the value of the global information "{0}"').format(values.information), None
def calculation_to_information_identifier( def calculation_to_information_identifier(
self, values, prop: str, version: str self, values, prop: str, version: str

View file

@ -202,6 +202,10 @@ doc:
types: types:
- file - file
document_a_type:
description: {_("Documentation a structural type")}
default: false
tabulars: tabulars:
description: {_('Variables and changelog documentation')} description: {_('Variables and changelog documentation')}
disabled: disabled:

View file

@ -44,6 +44,7 @@ class RougailOutputDoc(Examples, Changelog):
"""Rougail Output Doc: """Rougail Output Doc:
Generate documentation from rougail description files Generate documentation from rougail description files
""" """
output_name = "doc"
def __init__( def __init__(
self, self,
@ -61,34 +62,17 @@ class RougailOutputDoc(Examples, Changelog):
from rougail import RougailConfig from rougail import RougailConfig
rougailconfig = RougailConfig rougailconfig = RougailConfig
if rougailconfig["step.output"] != "doc": if rougailconfig["step.output"] != self.output_name:
rougailconfig["step.output"] = "doc" rougailconfig["step.output"] = self.output_name
if rougailconfig["step.output"] != "doc": if rougailconfig["step.output"] != self.output_name:
raise Exception("doc is not set as step.output") raise Exception(_("{0} is not set as step.output").format(self.output_name))
self.outputs = OutPuts().get() self.config = config
self.conf = config
if true_config is None: if true_config is None:
self.true_config = config self.true_config = config
else: else:
self.true_config = true_config self.true_config = true_config
self.modes_level = rougailconfig["modes_level"]
self.disabled_modes = []
if self.modes_level:
for mode in self.modes_level:
for prop in self.conf.property.get():
if mode != prop:
continue
self.disabled_modes.append(prop)
# self.conf.property.read_write()
# # self.conf.property.remove("cache")
self.rougailconfig = rougailconfig self.rougailconfig = rougailconfig
self.informations = None self.informations = None
try:
groups.namespace
self.support_namespace = True
except AttributeError:
self.support_namespace = False
self.property_to_string = get_properties_to_string()
self.formatter = None self.formatter = None
super().__init__() super().__init__()
@ -116,17 +100,33 @@ class RougailOutputDoc(Examples, Changelog):
return ret return ret
def load(self): def load(self):
self.document_a_type = self.rougailconfig["doc.document_a_type"]
self.modes_level = self.rougailconfig["modes_level"]
self.disabled_modes = []
if self.modes_level:
for mode in self.modes_level:
for prop in self.true_config.property.get():
if mode != prop:
continue
self.disabled_modes.append(prop)
try:
groups.namespace
self.support_namespace = True
except AttributeError:
self.support_namespace = False
self.property_to_string = get_properties_to_string()
self.outputs = OutPuts().get()
self.dynamic_paths = {} self.dynamic_paths = {}
config = self.conf.unrestraint config = self.config.unrestraint
self.populate_dynamics(config=config) self.populate_dynamics(config=config)
informations = self.parse_families(config) informations = self.parse_families(config)
if informations is None: if informations is None:
informations = {} informations = {}
elif self.conf.type() not in ['config', 'metaconfig', 'groupconfig', 'mixconfig']: elif self.config.type() not in ['config', 'metaconfig', 'groupconfig', 'mixconfig']:
root_informations = {} root_informations = {}
infos = root_informations infos = root_informations
family = self.true_config family = self.true_config
for path in self.conf.path().split('.'): for path in self.config.path().split('.'):
family = family.option(path) family = family.option(path)
name = family.name(uncalculated=True) name = family.name(uncalculated=True)
infos[name] = self.get_root_family(family) infos[name] = self.get_root_family(family)
@ -148,7 +148,7 @@ class RougailOutputDoc(Examples, Changelog):
def populate_dynamics(self, *, config=None, reload=False): def populate_dynamics(self, *, config=None, reload=False):
if config is None: if config is None:
config = self.conf.unrestraint config = self.config.unrestraint
self._populate_dynamics(config, reload) self._populate_dynamics(config, reload)
def _populate_dynamics(self, family, reload, uncalculated=False) -> None: def _populate_dynamics(self, family, reload, uncalculated=False) -> None:
@ -177,7 +177,7 @@ class RougailOutputDoc(Examples, Changelog):
self.dynamic_paths[path] = { self.dynamic_paths[path] = {
"names": [], "names": [],
"identifiers": [], "identifiers": [],
"path": path, "path": self.doc_path(path),
} }
if not obj.information.get("forced_description", False): if not obj.information.get("forced_description", False):
self.dynamic_paths[path]["description"] = self._convert_description( self.dynamic_paths[path]["description"] = self._convert_description(
@ -459,7 +459,7 @@ class RougailOutputDoc(Examples, Changelog):
child.description(uncalculated=True), type_, its_a_path=False child.description(uncalculated=True), type_, its_a_path=False
) )
if not child.isdynamic(): if not child.isdynamic():
informations["path"] = child.path(uncalculated=True) informations["path"] = self.doc_path(child.path(uncalculated=True))
informations["names"] = [child.name()] informations["names"] = [child.name()]
if description is not None: if description is not None:
informations["description"] = description informations["description"] = description
@ -672,21 +672,22 @@ class RougailOutputDoc(Examples, Changelog):
if isinstance(calculation, list): if isinstance(calculation, list):
values = [] values = []
for cal in calculation: for cal in calculation:
value = self._calculation_to_string(child, cal, prop, inside_list=True) value = self._calculation_to_string(child, cal, prop)
if value is not None: if value is not None:
values.append(value) values.append(value)
return values return values
return self._calculation_to_string(child, calculation, prop) return self._calculation_to_string(child, calculation, prop)
def _calculation_to_string(self, child, calculation, prop, inside_list=False): def _calculation_to_string(self, child, calculation, prop):
if "description" in calculation: if "description" in calculation:
values = calculation["description"] values = calculation
# if not values.endswith("."): if self.document_a_type and "variables" in values:
# values += "." for variable in list(values["variables"]):
return calculation variable["path"] = self.doc_path(variable["path"])
if "type" not in calculation:
return calculation["value"] elif "type" not in calculation:
if calculation["type"] == "jinja": values = calculation["value"]
elif calculation["type"] == "jinja":
values = self._calculation_jinja_to_string(child, calculation, prop) values = self._calculation_jinja_to_string(child, calculation, prop)
elif calculation["type"] == "variable": elif calculation["type"] == "variable":
values = self._calculation_variable_to_string(child, calculation, prop) values = self._calculation_variable_to_string(child, calculation, prop)
@ -697,10 +698,11 @@ class RougailOutputDoc(Examples, Changelog):
values = _("the value of the identifier") values = _("the value of the identifier")
elif calculation["type"] == "information": elif calculation["type"] == "information":
values = calculation["value"] values = calculation["value"]
if "path" in calculation:
variable_path = self.doc_path(calculation["path"])
values = values.format(variable_path)
else: else:
values = _("the value of the {0}").format(calculation["type"]) values = _("the value of the {0}").format(calculation["type"])
# if not inside_list and isinstance(values, str) and not values.endswith("."):
# values += "."
return values return values
def _calculation_jinja_to_string(self, child, calculation, prop): def _calculation_jinja_to_string(self, child, calculation, prop):
@ -717,7 +719,7 @@ class RougailOutputDoc(Examples, Changelog):
'"{0}" is a calculation for {1} but has no description in {2}' '"{0}" is a calculation for {1} but has no description in {2}'
).format( ).format(
prop, prop,
child.path(), self.doc_path(child.path()),
display_xmlfiles(child.information.get("ymlfiles")), display_xmlfiles(child.information.get("ymlfiles")),
) )
warn( warn(
@ -751,7 +753,7 @@ class RougailOutputDoc(Examples, Changelog):
if cpath: if cpath:
all_is_undocumented = False all_is_undocumented = False
path_obj = { path_obj = {
"path": cpath, "path": self.doc_path(cpath),
} }
if identifiers: if identifiers:
path_obj["identifiers"] = identifiers path_obj["identifiers"] = identifiers
@ -813,7 +815,7 @@ class RougailOutputDoc(Examples, Changelog):
values = { values = {
"message": true_msg, "message": true_msg,
"path": { "path": {
"path": calculation["ori_path"], "path": self.doc_path(calculation["ori_path"]),
}, },
"description": description, "description": description,
} }
@ -901,7 +903,7 @@ class RougailOutputDoc(Examples, Changelog):
else: else:
msg = _('when the variable "{{0}}" has the value "{0}"') msg = _('when the variable "{{0}}" has the value "{0}"')
path_obj = { path_obj = {
"path": variable_path, "path": self.doc_path(variable_path),
} }
if identifiers: if identifiers:
path_obj["identifiers"] = identifiers path_obj["identifiers"] = identifiers
@ -925,7 +927,7 @@ class RougailOutputDoc(Examples, Changelog):
+ "$" + "$"
) )
information = self.dynamic_paths[current_path] information = self.dynamic_paths[current_path]
path = information["path"] path = current_path
for identifiers in information["identifiers"]: for identifiers in information["identifiers"]:
cpath = calc_path(path, identifiers=identifiers) cpath = calc_path(path, identifiers=identifiers)
if regexp and not regexp.search(cpath): if regexp and not regexp.search(cpath):
@ -948,3 +950,10 @@ class RougailOutputDoc(Examples, Changelog):
if variable and self.is_inaccessible_user_data(variable): if variable and self.is_inaccessible_user_data(variable):
return self._get_unmodified_default_value(variable) return self._get_unmodified_default_value(variable)
raise VariableCalculationDependencyError() raise VariableCalculationDependencyError()
def doc_path(self, path):
if self.document_a_type:
if not '.' in path:
return None
return path.split('.', 1)[-1]
return path

View file

@ -57,18 +57,34 @@ class Examples: # pylint: disable=no-member,too-few-public-methods
return_string = "" return_string = ""
datas = [] datas = []
if self.examples_mandatories: if self.examples_mandatories:
if self.document_a_type:
col = list(self.examples_mandatories)
if len(col) == 1:
examples_mandatories = self.examples_mandatories[col[0]]
else:
examples_mandatories = self.examples_mandatories
else:
examples_mandatories = self.examples_mandatories
datas.extend([ datas.extend([
self.formatter.title( self.formatter.title(
_("Example with mandatory variables not filled in"), self.level _("Example with mandatory variables not filled in"), self.level
), ),
self.formatter.yaml(self.examples_mandatories), self.formatter.yaml(examples_mandatories),
self.formatter.end_family(self.level), self.formatter.end_family(self.level),
]) ])
if self.examples: if self.examples:
if self.document_a_type:
col = list(self.examples)
if len(col) == 1:
examples = self.examples[col[0]]
else:
examples = self.examples
else:
examples = self.examples
datas.extend([self.formatter.title( datas.extend([self.formatter.title(
_("Example with all variables modifiable"), self.level _("Example with all variables modifiable"), self.level
), ),
self.formatter.yaml(self.examples), self.formatter.yaml(examples),
self.formatter.end_family(self.level), self.formatter.end_family(self.level),
]) ])
return self.formatter.compute(datas) return self.formatter.compute(datas)

View file

@ -332,6 +332,8 @@ class CommonFormatter:
return path return path
ret_paths = [] ret_paths = []
path = informations["path"] path = informations["path"]
if not path:
return None
if is_bold: if is_bold:
bold = self.bold bold = self.bold
else: else:
@ -426,15 +428,14 @@ class CommonFormatter:
ret = [self.namespace_to_title(informations, level)] ret = [self.namespace_to_title(informations, level)]
else: else:
ret = [self.title(self.get_description("family", informations, {}, None), level)] ret = [self.title(self.get_description("family", informations, {}, None), level)]
fam_info = self.family_informations()
if fam_info:
ret.append(fam_info)
msg = [] msg = []
helps = informations.get("help") helps = informations.get("help")
if helps: if helps:
for help_ in helps: for help_ in helps:
msg.extend([to_phrase(h) for h in help_.strip().split('\n')]) msg.extend([to_phrase(h) for h in help_.strip().split('\n')])
msg.append(self.section(_("Path"), self.display_paths(informations, {}, None, with_anchor=False, is_bold=False), type_="family")) path = self.display_paths(informations, {}, None, with_anchor=False, is_bold=False)
if path:
msg.append(self.section(_("Path"), path, type_="family"))
calculated_properties = [] calculated_properties = []
property_str = self.property_to_string(informations, calculated_properties, {}) property_str = self.property_to_string(informations, calculated_properties, {})
if property_str: if property_str:
@ -447,7 +448,11 @@ class CommonFormatter:
self.section(_("Identifiers"), informations["identifier"], type_="family") self.section(_("Identifiers"), informations["identifier"], type_="family")
) )
starts_line = self.family_informations_starts_line() starts_line = self.family_informations_starts_line()
ret.append(self.family_informations_ends_line().join([starts_line + m for m in msg]) + self.end_family_informations()) if msg:
fam_info = self.family_informations()
if fam_info:
ret.append(fam_info)
ret.append(self.family_informations_ends_line().join([starts_line + m for m in msg]) + self.end_family_informations())
return ret return ret
def family_informations_starts_line(self) -> str: def family_informations_starts_line(self) -> str: