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)
|
||||
|
||||
### Feat
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
|
|||
|
||||
[project]
|
||||
name = "rougail"
|
||||
version = "1.2.0a25"
|
||||
version = "1.2.0a26"
|
||||
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
|
||||
readme = "README.md"
|
||||
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 .object_model import CONVERT_OPTION
|
||||
from .user_datas import UserDatas
|
||||
from .tiramisu import tiramisu_display_name
|
||||
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):
|
||||
"""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 _
|
||||
|
||||
|
||||
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):
|
||||
"""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 .i18n import _
|
||||
from .error import DictConsistencyError, VariableCalculationDependencyError
|
||||
from .tiramisu import CONVERT_OPTION
|
||||
|
||||
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
|
||||
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():
|
||||
for typ, datas in CONVERT_OPTION.items():
|
||||
obj = getattr(tiramisu, datas["opttype"])
|
||||
|
|
|
|||
|
|
@ -32,10 +32,9 @@ from importlib.util import (
|
|||
spec_from_loader as _spec_from_loader,
|
||||
module_from_spec as _module_from_spec,
|
||||
)
|
||||
from unicodedata import normalize, combining
|
||||
from jinja2 import StrictUndefined, DictLoader
|
||||
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.error import (
|
||||
ValueWarning,
|
||||
|
|
@ -44,12 +43,97 @@ from tiramisu.error import (
|
|||
CancelParam,
|
||||
errors,
|
||||
)
|
||||
from .utils import normalize_family
|
||||
from .i18n import _
|
||||
|
||||
|
||||
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):
|
||||
try:
|
||||
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)
|
||||
|
||||
|
||||
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):
|
||||
values = calc_value(*args, **kwargs)
|
||||
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 unicodedata import normalize, combining
|
||||
import re
|
||||
from itertools import chain
|
||||
|
||||
|
|
@ -58,17 +57,6 @@ def valid_variable_family_name(
|
|||
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]:
|
||||
"""list all functions in a module"""
|
||||
loader = SourceFileLoader(name, module)
|
||||
|
|
|
|||
Loading…
Reference in a new issue