feat: black + improvement

This commit is contained in:
egarette@silique.fr 2024-11-01 11:17:14 +01:00
parent db53c752dc
commit 1701d317d4
8 changed files with 691 additions and 478 deletions

40
pyproject.toml Normal file
View file

@ -0,0 +1,40 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "rougail.output_doc"
version = "0.1.0rc0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md"
description = "Rougail output doc"
requires-python = ">=3.8"
license = {file = "LICENSE"}
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
]
dependencies = [
"rougail ~= 1.1.0",
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/rougail-output-exporter"
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
update_changelog_on_bump = true
changelog_merge_prerelease = true

View file

@ -1,25 +1,22 @@
#!/usr/bin/env python3
"""
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2024
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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/>.
"""
#FIXME si plusieurs example dont le 1er est none tester les autres : tests/dictionaries/00_8test_none
# FIXME si plusieurs example dont le 1er est none tester les autres : tests/dictionaries/00_8test_none
from tiramisu import Calculation
from tiramisu.error import display_list
import tabulate as tabulate_module
@ -27,62 +24,62 @@ from tabulate import tabulate
from warnings import warn
from typing import Optional
from gettext import gettext as _
from rougail.error import display_xmlfiles
from rougail import RougailConfig, Rougail, CONVERT_OPTION
from rougail.object_model import PROPERTY_ATTRIBUTE
from .config import OutPuts
from .i18n import _
ENTER = "\n\n"
DocTypes = {
'domainname': {
'params': {
'allow_startswith_dot': _('the domain name can starts by a dot'),
'allow_without_dot': _('the domain name can be only a hostname'),
'allow_ip': _('the domain name can be an IP'),
'allow_cidr_network': _('the domain name can be network in CIDR format'),
"domainname": {
"params": {
"allow_startswith_dot": _("the domain name can starts by a dot"),
"allow_without_dot": _("the domain name can be a hostname"),
"allow_ip": _("the domain name can be an IP"),
"allow_cidr_network": _("the domain name can be network in CIDR format"),
},
},
'number': {
'params': {
'min_number': _('the minimum value is {value}'),
'max_number': _('the maximum value is {value}'),
"number": {
"params": {
"min_number": _("the minimum value is {value}"),
"max_number": _("the maximum value is {value}"),
},
},
'ip': {
'msg': 'IP',
'params': {
'cidr': _('IP must be in CIDR format'),
'private_only': _('private IP are allowed'),
'allow_reserved': _('reserved IP are allowed'),
"ip": {
"msg": "IP",
"params": {
"cidr": _("IP must be in CIDR format"),
"private_only": _("private IP are allowed"),
"allow_reserved": _("reserved IP are allowed"),
},
},
'hostname': {
'params': {
'allow_ip': _('the host name can be an IP'),
"hostname": {
"params": {
"allow_ip": _("the host name can be an IP"),
},
},
'web_address': {
'params': {
'allow_ip': _('the domain name in web address can be an IP'),
'allow_without_dot': _('the domain name in web address can be only a hostname'),
"web_address": {
"params": {
"allow_ip": _("the domain name in web address can be an IP"),
"allow_without_dot": _(
"the domain name in web address can be only a hostname"
),
},
},
'port': {
'params': {
'allow_range': _('can be range of port'),
'allow_protocol': _('can have the protocol'),
'allow_zero': _('port 0 is allowed'),
'allow_wellknown': _('ports 1 to 1023 are allowed'),
'allow_registred': _('ports 1024 to 49151 are allowed'),
'allow_private': _('ports greater than 49152 are allowed'),
"port": {
"params": {
"allow_range": _("can be range of port"),
"allow_protocol": _("can have the protocol"),
"allow_zero": _("port 0 is allowed"),
"allow_wellknown": _("ports 1 to 1023 are allowed"),
"allow_registred": _("ports 1024 to 49151 are allowed"),
"allow_private": _("ports greater than 49152 are allowed"),
},
},
}
@ -92,36 +89,48 @@ ROUGAIL_VARIABLE_TYPE = (
class RougailOutputDoc:
def __init__(self,
*,
config: 'Config'=None,
rougailconfig: RougailConfig=None,
):
def __init__(
self,
*,
config: "Config" = None,
rougailconfig: RougailConfig = None,
**kwarg,
):
if rougailconfig is None:
rougailconfig = RougailConfig
if rougailconfig["step.output"] != "doc":
rougailconfig["step.output"] = "doc"
if rougailconfig["step.output"] != "doc":
raise Exception("doc is not set as step.output")
self.rougailconfig = rougailconfig
outputs = OutPuts().get()
output = self.rougailconfig['doc.output_format']
output = self.rougailconfig["doc.output_format"]
if output not in outputs:
raise Exception(f'cannot find output "{output}", available outputs: {list(outputs)}')
raise Exception(
f'cannot find output "{output}", available outputs: {list(outputs)}'
)
if config is None:
rougail = Rougail(self.rougailconfig)
rougail.converted.plugins.append('output_doc')
rougail.converted.plugins.append("output_doc")
config = rougail.get_config()
self.conf = config
self.conf.property.setdefault(frozenset({'advanced'}), 'read_write', 'append')
self.conf.property.setdefault(frozenset({"advanced"}), "read_write", "append")
self.conf.property.read_write()
self.conf.property.remove("cache")
self.dynamic_paths = {}
self.formater = outputs[output]()
self.level = self.rougailconfig['doc.title_level']
#self.property_to_string = [('mandatory', 'obligatoire'), ('hidden', 'cachée'), ('disabled', 'désactivée'), ('unique', 'unique'), ('force_store_value', 'modifié automatiquement')]
self.property_to_string = [('mandatory', _('mandatory')),
('hidden', _('hidden')),
('disabled', _('disabled')),
('unique', _('unique')),
('force_store_value', _('auto modified')),
]
self.level = self.rougailconfig["doc.title_level"]
# self.property_to_string = [('mandatory', 'obligatoire'), ('hidden', 'cachée'), ('disabled', 'désactivée'), ('unique', 'unique'), ('force_store_value', 'modifié automatiquement')]
self.property_to_string = [
("mandatory", _("mandatory")),
("hidden", _("hidden")),
("disabled", _("disabled")),
("unique", _("unique")),
("force_store_value", _("auto modified")),
]
def run(self):
print(self.gen_doc())
def gen_doc(self):
tabulate_module.PRESERVE_WHITESPACE = True
@ -133,51 +142,62 @@ class RougailOutputDoc:
name = namespace.name()
examples_mini[name] = {}
examples_all[name] = {}
doc = self._display_doc(
self.display_families(
namespace,
self.level + 1,
examples_mini[name],
examples_all[name],
),
[],
) + '\n'
doc = (
self._display_doc(
self.display_families(
namespace,
self.level + 1,
examples_mini[name],
examples_all[name],
),
[],
)
+ "\n"
)
if not examples_mini[name]:
del examples_mini[name]
if not examples_all[name]:
del examples_all[name]
else:
return_string += self.formater.title(_(f'Variables for "{namespace.name()}"'), self.level)
return_string += self.formater.title(
_(f'Variables for "{namespace.name()}"'), self.level
)
return_string += doc
else:
doc = self._display_doc(
self.display_families(
self.conf.unrestraint,
self.level + 1,
examples_mini,
examples_all,
),
[],
) + '\n'
doc = (
self._display_doc(
self.display_families(
self.conf.unrestraint,
self.level + 1,
examples_mini,
examples_all,
),
[],
)
+ "\n"
)
if examples_all:
return_string += self.formater.title(_(f'Variables'), self.level)
return_string += self.formater.title(_(f"Variables"), self.level)
return_string += doc
if not examples_all:
return ''
if examples_mini:
#"Exemple avec les variables obligatoires non renseignées"
return_string += self.formater.title(
_("Example with mandatory variables not filled in"), self.level
)
return_string += self.formater.yaml(examples_mini)
if examples_all:
#"Exemple avec tous les variables modifiables"
return_string += self.formater.title("Example with all variables modifiable", self.level)
return_string += self.formater.yaml(examples_all)
return ""
if self.rougailconfig["doc.with_example"]:
if examples_mini:
# "Exemple avec les variables obligatoires non renseignées"
return_string += self.formater.title(
_("Example with mandatory variables not filled in"), self.level
)
return_string += self.formater.yaml(examples_mini)
if examples_all:
# "Exemple avec tous les variables modifiables"
return_string += self.formater.title(
"Example with all variables modifiable", self.level
)
return_string += self.formater.yaml(examples_all)
return return_string
def _display_doc(self, variables, add_paths):
return_string = ''
return_string = ""
for variable in variables:
typ = variable["type"]
path = variable["path"]
@ -189,21 +209,50 @@ class RougailOutputDoc:
else:
for idx, path in enumerate(variable["paths"]):
if path in self.dynamic_paths:
paths_msg = display_list([self.formater.bold(path_) for path_ in self.dynamic_paths[path]['paths']], separator='or')
variable["objects"][idx][0] = variable["objects"][idx][0].replace('{{ ROUGAIL_PATH }}', paths_msg)
suffixes = self.dynamic_paths[path]['suffixes']
paths_msg = display_list(
[
self.formater.bold(path_)
for path_ in self.dynamic_paths[path]["paths"]
],
separator="or",
)
variable["objects"][idx][0] = variable["objects"][idx][
0
].replace("{{ ROUGAIL_PATH }}", paths_msg)
identifiers = self.dynamic_paths[path]["identifiers"]
description = variable["objects"][idx][1][0]
if "{{ suffix }}" in description:
if description.endswith('.'):
if "{{ identifier }}" in description:
if description.endswith("."):
description = description[:-1]
comment_msg = self.to_phrase(display_list([description.replace('{{ suffix }}', self.formater.italic(suffix)) for suffix in suffixes], separator='or', add_quote=True))
comment_msg = self.to_phrase(
display_list(
[
description.replace(
"{{ identifier }}",
self.formater.italic(identifier),
)
for identifier in identifiers
],
separator="or",
add_quote=True,
)
)
variable["objects"][idx][1][0] = comment_msg
variable["objects"][idx][1] = self.formater.join(variable["objects"][idx][1])
return_string += self.formater.table(tabulate(
variable["objects"],
headers=self.formater.table_header(['Variable', 'Description']),
tablefmt=self.formater.name,
)) + '\n\n'
variable["objects"][idx][1] = self.formater.join(
variable["objects"][idx][1]
)
return_string += (
self.formater.table(
tabulate(
variable["objects"],
headers=self.formater.table_header(
["Variable", "Description"]
),
tablefmt=self.formater.name,
)
)
+ "\n\n"
)
add_paths.append(path)
return return_string
@ -236,8 +285,12 @@ class RougailOutputDoc:
continue
path = child.path(uncalculated=True)
if child.isdynamic():
self.dynamic_paths.setdefault(path, {'paths': [], 'suffixes': []})['paths'].append(child.path())
self.dynamic_paths[path]['suffixes'].append(child.suffixes()[-1])
self.dynamic_paths.setdefault(
path, {"paths": [], "identifiers": []}
)["paths"].append(child.path())
self.dynamic_paths[path]["identifiers"].append(
child.identifiers()[-1]
)
if not variables or variables[-1]["type"] != "variables":
variables.append(
{
@ -298,9 +351,18 @@ class RougailOutputDoc:
title = f"{family.path()}"
isdynamic = family.isdynamic(only_self=True)
if isdynamic:
suffixes = family.suffixes(only_self=True)
if '{{ suffix }}' in title:
title = display_list([title.replace('{{ suffix }}', self.formater.italic(suffix)) for suffix in suffixes], separator='or', add_quote=True)
identifiers = family.identifiers(only_self=True)
if "{{ identifier }}" in title:
title = display_list(
[
title.replace(
"{{ identifier }}", self.formater.italic(identifier)
)
for identifier in identifiers
],
separator="or",
add_quote=True,
)
msg = self.formater.title(title, level)
subparameter = []
self.manage_properties(family, subparameter)
@ -309,8 +371,8 @@ class RougailOutputDoc:
comment = []
self.subparameter_to_parameter(subparameter, comment)
if comment:
msg += '\n'.join(comment) + ENTER
help = self.to_phrase(family.information.get('help', ""))
msg += "\n".join(comment) + ENTER
help = self.to_phrase(family.information.get("help", ""))
if help:
msg += "\n" + help + ENTER
if family.isleadership():
@ -318,39 +380,43 @@ class RougailOutputDoc:
help = "This family contains lists of variable blocks."
msg += "\n" + help + ENTER
if isdynamic:
suffixes = family.suffixes(only_self=True , uncalculated=True)
if isinstance(suffixes, Calculation):
suffixes = self.to_string(family, 'dynamic')
if isinstance(suffixes, list):
for idx, val in enumerate(suffixes):
identifiers = family.identifiers(only_self=True, uncalculated=True)
if isinstance(identifiers, Calculation):
identifiers = self.to_string(family, "dynamic")
if isinstance(identifiers, list):
for idx, val in enumerate(identifiers):
if not isinstance(val, Calculation):
continue
suffixes[idx] = self.to_string(family, 'dynamic', f'_{idx}')
suffixes = self.formater.list(suffixes)
#help = f"Cette famille construit des familles dynamiquement.\n\n{self.formater.bold('Suffixes')}: {suffixes}"
help = f"This family builds families dynamically.\n\n{self.formater.bold('Suffixes')}: {suffixes}"
identifiers[idx] = self.to_string(family, "dynamic", f"_{idx}")
identifiers = self.formater.list(identifiers)
# help = f"Cette famille construit des familles dynamiquement.\n\n{self.formater.bold('Identifiers')}: {identifiers}"
help = f"This family builds families dynamically.\n\n{self.formater.bold('Identifiers')}: {identifiers}"
msg += "\n" + help + ENTER
return msg
def manage_properties(self,
variable,
subparameter,
):
def manage_properties(
self,
variable,
subparameter,
):
properties = variable.property.get(uncalculated=True)
for mode in self.rougailconfig['modes_level']:
for mode in self.rougailconfig["modes_level"]:
if mode in properties:
subparameter.append((self.formater.prop(mode), None, None))
break
for prop, msg in self.property_to_string:
if prop in properties:
subparameter.append((self.formater.prop(msg), None, None))
elif variable.information.get(f'{prop}_calculation', False):
subparameter.append((self.formater.prop(msg), msg, self.to_string(variable, prop)))
elif variable.information.get(f"{prop}_calculation", False):
subparameter.append(
(self.formater.prop(msg), msg, self.to_string(variable, prop))
)
def subparameter_to_string(self,
subparameter,
):
subparameter_str = ''
def subparameter_to_string(
self,
subparameter,
):
subparameter_str = ""
for param in subparameter:
if param[1]:
subparameter_str += f"_{param[0]}_ "
@ -358,10 +424,11 @@ class RougailOutputDoc:
subparameter_str += f"{param[0]} "
return subparameter_str[:-1]
def subparameter_to_parameter(self,
subparameter,
comment,
):
def subparameter_to_parameter(
self,
subparameter,
comment,
):
for param in subparameter:
if not param[1]:
continue
@ -370,10 +437,10 @@ class RougailOutputDoc:
def to_phrase(self, msg):
if not msg:
return ''
return ""
msg = str(msg).strip()
if not msg.endswith('.'):
msg += '.'
if not msg.endswith("."):
msg += "."
return msg[0].upper() + msg[1:]
def display_variable(
@ -389,16 +456,18 @@ class RougailOutputDoc:
subparameter = []
description = variable.description(uncalculated=True)
comment = [self.to_phrase(description)]
help_ = self.to_phrase(variable.information.get("help", ''))
help_ = self.to_phrase(variable.information.get("help", ""))
if help_:
comment.append(help_)
self.type_to_string(variable,
subparameter,
comment,
)
self.manage_properties(variable,
subparameter,
)
self.type_to_string(
variable,
subparameter,
comment,
)
self.manage_properties(
variable,
subparameter,
)
if variable.ismulti():
multi = not variable.isfollower() or variable.issubmulti()
else:
@ -410,127 +479,161 @@ class RougailOutputDoc:
if variable.name() == description:
warning = f'No attribute "description" for variable "{variable.path()}" in {display_xmlfiles(variable.information.get("dictionaries"))}'
warn(warning)
default = self.get_default(variable,
comment,
)
default = self.get_default(
variable,
comment,
)
default_in_choices = False
if variable.information.get("type") == 'choice':
if variable.information.get("type") == "choice":
choices = variable.value.list(uncalculated=True)
if isinstance(choices, Calculation):
choices = self.to_string(variable, 'choice')
choices = self.to_string(variable, "choice")
if isinstance(choices, list):
for idx, val in enumerate(choices):
if not isinstance(val, Calculation):
if default is not None and val == default:
choices[idx] = str(val) + '' + _("(default)")
choices[idx] = str(val) + "" + _("(default)")
default_in_choices = True
continue
choices[idx] = self.to_string(variable, 'choice', f'_{idx}')
choices[idx] = self.to_string(variable, "choice", f"_{idx}")
choices = self.formater.list(choices)
comment.append(f'{self.formater.bold(_("Choices"))}: {choices}')
# choice
if default is not None and not default_in_choices:
comment.append(f"{self.formater.bold(_('Default'))}: {default}")
self.manage_exemples(multi,
variable,
examples_all,
examples_mini,
comment,
)
self.manage_exemples(
multi,
variable,
examples_all,
examples_mini,
comment,
)
self.subparameter_to_parameter(subparameter, comment)
self.formater.columns(parameter, comment)
return [self.formater.join(parameter), comment]
def get_default(self,
variable,
comment,
):
if variable.information.get('fake_default', False):
def get_default(
self,
variable,
comment,
):
if variable.information.get("fake_default", False):
default = None
else:
default = variable.value.get(uncalculated=True)
if default in [None, []]:
return
if isinstance(default, Calculation):
default = self.to_string(variable, 'default')
default = self.to_string(variable, "default")
if isinstance(default, list):
for idx, val in enumerate(default):
if not isinstance(val, Calculation):
continue
default[idx] = self.to_string(variable, 'default', f'_{idx}')
default[idx] = self.to_string(variable, "default", f"_{idx}")
default = self.formater.list(default)
return default
def to_string(self,
variable,
prop,
suffix='',
):
calculation_type = variable.information.get(f'{prop}_calculation_type{suffix}', None)
def to_string(
self,
variable,
prop,
identifier="",
):
calculation_type = variable.information.get(
f"{prop}_calculation_type{identifier}", None
)
if not calculation_type:
raise Exception(f'cannot find {prop}_calculation_type{suffix} information, do you have declare doc has a plugins?')
calculation = variable.information.get(f'{prop}_calculation{suffix}')
if calculation_type == 'jinja':
raise Exception(
f"cannot find {prop}_calculation_type{identifier} information, do you have declare doc has a plugins?"
)
calculation = variable.information.get(f"{prop}_calculation{identifier}")
if calculation_type == "jinja":
if calculation is not True:
values = self.formater.to_string(calculation)
else:
values = "issu d'un calcul"
values = "depends on a calculation"
warning = f'"{prop}" is a calculation for {variable.path()} but has no description in {display_xmlfiles(variable.information.get("dictionaries"))}'
warn(warning)
elif calculation_type == 'variable':
elif calculation_type == "variable":
if prop in PROPERTY_ATTRIBUTE:
values = self.formater.to_string(calculation)
else:
values = _(f'the value of the variable "{calculation}"')
elif calculation_type == "identifier":
if prop in PROPERTY_ATTRIBUTE:
values = self.formater.to_string(calculation)
else:
values = _(f"value of the {calculation_type}")
else:
values = _(f"value of the {calculation_type}")
if not values.endswith('.'):
values += '.'
if not values.endswith("."):
values += "."
return values
def type_to_string(self,
variable,
subparameter,
comment,
):
def type_to_string(
self,
variable,
subparameter,
comment,
):
variable_type = variable.information.get("type")
doc_type = DocTypes.get(variable_type, {'params': {}})
subparameter.append((self.formater.link(doc_type.get('msg', variable_type), ROUGAIL_VARIABLE_TYPE), None))
doc_type = DocTypes.get(variable_type, {"params": {}})
subparameter.append(
(
self.formater.link(
doc_type.get("msg", variable_type), ROUGAIL_VARIABLE_TYPE
),
None,
)
)
option = variable.get()
validators = []
for param, msg in doc_type['params'].items():
value = option.impl_get_extra(f'_{param}')
for param, msg in doc_type["params"].items():
value = option.impl_get_extra(f"_{param}")
if value is None:
value = option.impl_get_extra(param)
if value is not None and value is not False:
validators.append(msg.format(value=value))
valids = [name for name in variable.information.list() if name.startswith('validators_calculation_type_')]
valids = [
name
for name in variable.information.list()
if name.startswith("validators_calculation_type_")
]
if valids:
for idx in range(len(valids)):
validators.append(self.to_string(variable,
'validators',
f'_{idx}',
))
validators.append(
self.to_string(
variable,
"validators",
f"_{idx}",
)
)
if validators:
if len(validators) == 1:
comment.append(f'{self.formater.bold("Validator")}: ' + validators[0])
else:
comment.append(f'{self.formater.bold("Validators")}:' + self.formater.list(validators))
comment.append(
f'{self.formater.bold("Validators")}:'
+ self.formater.list(validators)
)
def manage_exemples(self,
multi,
variable,
examples_all,
examples_mini,
comment,
):
def manage_exemples(
self,
multi,
variable,
examples_all,
examples_mini,
comment,
):
example_mini = None
example_all = None
example = variable.information.get("test", None)
example = variable.information.get("examples", None)
if example is None:
example = variable.information.get("test", None)
default = variable.value.get()
if isinstance(example, tuple):
example = list(example)
mandatory = 'mandatory' in variable.property.get(uncalculated=True)
mandatory = "mandatory" in variable.property.get(uncalculated=True)
if example:
if not multi:
example = example[0]
@ -552,9 +655,11 @@ class RougailOutputDoc:
elif default not in [None, []]:
example_all = default
else:
example = CONVERT_OPTION.get(variable.information.get("type"), {}).get('example', None)
example = CONVERT_OPTION.get(variable.information.get("type"), {}).get(
"example", None
)
if example is None:
example = 'xxx'
example = "xxx"
if multi:
example = [example]
if mandatory:
@ -578,3 +683,7 @@ class RougailOutputDoc:
if example_mini is not None:
examples_mini[variable.name()] = example_mini
examples_all[variable.name()] = example_all
RougailOutput = RougailOutputDoc
__all__ = ("RougailOutputDoc",)

View file

@ -19,14 +19,24 @@ 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 undefined
from rougail.annotator.variable import Walk
from rougail.i18n import _
from rougail.error import DictConsistencyError
from rougail.object_model import Calculation, JinjaCalculation, VariableCalculation, \
InformationCalculation, IndexCalculation, SuffixCalculation, CONVERT_OPTION, \
PROPERTY_ATTRIBUTE
from rougail.object_model import (
Calculation,
JinjaCalculation,
VariableCalculation,
VariablePropertyCalculation,
IdentifierCalculation,
IdentifierPropertyCalculation,
InformationCalculation,
IndexCalculation,
CONVERT_OPTION,
PROPERTY_ATTRIBUTE,
)
class Annotator(Walk):
@ -45,21 +55,33 @@ class Annotator(Walk):
self.populate_family()
self.populate_variable()
def add_default_value(self,
family,
value,
*,
inside_list=False,
) -> None:
def get_examples_values(self, variable):
values = self.objectspace.informations.get(variable.path).get("examples", None)
if not values:
values = self.objectspace.informations.get(variable.path).get("test", None)
return values
def add_default_value(
self,
family,
value,
*,
inside_list=False,
) -> None:
if isinstance(value, Calculation):
default_values ='example'
default_values = "example"
if not inside_list:
default_values = [default_values]
if isinstance(value, VariableCalculation):
variable, suffix = self.objectspace.paths.get_with_dynamic(
value.variable, value.path_prefix, family.path, value.version, value.namespace, value.xmlfiles
if isinstance(value, (VariableCalculation, VariablePropertyCalculation)):
variable, identifier = self.objectspace.paths.get_with_dynamic(
value.variable,
value.path_prefix,
family.path,
value.version,
value.namespace,
value.xmlfiles,
)
values = self.objectspace.informations.get(variable.path).get('test', None)
values = self.get_examples_values(variable)
if values:
if inside_list:
default_values = list(values)
@ -81,11 +103,12 @@ class Annotator(Walk):
else:
for value in family.dynamic:
self.add_default_value(family, value, inside_list=True)
self.calculation_to_information(family.path,
'dynamic',
family.dynamic,
family.version,
)
self.calculation_to_information(
family.path,
"dynamic",
family.dynamic,
family.version,
)
def populate_variable(self) -> None:
"""convert variables"""
@ -93,29 +116,31 @@ class Annotator(Walk):
if variable.type == "symlink":
continue
if variable.type == "choice":
self.calculation_to_information(variable.path,
'choice',
variable.choices,
variable.version,
)
self.calculation_to_information(variable.path,
'default',
variable.default,
variable.version,
)
self.calculation_to_information(variable.path,
'validators',
variable.validators,
variable.version,
)
if variable.path in self.objectspace.leaders and \
not variable.default:
values = self.objectspace.informations.get(variable.path).get('test', None)
self.calculation_to_information(
variable.path,
"choice",
variable.choices,
variable.version,
)
self.calculation_to_information(
variable.path,
"default",
variable.default,
variable.version,
)
self.calculation_to_information(
variable.path,
"validators",
variable.validators,
variable.version,
)
if variable.path in self.objectspace.leaders and not variable.default:
values = self.get_examples_values(variable)
if values:
variable.default = list(values)
else:
variable.default = [CONVERT_OPTION[variable.type]['example']]
self.objectspace.informations.add(variable.path, 'fake_default', True)
variable.default = [CONVERT_OPTION[variable.type]["example"]]
self.objectspace.informations.add(variable.path, "fake_default", True)
self.objectspace.informations.add(
variable.path, "dictionaries", variable.xmlfiles
)
@ -126,58 +151,64 @@ class Annotator(Walk):
variable: dict,
) -> None:
"""convert properties"""
for prop in ['hidden', 'disabled', 'mandatory']:
for prop in ["hidden", "disabled", "mandatory"]:
prop_value = getattr(variable, prop, None)
if not prop_value:
continue
self.calculation_to_information(variable.path,
prop,
prop_value,
variable.version,
)
self.calculation_to_information(
variable.path,
prop,
prop_value,
variable.version,
)
def calculation_to_information(self,
path: str,
prop: str,
values,
version: str,
):
self._calculation_to_information(path,
prop,
values,
version,
)
def calculation_to_information(
self,
path: str,
prop: str,
values,
version: str,
):
self._calculation_to_information(
path,
prop,
values,
version,
)
if isinstance(values, list):
for idx, val in enumerate(values):
self._calculation_to_information(path,
prop,
val,
version,
suffix=f'_{idx}',
)
self._calculation_to_information(
path,
prop,
val,
version,
identifier=f"_{idx}",
)
def _calculation_to_information(self,
path: str,
prop: str,
values,
version: str,
*,
suffix: str='',
):
if not isinstance(values, Calculation):
def _calculation_to_information(
self,
path: str,
prop: str,
values,
version: str,
*,
identifier: str = "",
):
if not isinstance(values, Calculation):
return
values_calculation = True
if isinstance(values, JinjaCalculation):
if values.description:
values_calculation = values.description
values_calculation_type = 'jinja'
elif isinstance(values, VariableCalculation):
values_calculation_type = "jinja"
elif isinstance(values, (VariableCalculation, VariablePropertyCalculation)):
values_calculation = values.variable
paths = self.objectspace.paths
if version != '1.0' and paths.regexp_relative.search(values_calculation):
calculation_path = paths.get_relative_path(values_calculation,
path,
)
if version != "1.0" and paths.regexp_relative.search(values_calculation):
calculation_path = paths.get_full_path(
values_calculation,
path,
)
if prop in PROPERTY_ATTRIBUTE:
if values.when is not undefined:
values_calculation = f'when the variable "{calculation_path}" has the value "{values.when}"'
@ -187,18 +218,27 @@ class Annotator(Walk):
values_calculation = f'when the variable "{calculation_path}" has the value "True"'
else:
values_calculation = calculation_path
values_calculation_type = 'variable'
values_calculation_type = "variable"
elif isinstance(values, InformationCalculation):
values_calculation_type = 'information'
elif isinstance(values, SuffixCalculation):
values_calculation_type = 'suffix'
values_calculation_type = "information"
elif isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)):
if version != "1.0" and prop in PROPERTY_ATTRIBUTE:
if values.when is not undefined:
values_calculation = f'when the identifier is "{values.when}"'
elif values.when_not is not undefined:
values_calculation = (
f'when the identifier is not "{values.when_not}"'
)
values_calculation_type = "identifier"
elif isinstance(values, IndexCalculation):
values_calculation_type = 'index'
self.objectspace.informations.add(path,
f'{prop}_calculation_type{suffix}',
values_calculation_type,
)
self.objectspace.informations.add(path,
f'{prop}_calculation{suffix}',
values_calculation,
)
values_calculation_type = "index"
self.objectspace.informations.add(
path,
f"{prop}_calculation_type{identifier}",
values_calculation_type,
)
self.objectspace.informations.add(
path,
f"{prop}_calculation{identifier}",
values_calculation,
)

View file

@ -1,36 +0,0 @@
"""
Cli code for Rougail-output-doc
Silique (https://www.silique.fr)
Copyright (C) 2024
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from . import RougailOutputDoc
def run(rougailconfig,
config,
user_data,
):
inventory = RougailOutputDoc(config=config,
rougailconfig=rougailconfig,
)
print(inventory.gen_doc())
__all__ = ('run',)

View file

@ -4,43 +4,42 @@ Config file for Rougail-doc
Silique (https://www.silique.fr)
Copyright (C) 2024
distribued with GPL-2 or later license
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU 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 free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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 pathlib import Path
from rougail.utils import load_modules
# from .utils import _
OUTPUTS = None
def get_outputs() -> None:
module_name = 'rougail.doc.output'
module_name = "rougail.doc.output"
outputs = {}
for path in (Path(__file__).parent / 'output').iterdir():
for path in (Path(__file__).parent / "output").iterdir():
name = path.name
if not name.endswith(".py") or name.endswith("__.py"):
continue
module = load_modules(module_name + '.' + name, str(path))
module = load_modules(module_name + "." + name, str(path))
if "Formater" not in dir(module):
continue
level = module.Formater.level
if level in outputs:
raise Exception(f'duplicated level rougail-doc for output "{level}": {module.Formater.name} and {outputs[level].name}')
raise Exception(
f'duplicated level rougail-doc for output "{level}": {module.Formater.name} and {outputs[level].name}'
)
outputs[module.Formater.level] = module.Formater
return {outputs[level].name: outputs[level] for level in sorted(outputs)}
@ -59,9 +58,10 @@ class OutPuts: # pylint: disable=R0903
return OUTPUTS
def get_rougail_config(*,
backward_compatibility=True,
) -> dict:
def get_rougail_config(
*,
backward_compatibility=True,
) -> dict:
outputs = list(OutPuts().get())
output_format_default = outputs[0]
rougail_options = """
@ -77,20 +77,28 @@ doc:
description: Start title level
alternative_name: dt
default: 1
with_example:
description: Display example in documentation
negative_description: Hide example in documentation
alternative_name: de
default: false
output_format:
description: Generate document in format
alternative_name: do
default: output_format_default
choices:
""".replace('output_format_default', output_format_default)
""".replace(
"output_format_default", output_format_default
)
for output in outputs:
rougail_options += f" - {output}\n"
return {'name': 'doc',
'process': 'output',
'options': rougail_options,
'allow_user_data': False,
'level': 50,
}
return {
"name": "doc",
"process": "output",
"options": rougail_options,
"allow_user_data": False,
"level": 50,
}
__all__ = ("OutPuts", 'get_rougail_config')
__all__ = ("OutPuts", "get_rougail_config")

View file

@ -2,24 +2,16 @@
Silique (https://www.silique.fr)
Copyright (C) 2024
distribued with GPL-2 or later license
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU 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 free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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/>.
"""
def echo(msg):
return msg
_ = echo

View file

@ -1,3 +1,21 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2024
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 io import BytesIO
from typing import List
from itertools import chain
@ -5,7 +23,7 @@ from ruamel.yaml import YAML
class Formater:
name = 'asciidoc'
name = "asciidoc"
level = 40
def __init__(self):
@ -13,12 +31,13 @@ class Formater:
self._yaml.indent(mapping=2, sequence=4, offset=2)
def header(self):
return ''
return ""
def title(self,
title: str,
level: int,
) -> str:
def title(
self,
title: str,
level: int,
) -> str:
char = "="
return f"{char * (level + 1)} {title}\n\n"
@ -30,79 +49,91 @@ class Formater:
stable = table.split("\n", 1)
return stable[0].replace("<", "a") + "\n" + stable[1]
def link(self,
comment: str,
link: str,
) -> str:
def link(
self,
comment: str,
link: str,
) -> str:
return f"`{link}[{comment}]`"
def prop(self,
prop: str,
) -> str:
return f'`{prop}`'
def prop(
self,
prop: str,
) -> str:
return f"`{prop}`"
def list(self,
choices: list,
) -> str:
def list(
self,
choices: list,
) -> str:
prefix = "\n\n* "
char = "\n* "
return prefix + char.join([self.dump(choice) for choice in choices])
def is_list(self,
txt: str,
) -> str:
return txt.startswith('* ')
def is_list(
self,
txt: str,
) -> str:
return txt.startswith("* ")
def columns(self,
col1: List[str],
col2: List[str],
) -> None:
def columns(
self,
col1: List[str],
col2: List[str],
) -> None:
self.max_line = 0
for params in chain(col1, col2):
for param in params.split('\n'):
for param in params.split("\n"):
self.max_line = max(self.max_line, len(param))
self.max_line += 1
def join(self,
lst: List[str],
) -> str:
def join(
self,
lst: List[str],
) -> str:
string = ""
previous = ''
previous = ""
for line in lst:
if string:
if self.is_list(previous.split('\n')[-1]):
if self.is_list(previous.split("\n")[-1]):
string += "\n\n"
else:
string += " +\n"
string += line
previous = line
return "\n" + string
def to_string(self,
text: str,
) -> str:
def to_string(
self,
text: str,
) -> str:
return text
def table_header(self,
lst,
):
return lst[0] + " " * (self.max_line - len(lst[0])), lst[1] + " " * (self.max_line - len(lst[1]))
def table_header(
self,
lst,
):
return lst[0] + " " * (self.max_line - len(lst[0])), lst[1] + " " * (
self.max_line - len(lst[1])
)
def bold(self,
msg: str,
) -> str:
def bold(
self,
msg: str,
) -> str:
return f"**{msg}**"
def italic(self,
msg: str,
) -> str:
def italic(
self,
msg: str,
) -> str:
return f"_{msg}_"
def dump(self, dico):
with BytesIO() as ymlfh:
self._yaml.dump(dico, ymlfh)
ret = ymlfh.getvalue().decode('utf-8').strip()
if ret.endswith('...'):
ret = ymlfh.getvalue().decode("utf-8").strip()
if ret.endswith("..."):
ret = ret[:-3].strip()
return ret

View file

@ -1,3 +1,21 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2024
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 io import BytesIO
from typing import List
from itertools import chain
@ -5,7 +23,7 @@ from ruamel.yaml import YAML
class Formater:
name = 'github'
name = "github"
level = 50
def __init__(self):
@ -15,14 +33,15 @@ class Formater:
def header(self):
if self.header_setted:
return ''
return ""
self.header_setted = True
return "---\ngitea: none\ninclude_toc: true\n---\n"
def title(self,
title: str,
level: int,
) -> str:
def title(
self,
title: str,
level: int,
) -> str:
char = "#"
return f"{char * level} {title}\n\n"
@ -32,67 +51,77 @@ class Formater:
def table(self, table):
return table
def link(self,
comment: str,
link: str,
) -> str:
def link(
self,
comment: str,
link: str,
) -> str:
return f"[`{comment}`]({link})"
def prop(self,
prop: str,
) -> str:
return f'`{prop}`'
def prop(
self,
prop: str,
) -> str:
return f"`{prop}`"
def list(self,
choices,
):
def list(
self,
choices,
):
prefix = "<br/>- "
char = "<br/>- "
return prefix + char.join([self.dump(choice) for choice in choices])
def is_list(self,
txt: str,
) -> str:
return txt.startswith('* ')
def columns(self,
col1: List[str],
col2: List[str],
) -> None:
def is_list(
self,
txt: str,
) -> str:
return txt.startswith("* ")
def columns(
self,
col1: List[str],
col2: List[str],
) -> None:
self.max_line = 0
for params in chain(col1, col2):
for param in params.split('\n'):
for param in params.split("\n"):
self.max_line = max(self.max_line, len(param))
self.max_line += 1
def join(self,
lst: List[str],
) -> str:
def join(
self,
lst: List[str],
) -> str:
return "<br/>".join(lst)
def to_string(self,
text: str,
) -> str:
return text.strip().replace('\n', '<br/>')
def to_string(
self,
text: str,
) -> str:
return text.strip().replace("\n", "<br/>")
def table_header(self,
lst):
return lst[0] + "&nbsp;" * (self.max_line - len(lst[0])), lst[1] + "&nbsp;" * (self.max_line - len(lst[1]))
def table_header(self, lst):
return lst[0] + "&nbsp;" * (self.max_line - len(lst[0])), lst[1] + "&nbsp;" * (
self.max_line - len(lst[1])
)
def bold(self,
msg: str,
) -> str:
def bold(
self,
msg: str,
) -> str:
return f"**{msg}**"
def italic(self,
msg: str,
) -> str:
def italic(
self,
msg: str,
) -> str:
return f"*{msg}*"
def dump(self, dico):
with BytesIO() as ymlfh:
self._yaml.dump(dico, ymlfh)
ret = ymlfh.getvalue().decode('utf-8').strip()
if ret.endswith('...'):
ret = ymlfh.getvalue().decode("utf-8").strip()
if ret.endswith("..."):
ret = ret[:-3].strip()
return ret