rougail-output-doc/src/rougail/output_doc/example.py

241 lines
9.5 KiB
Python
Raw Normal View History

2024-11-15 08:13:45 +01:00
"""
Silique (https://www.silique.fr)
Copyright (C) 2024-2026
2024-11-15 08:13:45 +01:00
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from ruamel.yaml import CommentedMap
from ruamel.yaml.representer import RoundTripRepresenter
2026-03-29 11:01:15 +02:00
from tiramisu import Calculation, owners
2024-11-15 08:13:45 +01:00
2026-03-29 11:01:15 +02:00
from .utils import _, dump, to_phrase
2024-11-15 08:13:45 +01:00
2026-03-29 11:01:15 +02:00
# XXX explicit null (see rougail-output-formatter # pylint: disable=W0511
def represent_none(
self, data
): # pylint: disable=missing-function-docstring,unused-argument
return self.represent_scalar("tag:yaml.org,2002:null", "null")
2026-03-29 11:01:15 +02:00
def represent_str(self, data): # pylint: disable=missing-function-docstring
if data == "":
return self.represent_scalar("tag:yaml.org,2002:null", "")
return self.represent_scalar("tag:yaml.org,2002:str", data)
RoundTripRepresenter.add_representer(type(None), represent_none)
RoundTripRepresenter.add_representer(str, represent_str)
2026-03-29 11:01:15 +02:00
# XXX # pylint: disable=W0511
2024-11-15 08:13:45 +01:00
class Examples: # pylint: disable=no-member,too-few-public-methods
"""Build examples"""
2024-11-20 21:12:56 +01:00
2024-11-15 08:13:45 +01:00
def __init__(self):
2026-03-29 11:01:15 +02:00
self.comment_examples = None
self.level = None
self.comment_examples_column = None
2024-11-15 08:13:45 +01:00
def gen_doc_examples(self):
"""Return examples"""
2025-11-29 22:21:06 +01:00
self.comment_examples = self.rougailconfig["doc.examples.comment"]
self.level = self.rougailconfig["doc.title_level"]
if self.comment_examples:
2026-03-29 11:01:15 +02:00
self.comment_examples_column = self.rougailconfig[
"doc.examples.comment_column"
]
config = self.true_config.config.copy()
config.information.set("description_type", "description")
config.property.read_write()
self._set_mandatories(config)
datas = []
2026-03-29 11:01:15 +02:00
for only_modified in [True, False]:
if not only_modified:
self._set_examples(config)
results = CommentedMap()
true_results = results
if self.true_config == self.config:
root_config = config
2026-01-14 13:56:51 +01:00
else:
2026-03-29 11:01:15 +02:00
root_config = config.option(self.config.path())
current_option = self.true_config
subpaths = self.config.path().split(".")
if not self.config.isoptiondescription():
subpaths = subpaths[:-1]
for subpath in subpaths:
current_option = current_option.option(subpath)
name = current_option.name()
results[name] = CommentedMap()
self._set_description(results, name, current_option)
results = results[name]
if root_config.isoptiondescription():
self._example_parse_family(
root_config.value.get(), results, only_modified
)
2026-01-14 13:56:51 +01:00
else:
2026-03-29 11:01:15 +02:00
self._set_example_value(
results, root_config, root_config.value.get(), only_modified
)
if results:
if only_modified:
title = _("Example with mandatory variables not filled in")
else:
title = _("Example with all variables modifiable")
datas.extend(
[
self.formatter.title(title, self.level),
self.formatter.yaml(dump(true_results)),
self.formatter.end_family(self.level),
]
)
return self.formatter.compute(datas)
2024-11-15 08:13:45 +01:00
2026-03-29 11:01:15 +02:00
def _set_mandatories(self, config):
for calculated_too in [False, True]:
for option in config.value.mandatory():
if not calculated_too:
uncalculated = option.value.default(uncalculated=True)
if isinstance(uncalculated, Calculation):
continue
self._set_value_example(option, self._get_an_example(option))
def _get_an_example(self, option):
value = self._get_value_from_example(option)
if value is None:
variable_type = option.information.get("type")
if variable_type == "choice":
value = option.value.list()
if not self._is_multi(option) and value:
if value[0] is not None:
value = value[0]
elif len(value) > 1:
value = value[1]
2024-11-15 08:13:45 +01:00
else:
2026-03-29 11:01:15 +02:00
value = self.convert_option.get(option.information.get("type"), {}).get(
"example"
)
if value is None:
value = "example"
if self._is_multi(option):
value = [value]
if option.isfollower() and option.index() and variable_type == "string":
value += str(option.index())
return value
def _set_examples(self, config):
def _set_example(subconfig):
for option in subconfig:
if option.isoptiondescription():
_set_example(option)
else:
# force examples value + do not let empty value
examples = self._get_value_from_example(option)
if examples is None:
if self._is_multi(option):
if not option.value.get():
examples = self._get_an_example(option)
elif option.value.get() is None:
examples = self._get_an_example(option)
if examples is None:
continue
self._set_value_example(option, examples)
_set_example(config)
self._set_mandatories(config)
def _set_value_example(self, option, value):
if option.isleader():
ori_len = option.value.len()
current_len = len(value)
if ori_len > current_len:
for idx in reversed(range(current_len, ori_len)):
option.value.pop(idx)
option.value.set(value)
def _get_value_from_example(self, option):
examples = option.information.get("examples", None)
if examples is None:
return None
if self._is_multi(option):
examples = list(examples)
2025-10-14 12:58:39 +02:00
else:
2026-03-29 11:01:15 +02:00
examples = examples[0]
return examples
def _is_multi(self, option):
if option.isfollower():
return option.issubmulti()
return option.ismulti()
def _example_parse_family(self, config, results, only_modified):
for option, values in config.items():
if option.isoptiondescription():
if option.isleadership():
subresults = self._example_parse_leadership(values, only_modified)
if subresults:
name = option.name()
results[name] = subresults
self._set_description(results, name, option)
else:
2026-03-29 11:01:15 +02:00
subresults = CommentedMap()
self._example_parse_family(values, subresults, only_modified)
if subresults:
name = option.name()
results[name] = subresults
self._set_description(results, name, option)
else:
self._set_example_value(results, option, values, only_modified)
def _set_example_value(self, results, option, values, only_modified):
if self._is_valid_owner(option, only_modified):
name = option.name()
results[name] = values
self._set_description(results, name, option)
def _is_valid_owner(self, option, only_modified):
return (
option.type(only_self=True, translation=False) != "symlink"
and not only_modified
or (not option.owner.isdefault() and option.owner.get() != owners.forced)
)
def _example_parse_leadership(self, values, only_modified):
leadership_iter = iter(values.items())
leader, leader_values = next(leadership_iter)
if not self._is_valid_owner(leader, only_modified):
return None
leadership = [CommentedMap() for idx in range(len(leader_values))]
for idx, value in enumerate(leader_values):
self._set_example_value(leadership[idx], leader, value, only_modified)
for option, value in leadership_iter:
idx = option.index()
self._set_example_value(leadership[idx], option, value, only_modified)
return leadership
def _set_description(self, results, name, option):
if not self.comment_examples or option.information.get(
"forced_description", False
):
return
description = to_phrase(option.description())
if description.endswith("."):
description = description[:-1]
results.yaml_add_eol_comment(
description, name, column=self.comment_examples_column
)