bump: version 1.2.0a25 → 1.2.0a26
This commit is contained in:
parent
9e06f1142e
commit
c7a66a034e
8 changed files with 144 additions and 131 deletions
|
|
@ -1,3 +1,9 @@
|
||||||
|
## 1.2.0a26 (2025-05-26)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- user_data better support for follower variable
|
||||||
|
|
||||||
## 1.2.0a25 (2025-05-14)
|
## 1.2.0a25 (2025-05-14)
|
||||||
|
|
||||||
### Feat
|
### Feat
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "rougail"
|
name = "rougail"
|
||||||
version = "1.2.0a25"
|
version = "1.2.0a26"
|
||||||
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
|
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
description = "A consistency handling system that was initially designed in the configuration management"
|
description = "A consistency handling system that was initially designed in the configuration management"
|
||||||
|
|
|
||||||
|
|
@ -33,44 +33,10 @@ from .config import RougailConfig
|
||||||
from .utils import normalize_family
|
from .utils import normalize_family
|
||||||
from .object_model import CONVERT_OPTION
|
from .object_model import CONVERT_OPTION
|
||||||
from .user_datas import UserDatas
|
from .user_datas import UserDatas
|
||||||
|
from .tiramisu import tiramisu_display_name
|
||||||
from .__version__ import __version__
|
from .__version__ import __version__
|
||||||
|
|
||||||
|
|
||||||
def tiramisu_display_name(
|
|
||||||
kls,
|
|
||||||
subconfig,
|
|
||||||
with_quote: bool = False,
|
|
||||||
) -> str:
|
|
||||||
"""Replace the Tiramisu display_name function to display path + description"""
|
|
||||||
config_bag = subconfig.config_bag
|
|
||||||
context = config_bag.context
|
|
||||||
values = context.get_values()
|
|
||||||
context_subconfig = context.get_root(config_bag)
|
|
||||||
doc = values.get_information(subconfig, "doc", None)
|
|
||||||
comment = doc if doc and doc != kls.impl_getname() else ""
|
|
||||||
if "{{ identifier }}" in comment and subconfig.identifiers:
|
|
||||||
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
|
|
||||||
path_in_description = values.get_information(
|
|
||||||
context_subconfig, "path_in_description", True
|
|
||||||
)
|
|
||||||
if path_in_description or not comment:
|
|
||||||
comment = f" ({comment})" if comment else ""
|
|
||||||
if path_in_description is False:
|
|
||||||
path = kls.impl_getname()
|
|
||||||
else:
|
|
||||||
path = kls.impl_getpath()
|
|
||||||
if "{{ identifier }}" in path and subconfig.identifiers:
|
|
||||||
path = path.replace(
|
|
||||||
"{{ identifier }}", normalize_family(str(subconfig.identifiers[-1]))
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
path = comment
|
|
||||||
comment = ""
|
|
||||||
if with_quote:
|
|
||||||
return f'"{path}"{comment}'
|
|
||||||
return f"{path}{comment}"
|
|
||||||
|
|
||||||
|
|
||||||
class Rougail(UserDatas):
|
class Rougail(UserDatas):
|
||||||
"""Main Rougail object"""
|
"""Main Rougail object"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
__version__ = "1.2.0a25"
|
__version__ = "1.2.0a26"
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
|
|
||||||
|
|
||||||
def display_xmlfiles(xmlfiles: list) -> str:
|
|
||||||
"""The function format xmlfiles informations to generate errors"""
|
|
||||||
if len(xmlfiles) == 1:
|
|
||||||
return '"' + xmlfiles[0] + '"'
|
|
||||||
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + " and " + '"' + xmlfiles[-1] + '"'
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(Exception):
|
class ConfigError(Exception):
|
||||||
"""Standard error for templating"""
|
"""Standard error for templating"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,82 +31,12 @@ from tiramisu.config import get_common_path
|
||||||
from .utils import get_jinja_variable_to_param, calc_multi_for_type_variable, undefined
|
from .utils import get_jinja_variable_to_param, calc_multi_for_type_variable, undefined
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .error import DictConsistencyError, VariableCalculationDependencyError
|
from .error import DictConsistencyError, VariableCalculationDependencyError
|
||||||
|
from .tiramisu import CONVERT_OPTION
|
||||||
|
|
||||||
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
|
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
|
||||||
PROPERTY_ATTRIBUTE = ["frozen", "hidden", "disabled", "mandatory"]
|
PROPERTY_ATTRIBUTE = ["frozen", "hidden", "disabled", "mandatory"]
|
||||||
|
|
||||||
|
|
||||||
def convert_boolean(value: str) -> bool:
|
|
||||||
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`"""
|
|
||||||
if isinstance(value, bool):
|
|
||||||
return value
|
|
||||||
value = value.lower()
|
|
||||||
if value == "true":
|
|
||||||
return True
|
|
||||||
elif value == "false":
|
|
||||||
return False
|
|
||||||
elif value in ["", None]:
|
|
||||||
return None
|
|
||||||
raise Exception(_('unknown boolean value "{0}"').format(value))
|
|
||||||
|
|
||||||
|
|
||||||
CONVERT_OPTION = {
|
|
||||||
"string": dict(opttype="StrOption", example="example"),
|
|
||||||
"number": dict(opttype="IntOption", func=int, example=42),
|
|
||||||
"float": dict(opttype="FloatOption", func=float, example=1.42),
|
|
||||||
"boolean": dict(opttype="BoolOption", func=convert_boolean),
|
|
||||||
"secret": dict(opttype="PasswordOption", example="secrets"),
|
|
||||||
"mail": dict(opttype="EmailOption", example="user@example.net"),
|
|
||||||
"unix_filename": dict(opttype="FilenameOption", example="/tmp/myfile.txt"),
|
|
||||||
"date": dict(opttype="DateOption", example="2000-01-01"),
|
|
||||||
"unix_user": dict(opttype="UsernameOption", example="username"),
|
|
||||||
"ip": dict(
|
|
||||||
opttype="IPOption", initkwargs={"allow_reserved": True}, example="1.1.1.1"
|
|
||||||
),
|
|
||||||
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}, example="1.1.1.0/24"),
|
|
||||||
"netmask": dict(opttype="NetmaskOption", example="255.255.255.0"),
|
|
||||||
"network": dict(opttype="NetworkOption", example="1.1.1.0"),
|
|
||||||
"network_cidr": dict(
|
|
||||||
opttype="NetworkOption", initkwargs={"cidr": True}, example="1.1.1.0/24"
|
|
||||||
),
|
|
||||||
"broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"),
|
|
||||||
"netbios": dict(
|
|
||||||
opttype="DomainnameOption",
|
|
||||||
initkwargs={"type": "netbios", "warnings_only": True},
|
|
||||||
example="example",
|
|
||||||
),
|
|
||||||
"domainname": dict(
|
|
||||||
opttype="DomainnameOption",
|
|
||||||
initkwargs={"type": "domainname", "allow_ip": False},
|
|
||||||
example="example.net",
|
|
||||||
),
|
|
||||||
"hostname": dict(
|
|
||||||
opttype="DomainnameOption",
|
|
||||||
initkwargs={"type": "hostname", "allow_ip": False},
|
|
||||||
example="example",
|
|
||||||
),
|
|
||||||
"web_address": dict(
|
|
||||||
opttype="URLOption",
|
|
||||||
initkwargs={"allow_ip": False, "allow_without_dot": True},
|
|
||||||
example="https://example.net",
|
|
||||||
),
|
|
||||||
"port": dict(
|
|
||||||
opttype="PortOption", initkwargs={"allow_private": True}, example="111"
|
|
||||||
),
|
|
||||||
"mac": dict(opttype="MACOption", example="00:00:00:00:00"),
|
|
||||||
"unix_permissions": dict(
|
|
||||||
opttype="PermissionsOption",
|
|
||||||
initkwargs={"warnings_only": True},
|
|
||||||
func=int,
|
|
||||||
example="644",
|
|
||||||
),
|
|
||||||
"choice": dict(opttype="ChoiceOption", example="a_choice"),
|
|
||||||
"regexp": dict(opttype="RegexpOption"),
|
|
||||||
#
|
|
||||||
"symlink": dict(opttype="SymLinkOption"),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_convert_option_types():
|
def get_convert_option_types():
|
||||||
for typ, datas in CONVERT_OPTION.items():
|
for typ, datas in CONVERT_OPTION.items():
|
||||||
obj = getattr(tiramisu, datas["opttype"])
|
obj = getattr(tiramisu, datas["opttype"])
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,9 @@ from importlib.util import (
|
||||||
spec_from_loader as _spec_from_loader,
|
spec_from_loader as _spec_from_loader,
|
||||||
module_from_spec as _module_from_spec,
|
module_from_spec as _module_from_spec,
|
||||||
)
|
)
|
||||||
|
from unicodedata import normalize, combining
|
||||||
from jinja2 import StrictUndefined, DictLoader
|
from jinja2 import StrictUndefined, DictLoader
|
||||||
from jinja2.sandbox import SandboxedEnvironment
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
from rougail.object_model import CONVERT_OPTION
|
|
||||||
from rougail.error import display_xmlfiles
|
|
||||||
from tiramisu import DynOptionDescription, calc_value, function_waiting_for_error
|
from tiramisu import DynOptionDescription, calc_value, function_waiting_for_error
|
||||||
from tiramisu.error import (
|
from tiramisu.error import (
|
||||||
ValueWarning,
|
ValueWarning,
|
||||||
|
|
@ -44,12 +43,97 @@ from tiramisu.error import (
|
||||||
CancelParam,
|
CancelParam,
|
||||||
errors,
|
errors,
|
||||||
)
|
)
|
||||||
from .utils import normalize_family
|
|
||||||
from .i18n import _
|
|
||||||
|
|
||||||
ori_raise_carry_out_calculation_error = errors.raise_carry_out_calculation_error
|
ori_raise_carry_out_calculation_error = errors.raise_carry_out_calculation_error
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .i18n import _
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
# FIXME
|
||||||
|
def _(msg):
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def display_xmlfiles(xmlfiles: list) -> str:
|
||||||
|
"""The function format xmlfiles informations to generate errors"""
|
||||||
|
if len(xmlfiles) == 1:
|
||||||
|
return '"' + xmlfiles[0] + '"'
|
||||||
|
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + " and " + '"' + xmlfiles[-1] + '"'
|
||||||
|
|
||||||
|
|
||||||
|
def convert_boolean(value: str) -> bool:
|
||||||
|
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`"""
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
value = value.lower()
|
||||||
|
if value == "true":
|
||||||
|
return True
|
||||||
|
elif value == "false":
|
||||||
|
return False
|
||||||
|
elif value in ["", None]:
|
||||||
|
return None
|
||||||
|
raise Exception(_('unknown boolean value "{0}"').format(value))
|
||||||
|
|
||||||
|
|
||||||
|
CONVERT_OPTION = {
|
||||||
|
"string": dict(opttype="StrOption", example="example"),
|
||||||
|
"number": dict(opttype="IntOption", func=int, example=42),
|
||||||
|
"float": dict(opttype="FloatOption", func=float, example=1.42),
|
||||||
|
"boolean": dict(opttype="BoolOption", func=convert_boolean),
|
||||||
|
"secret": dict(opttype="PasswordOption", example="secrets"),
|
||||||
|
"mail": dict(opttype="EmailOption", example="user@example.net"),
|
||||||
|
"unix_filename": dict(opttype="FilenameOption", example="/tmp/myfile.txt"),
|
||||||
|
"date": dict(opttype="DateOption", example="2000-01-01"),
|
||||||
|
"unix_user": dict(opttype="UsernameOption", example="username"),
|
||||||
|
"ip": dict(
|
||||||
|
opttype="IPOption", initkwargs={"allow_reserved": True}, example="1.1.1.1"
|
||||||
|
),
|
||||||
|
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}, example="1.1.1.0/24"),
|
||||||
|
"netmask": dict(opttype="NetmaskOption", example="255.255.255.0"),
|
||||||
|
"network": dict(opttype="NetworkOption", example="1.1.1.0"),
|
||||||
|
"network_cidr": dict(
|
||||||
|
opttype="NetworkOption", initkwargs={"cidr": True}, example="1.1.1.0/24"
|
||||||
|
),
|
||||||
|
"broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"),
|
||||||
|
"netbios": dict(
|
||||||
|
opttype="DomainnameOption",
|
||||||
|
initkwargs={"type": "netbios", "warnings_only": True},
|
||||||
|
example="example",
|
||||||
|
),
|
||||||
|
"domainname": dict(
|
||||||
|
opttype="DomainnameOption",
|
||||||
|
initkwargs={"type": "domainname", "allow_ip": False},
|
||||||
|
example="example.net",
|
||||||
|
),
|
||||||
|
"hostname": dict(
|
||||||
|
opttype="DomainnameOption",
|
||||||
|
initkwargs={"type": "hostname", "allow_ip": False},
|
||||||
|
example="example",
|
||||||
|
),
|
||||||
|
"web_address": dict(
|
||||||
|
opttype="URLOption",
|
||||||
|
initkwargs={"allow_ip": False, "allow_without_dot": True},
|
||||||
|
example="https://example.net",
|
||||||
|
),
|
||||||
|
"port": dict(
|
||||||
|
opttype="PortOption", initkwargs={"allow_private": True}, example="111"
|
||||||
|
),
|
||||||
|
"mac": dict(opttype="MACOption", example="00:00:00:00:00"),
|
||||||
|
"unix_permissions": dict(
|
||||||
|
opttype="PermissionsOption",
|
||||||
|
initkwargs={"warnings_only": True},
|
||||||
|
func=int,
|
||||||
|
example="644",
|
||||||
|
),
|
||||||
|
"choice": dict(opttype="ChoiceOption", example="a_choice"),
|
||||||
|
"regexp": dict(opttype="RegexpOption"),
|
||||||
|
#
|
||||||
|
"symlink": dict(opttype="SymLinkOption"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def raise_carry_out_calculation_error(subconfig, *args, **kwargs):
|
def raise_carry_out_calculation_error(subconfig, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
ori_raise_carry_out_calculation_error(subconfig, *args, **kwargs)
|
ori_raise_carry_out_calculation_error(subconfig, *args, **kwargs)
|
||||||
|
|
@ -122,6 +206,52 @@ def load_functions(path, dict_func=None):
|
||||||
dict_func[function] = getattr(func_, function)
|
dict_func[function] = getattr(func_, function)
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_family(family_name: str) -> str:
|
||||||
|
"""replace space, accent, uppercase, ... by valid character"""
|
||||||
|
if not family_name:
|
||||||
|
return
|
||||||
|
family_name = family_name.lower()
|
||||||
|
family_name = family_name.replace("-", "_").replace(" ", "_").replace(".", "_")
|
||||||
|
nfkd_form = normalize("NFKD", family_name)
|
||||||
|
family_name = "".join([c for c in nfkd_form if not combining(c)])
|
||||||
|
return family_name.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def tiramisu_display_name(
|
||||||
|
kls,
|
||||||
|
subconfig,
|
||||||
|
with_quote: bool = False,
|
||||||
|
) -> str:
|
||||||
|
"""Replace the Tiramisu display_name function to display path + description"""
|
||||||
|
config_bag = subconfig.config_bag
|
||||||
|
context = config_bag.context
|
||||||
|
values = context.get_values()
|
||||||
|
context_subconfig = context.get_root(config_bag)
|
||||||
|
doc = values.get_information(subconfig, "doc", None)
|
||||||
|
comment = doc if doc and doc != kls.impl_getname() else ""
|
||||||
|
if "{{ identifier }}" in comment and subconfig.identifiers:
|
||||||
|
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
|
||||||
|
path_in_description = values.get_information(
|
||||||
|
context_subconfig, "path_in_description", True
|
||||||
|
)
|
||||||
|
if path_in_description or not comment:
|
||||||
|
comment = f" ({comment})" if comment else ""
|
||||||
|
if path_in_description is False:
|
||||||
|
path = kls.impl_getname()
|
||||||
|
else:
|
||||||
|
path = kls.impl_getpath()
|
||||||
|
if "{{ identifier }}" in path and subconfig.identifiers:
|
||||||
|
path = path.replace(
|
||||||
|
"{{ identifier }}", normalize_family(str(subconfig.identifiers[-1]))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
path = comment
|
||||||
|
comment = ""
|
||||||
|
if with_quote:
|
||||||
|
return f'"{path}"{comment}'
|
||||||
|
return f"{path}{comment}"
|
||||||
|
|
||||||
|
|
||||||
def rougail_calc_value(*args, __default_value=None, __internal_multi=False, **kwargs):
|
def rougail_calc_value(*args, __default_value=None, __internal_multi=False, **kwargs):
|
||||||
values = calc_value(*args, **kwargs)
|
values = calc_value(*args, **kwargs)
|
||||||
if values is None and __internal_multi:
|
if values is None and __internal_multi:
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
from unicodedata import normalize, combining
|
|
||||||
import re
|
import re
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
|
@ -58,17 +57,6 @@ def valid_variable_family_name(
|
||||||
raise DictConsistencyError(msg, 76, xmlfiles)
|
raise DictConsistencyError(msg, 76, xmlfiles)
|
||||||
|
|
||||||
|
|
||||||
def normalize_family(family_name: str) -> str:
|
|
||||||
"""replace space, accent, uppercase, ... by valid character"""
|
|
||||||
if not family_name:
|
|
||||||
return
|
|
||||||
family_name = family_name.lower()
|
|
||||||
family_name = family_name.replace("-", "_").replace(" ", "_").replace(".", "_")
|
|
||||||
nfkd_form = normalize("NFKD", family_name)
|
|
||||||
family_name = "".join([c for c in nfkd_form if not combining(c)])
|
|
||||||
return family_name.lower()
|
|
||||||
|
|
||||||
|
|
||||||
def load_modules(name, module) -> List[str]:
|
def load_modules(name, module) -> List[str]:
|
||||||
"""list all functions in a module"""
|
"""list all functions in a module"""
|
||||||
loader = SourceFileLoader(name, module)
|
loader = SourceFileLoader(name, module)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue