fix: translation + black

This commit is contained in:
egarette@silique.fr 2024-10-31 08:53:58 +01:00
parent b5d477a439
commit 5e66d2074d
50 changed files with 8121 additions and 6694 deletions

1
.gitignore vendored
View file

@ -1,6 +1,5 @@
*~
*#
*.pyc
*.mo
*.swp
build/

View file

@ -1,8 +0,0 @@
# Include the README
include *.rst
# Include the license file
include LICENSE.txt
# Include the data files
recursive-include tiramisu *.py *.mo

View file

@ -1,13 +1,25 @@
![Logo Tiramisu](logo.png "logo Tiramisu")
An options controller tool
-------------------------------------
Due to more and more available options required to set up an operating system,
compiler options or whatever, it became quite annoying to hand the necessary
options to where they are actually used and even more annoying to add new
options. To circumvent these problems the configuration control was
introduced...
Tiramisu is an options handler and an options controller, wich aims at
producing flexible and fast options access.
[Documentations](doc/README.md)
# LICENSES
See COPYING for the licences of the code and the documentation.
See [COPYING](COPYING) for the licences of the code and the documentation.
See AUTHORS for the details about the tiramisu's team.
See [AUTHORS](AUTHORS) for the details about the tiramisu's team.

File diff suppressed because it is too large Load diff

811
locale/tiramisu.pot Normal file
View file

@ -0,0 +1,811 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-10-30 13:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: tiramisu/api.py:79
msgid "Settings:"
msgstr ""
#: tiramisu/api.py:83
msgid "Access to option without verifying permissive properties"
msgstr ""
#: tiramisu/api.py:88
msgid "Access to option without property restriction"
msgstr ""
#: tiramisu/api.py:93
msgid "Do not warnings during validation"
msgstr ""
#: tiramisu/api.py:97
msgid "Commands:"
msgstr ""
#: tiramisu/api.py:111 tiramisu/api.py:1840
msgid "please specify a valid sub function ({0}.{1})"
msgstr ""
#: tiramisu/api.py:194
msgid "please do not specify index ({0}.{1})"
msgstr ""
#: tiramisu/api.py:199 tiramisu/api.py:844
msgid "please specify index with a follower option ({0}.{1})"
msgstr ""
#: tiramisu/api.py:220
msgid "please specify a valid sub function ({0}.{1}): {2}"
msgstr ""
#: tiramisu/api.py:431
msgid "the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True"
msgstr ""
#: tiramisu/api.py:517
msgid "cannot get option from a follower symlink without index"
msgstr ""
#: tiramisu/api.py:592
msgid "cannot add this property: \"{0}\""
msgstr ""
#: tiramisu/api.py:619
msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\""
msgstr ""
#: tiramisu/api.py:623
msgid "cannot find \"{0}\" in option \"{1}\""
msgstr ""
#: tiramisu/api.py:628
msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\" at index \"{2}\""
msgstr ""
#: tiramisu/api.py:632
msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\""
msgstr ""
#: tiramisu/api.py:676
msgid "cannot find \"{0}\""
msgstr ""
#: tiramisu/api.py:808
msgid "cannot reduce length of the leader {}"
msgstr ""
#: tiramisu/api.py:861
msgid "only multi value has defaultmulti"
msgstr ""
#: tiramisu/api.py:1020
msgid "please specify a valid sub function ({0}.{1}) for {2}"
msgstr ""
#: tiramisu/api.py:1407
msgid "properties must be a frozenset"
msgstr ""
#: tiramisu/api.py:1411 tiramisu/api.py:1438
msgid "unknown when {} (must be in append or remove)"
msgstr ""
#: tiramisu/api.py:1424 tiramisu/api.py:1448 tiramisu/config.py:1680
msgid "unknown type {}"
msgstr ""
#: tiramisu/api.py:1812
msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr ""
#: tiramisu/autolib.py:80
msgid "args in params must be a tuple"
msgstr ""
#: tiramisu/autolib.py:83 tiramisu/autolib.py:88
msgid "arg in params must be a Param"
msgstr ""
#: tiramisu/autolib.py:85
msgid "kwargs in params must be a dict"
msgstr ""
#: tiramisu/autolib.py:113
msgid "paramoption needs an option not {}"
msgstr ""
#: tiramisu/autolib.py:119
msgid "param must have a boolean not a {} for notraisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:122
msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:212
msgid "option in ParamInformation cannot be a symlinkoption"
msgstr ""
#: tiramisu/autolib.py:215
msgid "option in ParamInformation cannot be a follower"
msgstr ""
#: tiramisu/autolib.py:218
msgid "option in ParamInformation cannot be a dynamic option"
msgstr ""
#: tiramisu/autolib.py:279
msgid "first argument ({0}) must be a function"
msgstr ""
#: tiramisu/autolib.py:283
msgid "help_function ({0}) must be a function"
msgstr ""
#: tiramisu/autolib.py:452 tiramisu/autolib.py:514
msgid "unable to carry out a calculation for {}, {}"
msgstr ""
#: tiramisu/autolib.py:461 tiramisu/autolib.py:521
msgid "the option {0} is used in a calculation but is invalid ({1})"
msgstr ""
#: tiramisu/autolib.py:477 tiramisu/autolib.py:535 tiramisu/autolib.py:584
msgid "unable to get value for calculating {0}, {1}"
msgstr ""
#: tiramisu/autolib.py:601
msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription"
msgstr ""
#: tiramisu/autolib.py:848
msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option {}"
msgstr ""
#: tiramisu/autolib.py:863
msgid "the \"{}\" function must not return a list (\"{}\") for the follower option {}"
msgstr ""
#: tiramisu/autolib.py:904
msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option {2}"
msgstr ""
#: tiramisu/autolib.py:915
msgid "unexpected error \"{0}\" in function \"{1}\" for option {2}"
msgstr ""
#: tiramisu/config.py:419
msgid "index \"{0}\" is greater than the leadership length \"{1}\" for option {2}"
msgstr ""
#: tiramisu/config.py:579
msgid "there is no option description for this config (may be GroupConfig)"
msgstr ""
#: tiramisu/config.py:668
msgid "no option found in config with these criteria"
msgstr ""
#: tiramisu/config.py:871
msgid "the follower option {0} has greater length ({1}) than the leader length ({2})"
msgstr ""
#: tiramisu/config.py:982 tiramisu/option/optiondescription.py:74
msgid "option description seems to be part of an other config"
msgstr ""
#: tiramisu/config.py:1144
msgid "parent of {0} not already exists"
msgstr ""
#: tiramisu/config.py:1191
msgid "cannot set leadership object has root optiondescription"
msgstr ""
#: tiramisu/config.py:1194
msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr ""
#: tiramisu/config.py:1246
msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr ""
#: tiramisu/config.py:1457
msgid "unknown config \"{}\""
msgstr ""
#: tiramisu/config.py:1482
msgid "child must be a Config, MixConfig or MetaConfig"
msgstr ""
#: tiramisu/config.py:1517
msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config"
msgstr ""
#: tiramisu/config.py:1527
msgid "force_default and force_dont_change_value cannot be set together"
msgstr ""
#: tiramisu/config.py:1676
msgid "config name must be uniq in groupconfig for {0}"
msgstr ""
#: tiramisu/config.py:1721
msgid "config added has no name, the name is mandatory"
msgstr ""
#: tiramisu/config.py:1726
msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr ""
#: tiramisu/config.py:1744 tiramisu/config.py:1750
msgid "cannot find the config {0}"
msgstr ""
#: tiramisu/config.py:1776
msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
#: tiramisu/config.py:1788
msgid "child must be a Config or MetaConfig"
msgstr ""
#: tiramisu/config.py:1793
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/config.py:1810
msgid "metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/error.py:31
msgid "and"
msgstr ""
#: tiramisu/error.py:33
msgid "or"
msgstr ""
#: tiramisu/error.py:55
msgid " {} "
msgstr ""
#: tiramisu/error.py:108
msgid "property"
msgstr ""
#: tiramisu/error.py:110
msgid "properties"
msgstr ""
#: tiramisu/error.py:113
msgid "cannot modify the {0} {1} because \"{2}\" has {3} {4}"
msgstr ""
#: tiramisu/error.py:115
msgid "cannot modify the {0} {1} because has {2} {3}"
msgstr ""
#: tiramisu/error.py:118
msgid "cannot access to {0} {1} because \"{2}\" has {3} {4}"
msgstr ""
#: tiramisu/error.py:120
msgid "cannot access to {0} {1} because has {2} {3}"
msgstr ""
#: tiramisu/error.py:192
msgid "invalid value"
msgstr ""
#: tiramisu/error.py:201
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/error.py:219 tiramisu/error.py:228
msgid "\"{0}\" is an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/function.py:65
msgid "network \"{0}\" ({1}) does not match with this netmask"
msgstr ""
#: tiramisu/function.py:83
msgid "IP \"{0}\" ({1}) with this netmask is in fact a network address"
msgstr ""
#: tiramisu/function.py:88
msgid "IP \"{0}\" ({1}) with this netmask is in fact a broadcast address"
msgstr ""
#: tiramisu/function.py:106
msgid "broadcast invalid with network {0} ({1}) and netmask {2} ({3})"
msgstr ""
#: tiramisu/function.py:134
msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]})"
msgstr ""
#: tiramisu/function.py:136
msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]}) with netmask {netmask[\"value\"]} ({netmask[\"name\"]})"
msgstr ""
#: tiramisu/function.py:143
msgid "this IP with the network {0} ({1}) is in fact a network address"
msgstr ""
#: tiramisu/function.py:148
msgid "this IP with the network {0} ({1}) is in fact a broadcast address"
msgstr ""
#: tiramisu/function.py:165
msgid "value is identical to {0}"
msgstr ""
#: tiramisu/function.py:400
msgid "unexpected value in calc_value with join attribute \"{0}\" with invalid length \"{1}\""
msgstr ""
#: tiramisu/function.py:527
msgid "unexpected {0} condition_operator in calc_value"
msgstr ""
#: tiramisu/function.py:591
msgid "unexpected condition_{0} must have \"todict\" argument"
msgstr ""
#: tiramisu/function.py:602
msgid "the value of \"{0}\" is {1}"
msgstr ""
#: tiramisu/function.py:604
msgid "the value of \"{0}\" is not {1}"
msgstr ""
#: tiramisu/option/baseoption.py:75 tiramisu/option/symlinkoption.py:44
msgid "\"{0}\" is an invalid name for an option"
msgstr ""
#: tiramisu/option/baseoption.py:88
msgid "invalid properties type {0} for {1}, must be a frozenset"
msgstr ""
#: tiramisu/option/baseoption.py:98
msgid "invalid property type {0} for {1}, must be a string or a Calculation"
msgstr ""
#: tiramisu/option/baseoption.py:249
msgid "information's item for {0} not found: \"{1}\""
msgstr ""
#: tiramisu/option/baseoption.py:267
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr ""
#: tiramisu/option/baseoption.py:308
msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr ""
#: tiramisu/option/baseoption.py:320
msgid "{0} not part of any Config"
msgstr ""
#: tiramisu/option/broadcastoption.py:41
msgid "invalid string"
msgstr ""
#: tiramisu/option/choiceoption.py:47
msgid "values must be a tuple or a calculation for {0}"
msgstr ""
#: tiramisu/option/choiceoption.py:70
msgid "the calculated values \"{0}\" for \"{1}\" is not a list"
msgstr ""
#: tiramisu/option/choiceoption.py:101
msgid "only \"{0}\" is allowed"
msgstr ""
#: tiramisu/option/choiceoption.py:103
msgid "only {0} are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:60
msgid "unknown type {0} for hostname"
msgstr ""
#: tiramisu/option/domainnameoption.py:63
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:65
msgid "allow_cidr_network must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:67
msgid "allow_without_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:69
msgid "allow_startswith_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:81
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:84
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded"
msgstr ""
#: tiramisu/option/domainnameoption.py:88
#: tiramisu/option/domainnameoption.py:89
msgid "could be a IP, otherwise {}"
msgstr ""
#: tiramisu/option/domainnameoption.py:134
msgid "invalid length (min 1)"
msgstr ""
#: tiramisu/option/domainnameoption.py:137
msgid "invalid length (max {0})"
msgstr ""
#: tiramisu/option/domainnameoption.py:143
msgid "must have dot"
msgstr ""
#: tiramisu/option/domainnameoption.py:145
msgid "invalid length (max 255)"
msgstr ""
#: tiramisu/option/domainnameoption.py:163
msgid "must not be an IP"
msgstr ""
#: tiramisu/option/domainnameoption.py:186
msgid "some characters are uppercase"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:131
msgid "DynOptionDescription identifiers for option {0}, is not a list ({1})"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:142
msgid "invalid identifier \"{}\" for option {}"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:150
msgid "DynOptionDescription \"{0}\" identifiers return a list with same values \"{1}\""
msgstr ""
#: tiramisu/option/filenameoption.py:47
msgid "types parameter must be a list, not \"{0}\" for \"{1}\""
msgstr ""
#: tiramisu/option/filenameoption.py:67
msgid "must starts with \"/\""
msgstr ""
#: tiramisu/option/filenameoption.py:78
msgid "cannot find {0} \"{1}\""
msgstr ""
#: tiramisu/option/intoption.py:52
msgid "value should be equal or greater than \"{0}\""
msgstr ""
#: tiramisu/option/intoption.py:54
msgid "value must be equal or greater than \"{0}\""
msgstr ""
#: tiramisu/option/intoption.py:59
msgid "value should be less than \"{0}\""
msgstr ""
#: tiramisu/option/intoption.py:61
msgid "value must be less than \"{0}\""
msgstr ""
#: tiramisu/option/ipoption.py:57
msgid "it's in fact a network address"
msgstr ""
#: tiramisu/option/ipoption.py:59
msgid "it's in fact a broacast address"
msgstr ""
#: tiramisu/option/ipoption.py:71
msgid "CIDR address must have a \"/\""
msgstr ""
#: tiramisu/option/ipoption.py:80
msgid "shouldn't be reserved IP"
msgstr ""
#: tiramisu/option/ipoption.py:82
msgid "mustn't be reserved IP"
msgstr ""
#: tiramisu/option/ipoption.py:86
msgid "should be private IP"
msgstr ""
#: tiramisu/option/ipoption.py:88
msgid "must be private IP"
msgstr ""
#: tiramisu/option/leadership.py:55
msgid "cannot set \"group_type\" attribute for a Leadership"
msgstr ""
#: tiramisu/option/leadership.py:67
msgid "a leader and a follower are mandatories in leadership \"{}\""
msgstr ""
#: tiramisu/option/leadership.py:89
msgid "leader cannot have \"{}\" property"
msgstr ""
#: tiramisu/option/leadership.py:101
msgid "leadership {0} shall not have a symlinkoption"
msgstr ""
#: tiramisu/option/leadership.py:108
msgid "leadership {0} shall not have a subgroup"
msgstr ""
#: tiramisu/option/leadership.py:114
msgid "only multi option allowed in leadership {0} but option {1} is not a multi"
msgstr ""
#: tiramisu/option/leadership.py:141
msgid "not allowed default value for follower option {0} in leadership {1}"
msgstr ""
#: tiramisu/option/networkoption.py:45
msgid "must use CIDR notation"
msgstr ""
#: tiramisu/option/networkoption.py:60
msgid "shouldn't be reserved network"
msgstr ""
#: tiramisu/option/networkoption.py:62
msgid "mustn't be reserved network"
msgstr ""
#: tiramisu/option/option.py:73
msgid "default_multi is set whereas multi is False in option: {0}"
msgstr ""
#: tiramisu/option/option.py:93
msgid "invalid multi type \"{}\" for \"{}\""
msgstr ""
#: tiramisu/option/option.py:112
msgid "validators must be a list of Calculation for \"{0}\""
msgstr ""
#: tiramisu/option/option.py:117
msgid "validators must be a Calculation for \"{0}\""
msgstr ""
#: tiramisu/option/option.py:146
msgid "invalid default_multi value \"{0}\" for option {1}"
msgstr ""
#: tiramisu/option/option.py:154
msgid "invalid default_multi value \"{0}\" for option {1}, {2}"
msgstr ""
#: tiramisu/option/option.py:167
msgid "invalid default_multi value \"{0}\" for option {1}, must be a list for a submulti"
msgstr ""
#: tiramisu/option/option.py:290
msgid "the value \"{}\" is not unique"
msgstr ""
#: tiramisu/option/option.py:352
msgid "which must not be a list"
msgstr ""
#: tiramisu/option/option.py:404 tiramisu/option/option.py:430
msgid "which must be a list"
msgstr ""
#: tiramisu/option/option.py:424
msgid "which \"{}\" must be a list of list"
msgstr ""
#: tiramisu/option/optiondescription.py:109
msgid "duplicate option: {0}"
msgstr ""
#: tiramisu/option/optiondescription.py:244
msgid "unknown option \"{0}\" in root optiondescription (it's a dynamic option)"
msgstr ""
#: tiramisu/option/optiondescription.py:279
msgid "unknown option \"{0}\" in root optiondescription"
msgstr ""
#: tiramisu/option/optiondescription.py:282
msgid "unknown option \"{0}\" in optiondescription {1}"
msgstr ""
#: tiramisu/option/optiondescription.py:338
msgid "children in optiondescription \"{}\" must be a list"
msgstr ""
#: tiramisu/option/optiondescription.py:366
msgid "duplicate option name: \"{0}\""
msgstr ""
#: tiramisu/option/optiondescription.py:372
msgid "the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\""
msgstr ""
#: tiramisu/option/optiondescription.py:415
msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr ""
#: tiramisu/option/optiondescription.py:420
msgid "group_type: {0} not allowed"
msgstr ""
#: tiramisu/option/permissionsoption.py:52
msgid "only 3 or 4 octal digits are allowed"
msgstr ""
#: tiramisu/option/permissionsoption.py:63
msgid "user"
msgstr ""
#: tiramisu/option/permissionsoption.py:64
#: tiramisu/option/permissionsoption.py:66
msgid "group"
msgstr ""
#: tiramisu/option/permissionsoption.py:67
msgid "other"
msgstr ""
#: tiramisu/option/permissionsoption.py:68
msgid "{0} has more right than {1}"
msgstr ""
#: tiramisu/option/permissionsoption.py:71
msgid "too weak"
msgstr ""
#: tiramisu/option/portoption.py:74
msgid "inconsistency in allowed range"
msgstr ""
#: tiramisu/option/portoption.py:79
msgid "max value is empty"
msgstr ""
#: tiramisu/option/portoption.py:92
msgid "range must have two values only"
msgstr ""
#: tiramisu/option/portoption.py:95
msgid "first port in range must be smaller than the second one"
msgstr ""
#: tiramisu/option/portoption.py:121
msgid "should be between {0} and {1}"
msgstr ""
#: tiramisu/option/portoption.py:123
msgid "must be between {0} and {1}"
msgstr ""
#: tiramisu/option/symlinkoption.py:51
msgid "malformed symlink second parameters must be an option for \"{0}\", not {1}"
msgstr ""
#: tiramisu/option/urloption.py:91
msgid "must start with http:// or https://"
msgstr ""
#: tiramisu/option/urloption.py:119
msgid "must ends with a valid resource name"
msgstr ""
#: tiramisu/setting.py:255
msgid "can't rebind {0}"
msgstr ""
#: tiramisu/setting.py:262
msgid "can't unbind {0}"
msgstr ""
#: tiramisu/setting.py:464
msgid "invalid property type {type(new_prop)} for {subconfig.option.impl_getname()} with {prop.function.__name__} function"
msgstr ""
#: tiramisu/setting.py:476
msgid "leader cannot have \"{new_prop}\" property"
msgstr ""
#: tiramisu/setting.py:564
msgid "leader cannot have \"{0}\" property"
msgstr ""
#: tiramisu/setting.py:573
msgid "a leader ({0}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\""
msgstr ""
#: tiramisu/setting.py:607
msgid "permissive must be a frozenset"
msgstr ""
#: tiramisu/setting.py:617
msgid "cannot add those permissives: {0}"
msgstr ""
#: tiramisu/setting.py:654
msgid "can't reset properties to the symlinkoption \"{}\""
msgstr ""
#: tiramisu/setting.py:667
msgid "can't reset permissives to the symlinkoption \"{}\""
msgstr ""
#: tiramisu/todict.py:395
msgid "option {} only works when remotable is not \"none\""
msgstr ""
#: tiramisu/todict.py:561
msgid "unable to transform tiramisu object to dict: {}"
msgstr ""
#: tiramisu/todict.py:876 tiramisu/todict.py:1033
msgid "unknown form {}"
msgstr ""
#: tiramisu/todict.py:923
msgid "not in current area"
msgstr ""
#: tiramisu/todict.py:947
msgid "only multi option can have action \"add\", but \"{}\" is not a multi"
msgstr ""
#: tiramisu/todict.py:953
msgid "unknown action {}"
msgstr ""
#: tiramisu/value.py:564 tiramisu/value.py:861
msgid "set owner \"{0}\" is forbidden"
msgstr ""
#: tiramisu/value.py:571
msgid "\"{0}\" is a default value, so we cannot change owner to \"{1}\""
msgstr ""
#: tiramisu/value.py:740
msgid "index {index} is greater than the length {length} for option {subconfig.option.impl_get_display_name(with_quote=True)}"
msgstr ""
#: tiramisu/value.py:847
msgid "information's item not found \"{}\""
msgstr ""

35
pyproject.toml Normal file
View file

@ -0,0 +1,35 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "tiramisu"
version = "4.1.0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md"
description = "an options controller tool"
requires-python = ">=3.8"
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",
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/tiramisu"
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
update_changelog_on_bump = true

View file

@ -1,52 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import setup
import os
from tiramisu import __version__
ORI_PACKAGE_NAME = 'tiramisu'
PACKAGE_NAME = os.environ.get('PACKAGE_DST', ORI_PACKAGE_NAME)
if PACKAGE_NAME != ORI_PACKAGE_NAME:
package_dir = {PACKAGE_NAME: ORI_PACKAGE_NAME}
else:
package_dir = None
setup(
version=__version__,
author="Tiramisu's team",
author_email='gnunux@gnunux.info',
name=PACKAGE_NAME,
description='an options controller tool',
url='https://forge.cloud.silique.fr/stove/tiramisu/',
license='GNU Library or Lesser General Public License (LGPL)',
provides=['tiramisu_api'],
install_requires=['setuptools'],
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
],
long_description="""\
An options controller tool
-------------------------------------
Due to more and more available options required to set up an operating system,
compiler options or whatever, it became quite annoying to hand the necessary
options to where they are actually used and even more annoying to add new
options. To circumvent these problems the configuration control was
introduced...
Tiramisu is an options handler and an options controller, wich aims at
producing flexible and fast options access.
This version requires Python 3.5 or later.
""",
include_package_data=True,
package_dir=package_dir,
packages=[PACKAGE_NAME],
)

View file

@ -86,8 +86,6 @@ def build_variables(*,
if hidden:
base_name += 'hidden_'
#str_mandatory_multi_hidden_information_deps
if base_name + 'information_deps' == 'str_mandatory_multi_hidden_information_deps':
print('blah', multi, properties, str1_5_informations)
str1 = StrOption(base_name + 'information_deps',
'',
multi=multi,
@ -465,7 +463,6 @@ def _test_option(option, without_index=False):
assert isinstance(option.path(), str) and (option.path() == name or option.path().endswith(f'.{name}'))
if '_deps' in name:
assert option.has_dependency(False)
print(option.path())
assert option.dependencies()
else:
assert not option.has_dependency(False)

View file

@ -1046,12 +1046,12 @@ def test_validator_error_prefix():
try:
cfg.option('b').value.set(1)
except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('1', _('integer'), 'b') + ', ' + _('value is identical to {}').format('"a"')
assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('1', _('integer'), 'b') + ', ' + _('value is identical to {0}').format('"a"')
try:
cfg.option('b').value.set(1)
except Exception as err:
err.prefix = ''
assert str(err) == _('value is identical to {}').format('"a"')
assert str(err) == _('value is identical to {0}').format('"a"')
# assert not list_sessions()

View file

@ -14,11 +14,29 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Configuration management library written in python
"""
from .function import calc_value, calc_value_property_help, valid_ip_netmask, \
valid_network_netmask, valid_in_network, valid_broadcast, \
valid_not_equal, function_waiting_for_dict, function_waiting_for_error
from .autolib import Calculation, Params, ParamOption, ParamDynOption, ParamSelfOption, \
ParamValue, ParamIndex, ParamIdentifier, ParamInformation, ParamSelfInformation
from .function import (
calc_value,
calc_value_property_help,
valid_ip_netmask,
valid_network_netmask,
valid_in_network,
valid_broadcast,
valid_not_equal,
function_waiting_for_dict,
function_waiting_for_error,
)
from .autolib import (
Calculation,
Params,
ParamOption,
ParamDynOption,
ParamSelfOption,
ParamValue,
ParamIndex,
ParamIdentifier,
ParamInformation,
ParamSelfInformation,
)
from .option import *
from .error import ConfigError
from .api import Config, MetaConfig, GroupConfig, MixConfig
@ -26,35 +44,36 @@ from .option import __all__ as all_options
from .setting import owners, groups, undefined
allfuncs = ['Calculation',
'Params',
'ParamOption',
'ParamDynOption',
'ParamSelfOption',
'ParamValue',
'ParamIndex',
'ParamIdentifier',
'ParamInformation',
'ParamSelfInformation',
'MetaConfig',
'MixConfig',
'GroupConfig',
'Config',
'ConfigError',
'undefined',
'owners',
'groups',
'calc_value',
'calc_value_property_help',
'valid_ip_netmask',
'valid_network_netmask',
'valid_in_network',
'valid_broadcast',
'function_waiting_for_dict',
'function_waiting_for_error',
]
allfuncs = [
"Calculation",
"Params",
"ParamOption",
"ParamDynOption",
"ParamSelfOption",
"ParamValue",
"ParamIndex",
"ParamIdentifier",
"ParamInformation",
"ParamSelfInformation",
"MetaConfig",
"MixConfig",
"GroupConfig",
"Config",
"ConfigError",
"undefined",
"owners",
"groups",
"calc_value",
"calc_value_property_help",
"valid_ip_netmask",
"valid_network_netmask",
"valid_in_network",
"valid_broadcast",
"function_waiting_for_dict",
"function_waiting_for_error",
]
allfuncs.extend(all_options)
del(all_options)
del all_options
__all__ = tuple(allfuncs)
del(allfuncs)
del allfuncs
__version__ = "4.1.0"

File diff suppressed because it is too large Load diff

View file

@ -26,29 +26,32 @@ from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWar
from .i18n import _
from .setting import undefined, ConfigBag
from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR
# ____________________________________________________________
def get_calculated_value(subconfig: "SubConfig",
def get_calculated_value(
subconfig: "SubConfig",
value: Any,
*,
reset_cache: bool=True,
validate_properties: bool=True,
) -> Any:
"""value could be a calculation, in this case do calculation
"""
reset_cache: bool = True,
validate_properties: bool = True,
) -> Any:
"""value could be a calculation, in this case do calculation"""
has_calculation = False
if isinstance(value, Calculation):
if subconfig is None:
return undefined, False
value = value.execute(subconfig,
value = value.execute(
subconfig,
validate_properties=validate_properties,
)
has_calculation = True
elif isinstance(value, list):
# if value is a list, do subcalculation
for idx, val in enumerate(value):
value[idx], _has_calculation = get_calculated_value(subconfig,
value[idx], _has_calculation = get_calculated_value(
subconfig,
val,
reset_cache=False,
validate_properties=validate_properties,
@ -61,7 +64,8 @@ def get_calculated_value(subconfig: "SubConfig",
class Params:
__slots__ = ('args', 'kwargs')
__slots__ = ("args", "kwargs")
def __init__(self, args=None, kwargs=None, **kwgs):
if args is None:
args = tuple()
@ -73,15 +77,15 @@ class Params:
args = (args,)
else:
if not isinstance(args, tuple):
raise ValueError(_('args in params must be a tuple'))
raise ValueError(_("args in params must be a tuple"))
for arg in args:
if not isinstance(arg, Param):
raise ValueError(_('arg in params must be a Param'))
raise ValueError(_("arg in params must be a Param"))
if not isinstance(kwargs, dict):
raise ValueError(_('kwargs in params must be a dict'))
raise ValueError(_("kwargs in params must be a dict"))
for arg in kwargs.values():
if not isinstance(arg, Param):
raise ValueError(_('arg in params must be a Param'))
raise ValueError(_("arg in params must be a Param"))
self.args = args
self.kwargs = kwargs
@ -92,55 +96,74 @@ class Param:
class ParamOption(Param):
__slots__ = ('option',
'notraisepropertyerror',
'raisepropertyerror',
__slots__ = (
"option",
"notraisepropertyerror",
"raisepropertyerror",
)
def __init__(self,
option: 'Option',
notraisepropertyerror: bool=False,
raisepropertyerror: bool=False,
def __init__(
self,
option: "Option",
notraisepropertyerror: bool = False,
raisepropertyerror: bool = False,
) -> None:
if __debug__ and not hasattr(option, 'impl_is_symlinkoption'):
raise ValueError(_('paramoption needs an option not {}').format(type(option)))
if __debug__ and not hasattr(option, "impl_is_symlinkoption"):
raise ValueError(
_("paramoption needs an option not {}").format(type(option))
)
if option.impl_is_symlinkoption():
cur_opt = option.impl_getopt()
else:
cur_opt = option
assert isinstance(notraisepropertyerror, bool), _('param must have a boolean not a {} for notraisepropertyerror').format(type(notraisepropertyerror))
assert isinstance(raisepropertyerror, bool), _('param must have a boolean not a {} for raisepropertyerror').format(type(raisepropertyerror))
assert isinstance(notraisepropertyerror, bool), _(
"param must have a boolean not a {} for notraisepropertyerror"
).format(type(notraisepropertyerror))
assert isinstance(raisepropertyerror, bool), _(
"param must have a boolean not a {} for raisepropertyerror"
).format(type(raisepropertyerror))
self.option = cur_opt
self.notraisepropertyerror = notraisepropertyerror
self.raisepropertyerror = raisepropertyerror
class ParamDynOption(ParamOption):
__slots__ = ('identifiers',
'optional',
__slots__ = (
"identifiers",
"optional",
)
def __init__(self,
option: 'Option',
def __init__(
self,
option: "Option",
identifiers: list[str],
notraisepropertyerror: bool=False,
raisepropertyerror: bool=False,
optional: bool=False,
notraisepropertyerror: bool = False,
raisepropertyerror: bool = False,
optional: bool = False,
) -> None:
super().__init__(option,
super().__init__(
option,
notraisepropertyerror,
raisepropertyerror,
)
if not isinstance(identifiers, list):
raise Exception(f'identifiers in ParamDynOption must be a list, not {identifiers}')
raise Exception(
f"identifiers in ParamDynOption must be a list, not {identifiers}"
)
if not isinstance(optional, bool):
raise Exception(f'optional in ParamDynOption must be a boolean, not {optional}')
raise Exception(
f"optional in ParamDynOption must be a boolean, not {optional}"
)
self.identifiers = identifiers
self.optional = optional
class ParamSelfOption(Param):
__slots__ = ('whole')
def __init__(self,
whole: bool=undefined,
__slots__ = "whole"
def __init__(
self,
whole: bool = undefined,
) -> None:
"""whole: send all value for a multi, not only indexed value"""
if whole is not undefined:
@ -148,21 +171,25 @@ class ParamSelfOption(Param):
class ParamValue(Param):
__slots__ = ('value',)
__slots__ = ("value",)
def __init__(self, value):
self.value = value
class ParamInformation(Param):
__slots__ = ('information_name',
'default_value',
'option',
'self_option',
__slots__ = (
"information_name",
"default_value",
"option",
"self_option",
)
def __init__(self,
def __init__(
self,
information_name: str,
default_value: Any=undefined,
option: 'Option'=None
default_value: Any = undefined,
option: "Option" = None,
) -> None:
self.information_name = information_name
self.default_value = default_value
@ -174,20 +201,22 @@ class ParamInformation(Param):
def set_self_option(self, option):
self.self_option = option
def set_option(self,
option: 'Option'=None
) -> None:
if not hasattr(self, 'self_option'):
raise ConfigError('cannot add option in information after creating config')
def set_option(self, option: "Option" = None) -> None:
if not hasattr(self, "self_option"):
raise ConfigError("cannot add option in information after creating config")
if self.option:
raise ConfigError('cannot redefine option in information')
raise ConfigError("cannot redefine option in information")
if not option.impl_is_optiondescription():
if option.impl_is_symlinkoption():
raise ValueError(_('option in ParamInformation cannot be a symlinkoption'))
raise ValueError(
_("option in ParamInformation cannot be a symlinkoption")
)
if option.impl_is_follower():
raise ValueError(_('option in ParamInformation cannot be a follower'))
raise ValueError(_("option in ParamInformation cannot be a follower"))
if option.impl_is_dynsymlinkoption():
raise ValueError(_('option in ParamInformation cannot be a dynamic option'))
raise ValueError(
_("option in ParamInformation cannot be a dynamic option")
)
self.option = option
if self.self_option:
informations = self.self_option._dependencies_information
@ -195,19 +224,24 @@ class ParamInformation(Param):
del self.self_option._dependencies_information
else:
informations.remove(None)
if not getattr(option, '_dependencies_information', {}):
if not getattr(option, "_dependencies_information", {}):
option._dependencies_information = {None: []}
option._dependencies_information[None].append(self)
option._dependencies_information.setdefault(self.information_name, []).append(weakref.ref(self.self_option))
option._dependencies_information.setdefault(
self.information_name, []
).append(weakref.ref(self.self_option))
class ParamSelfInformation(ParamInformation):
__slots__ = tuple()
def __init__(self,
def __init__(
self,
information_name: str,
default_value: Any=undefined,
default_value: Any = undefined,
) -> None:
return super().__init__(information_name,
return super().__init__(
information_name,
default_value,
)
@ -217,29 +251,38 @@ class ParamIndex(Param):
class ParamIdentifier(Param):
__slots__ = ('identifier_index',)
def __init__(self,
identifier_index: int=-1,
__slots__ = ("identifier_index",)
def __init__(
self,
identifier_index: int = -1,
) -> None:
self.identifier_index = identifier_index
class Calculation:
__slots__ = ('function',
'params',
'help_function',
'_has_index',
'warnings_only',
__slots__ = (
"function",
"params",
"help_function",
"_has_index",
"warnings_only",
)
def __init__(self,
def __init__(
self,
function: Callable,
params: Params=Params(),
help_function: Optional[Callable]=None,
warnings_only: bool=False,
params: Params = Params(),
help_function: Optional[Callable] = None,
warnings_only: bool = False,
):
assert isinstance(function, Callable), _('first argument ({0}) must be a function').format(function)
assert isinstance(function, Callable), _(
"first argument ({0}) must be a function"
).format(function)
if help_function:
assert isinstance(help_function, Callable), _('help_function ({0}) must be a function').format(help_function)
assert isinstance(help_function, Callable), _(
"help_function ({0}) must be a function"
).format(help_function)
self.help_function = help_function
else:
self.help_function = None
@ -252,16 +295,18 @@ class Calculation:
if warnings_only is True:
self.warnings_only = warnings_only
def execute(self,
def execute(
self,
subconfig: "SubConfig",
*,
orig_value: Any=undefined,
allow_value_error: bool=False,
force_value_warning: bool=False,
for_settings: bool=False,
validate_properties: bool=True,
orig_value: Any = undefined,
allow_value_error: bool = False,
force_value_warning: bool = False,
for_settings: bool = False,
validate_properties: bool = True,
) -> Any:
return carry_out_calculation(subconfig,
return carry_out_calculation(
subconfig,
callback=self.function,
callback_params=self.params,
index=subconfig.index,
@ -273,15 +318,18 @@ class Calculation:
validate_properties=validate_properties,
)
def help(self,
def help(
self,
subconfig: "SubConfig",
for_settings: bool=False,
for_settings: bool = False,
) -> str:
if not self.help_function:
return self.execute(subconfig,
return self.execute(
subconfig,
for_settings=for_settings,
)
return carry_out_calculation(subconfig,
return carry_out_calculation(
subconfig,
callback=self.help_function,
callback_params=self.params,
index=subconfig.index,
@ -293,24 +341,27 @@ class Calculation:
return x
def manager_callback(callback: Callable,
def manager_callback(
callback: Callable,
param: Param,
subconfig: 'SubConfig',
subconfig: "SubConfig",
index: Optional[int],
orig_value,
config_bag: ConfigBag,
for_settings: bool,
validate_properties: bool,
) -> Any:
) -> Any:
"""replace Param by true value"""
option = subconfig.option
def calc_apply_index(param,
def calc_apply_index(
param,
same_leadership,
):
return index is not None and \
not getattr(param, 'whole', not same_leadership)
return index is not None and not getattr(param, "whole", not same_leadership)
def calc_self(param,
def calc_self(
param,
index,
value,
config_bag,
@ -318,15 +369,18 @@ def manager_callback(callback: Callable,
# index must be apply only if follower
is_follower = subconfig.option.impl_is_follower()
# FIXME "same_leadership" or "is_follower"?
apply_index = calc_apply_index(param,
apply_index = calc_apply_index(
param,
is_follower,
)
if value is undefined or (apply_index is False and is_follower):
path = subconfig.path
properties = config_bag.context.get_settings().getproperties(subconfig,
properties = config_bag.context.get_settings().getproperties(
subconfig,
uncalculated=True,
)
new_value = get_value(config_bag,
new_value = get_value(
config_bag,
subconfig,
param,
True,
@ -340,7 +394,8 @@ def manager_callback(callback: Callable,
value = value[index]
return value
def get_value(config_bag,
def get_value(
config_bag,
subconfig,
param,
self_calc,
@ -349,52 +404,84 @@ def manager_callback(callback: Callable,
properties=undefined,
):
option = subconfig.option
if option.impl_is_follower() and (subconfig.index is None or apply_index is False):
if option.impl_is_follower() and (
subconfig.index is None or apply_index is False
):
value = []
for idx in range(subconfig.parent.get_length_leadership()):
subconfig = get_option_bag(config_bag,
subconfig = get_option_bag(
config_bag,
option,
param,
idx,
self_calc,
properties=properties,
)
value.append(_get_value(param,
value.append(
_get_value(
param,
subconfig,
))
)
)
else:
value = _get_value(param,
value = _get_value(
param,
subconfig,
)
return value
def _get_value(param: Params,
subconfig: 'SubConfig',
def _get_value(
param: Params,
subconfig: "SubConfig",
) -> Any:
try:
# get value
value = config_bag.context.get_value(subconfig)
except PropertiesOptionError as err:
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if isinstance(param, ParamSelfOption) or param.notraisepropertyerror or param.raisepropertyerror:
if (
isinstance(param, ParamSelfOption)
or param.notraisepropertyerror
or param.raisepropertyerror
):
raise err from err
display_name = subconfig.option.impl_get_display_name(subconfig, with_quote=True)
raise ConfigError(_('unable to carry out a calculation for {}, {}').format(display_name, err)) from err
display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True
)
raise ConfigError(
_("unable to carry out a calculation for {}, {}").format(
display_name, err
)
) from err
except ValueError as err:
display_name = subconfig.option.impl_get_display_name(subconfig, with_quote=True)
raise ValueError(_('the option {0} is used in a calculation but is invalid ({1})').format(display_name, err)) from err
display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True
)
raise ValueError(
_(
"the option {0} is used in a calculation but is invalid ({1})"
).format(display_name, err)
) from err
except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional:
# cannot acces, simulate a propertyerror
raise PropertiesOptionError(subconfig,
['configerror'],
raise PropertiesOptionError(
subconfig,
["configerror"],
config_bag.context.get_settings(),
)
display_name = subconfig.option.impl_get_display_name(subconfig, with_quote=True)
raise ConfigError(_(f'unable to get value for calculating {display_name}, {err}')) from err
display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True
)
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
return value
def get_option_bag(config_bag,
def get_option_bag(
config_bag,
opt,
param,
index_,
@ -404,14 +491,15 @@ def manager_callback(callback: Callable,
):
# don't validate if option is option that we tried to validate
if for_settings:
config_bag.properties = config_bag.properties - {'warnings'}
config_bag.properties = config_bag.properties - {"warnings"}
if not for_settings:
config_bag.properties -= {'warnings'}
config_bag.properties -= {"warnings"}
if self_calc:
config_bag.unrestraint()
config_bag.remove_validation()
try:
subsubconfig = config_bag.context.get_sub_config(config_bag,
subsubconfig = config_bag.context.get_sub_config(
config_bag,
opt.impl_getpath(),
index_,
validate_properties=not self_calc,
@ -422,19 +510,32 @@ def manager_callback(callback: Callable,
if param.notraisepropertyerror or param.raisepropertyerror:
raise err from err
display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ConfigError(_('unable to carry out a calculation for {}, {}').format(display_name, err)) from err
raise ConfigError(
_("unable to carry out a calculation for {}, {}").format(
display_name, err
)
) from err
except ValueError as err:
display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ValueError(_('the option {0} is used in a calculation but is invalid ({1})').format(display_name, err)) from err
raise ValueError(
_(
"the option {0} is used in a calculation but is invalid ({1})"
).format(display_name, err)
) from err
except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional:
# cannot acces, simulate a propertyerror
raise PropertiesOptionError(param,
['configerror'],
raise PropertiesOptionError(
param,
["configerror"],
config_bag.context.get_settings(),
)
display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ConfigError(_(f'unable to get value for calculating {display_name}, {err}')) from err
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
return subsubconfig
if isinstance(param, ParamValue):
@ -446,57 +547,84 @@ def manager_callback(callback: Callable,
elif param.option:
if param.option.issubdyn():
search_option = param.option
isubconfig = subconfig.get_common_child(search_option,
isubconfig = subconfig.get_common_child(
search_option,
true_path=subconfig.path,
)
if isinstance(isubconfig, list):
display_name = option.impl_get_display_name(subconfig, with_quote=True)
search_name = search_option.impl_get_display_name(None, with_quote=True)
raise ConfigError(f'cannot find information for {display_name}, {search_name} is a dynamic option')
display_name = option.impl_get_display_name(
subconfig, with_quote=True
)
search_name = search_option.impl_get_display_name(
None, with_quote=True
)
raise ConfigError(
f"cannot find information for {display_name}, {search_name} is a dynamic option"
)
else:
isubconfig = get_option_bag(config_bag,
isubconfig = get_option_bag(
config_bag,
param.option,
param,
None,
False,
#properties=properties,
# properties=properties,
)
else:
isubconfig = config_bag.context.get_root(config_bag)
try:
return config_bag.context.get_values().get_information(isubconfig,
return config_bag.context.get_values().get_information(
isubconfig,
param.information_name,
param.default_value,
)
except ValueError as err:
display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ConfigError(_(f'unable to get value for calculating {display_name}, {err}')) from err
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
if isinstance(param, ParamIndex):
return index
if isinstance(param, ParamIdentifier):
if not option.issubdyn() and (not option.impl_is_optiondescription() or not option.impl_is_dynoptiondescription()):
display_name = subconfig.option.impl_get_display_name(subconfig, with_quote=True)
raise ConfigError(_(f'option {display_name} is not a dynoptiondescription or in a dynoptiondescription'))
if not option.issubdyn() and (
not option.impl_is_optiondescription()
or not option.impl_is_dynoptiondescription()
):
display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True
)
raise ConfigError(
_(
"option {0} is not a dynoptiondescription or in a dynoptiondescription"
).format(display_name)
)
return subconfig.identifiers[param.identifier_index]
if isinstance(param, ParamSelfOption):
value = calc_self(param,
value = calc_self(
param,
index,
orig_value,
config_bag,
)
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
return value
return {'name': option.impl_get_display_name(subconfig),
'value': value,
return {
"name": option.impl_get_display_name(subconfig),
"value": value,
}
if isinstance(param, ParamOption):
callbk_option = param.option
if index is not None and callbk_option.impl_get_leadership() and \
callbk_option.impl_get_leadership().in_same_leadership(option):
if (
index is not None
and callbk_option.impl_get_leadership()
and callbk_option.impl_get_leadership().in_same_leadership(option)
):
if not callbk_option.impl_is_follower():
# leader
index_ = None
@ -511,13 +639,14 @@ def manager_callback(callback: Callable,
if callbk_option.issubdyn():
if isinstance(param, ParamDynOption):
identifiers = param.identifiers.copy()
paths = callbk_option.impl_getpath().split('.')
paths = callbk_option.impl_getpath().split(".")
parents = [config_bag.context.get_root(config_bag)]
subconfigs_is_a_list = False
for name in paths:
new_parents = []
for parent in parents:
doption = parent.option.get_child(name,
doption = parent.option.get_child(
name,
config_bag,
parent,
allow_dynoption=True,
@ -529,31 +658,40 @@ def manager_callback(callback: Callable,
identifier = identifiers.pop(0)
if not identifier:
subconfigs_is_a_list = True
new_parents.extend(parent.dyn_to_subconfig(doption,
new_parents.extend(
parent.dyn_to_subconfig(
doption,
True,
)
)
else:
name = doption.impl_getname(identifier)
try:
doption = parent.option.get_child(name,
doption = parent.option.get_child(
name,
config_bag,
parent,
)
except AttributeError as err:
raise ConfigError(err) from err
new_parents.append(parent.get_child(doption,
new_parents.append(
parent.get_child(
doption,
None,
True,
name=name,
identifier=identifier,
))
)
)
else:
new_parents.append(parent.get_child(doption,
new_parents.append(
parent.get_child(
doption,
None,
True,
name=name,
))
)
)
parents = new_parents
if subconfigs_is_a_list:
@ -563,7 +701,8 @@ def manager_callback(callback: Callable,
else:
search_option = param.option
subconfigs = subconfig.get_common_child(search_option,
subconfigs = subconfig.get_common_child(
search_option,
true_path=subconfig.path,
validate_properties=validate_properties,
)
@ -573,18 +712,21 @@ def manager_callback(callback: Callable,
values = None
subconfigs = [subconfigs]
else:
subconfigs = [get_option_bag(config_bag,
subconfigs = [
get_option_bag(
config_bag,
callbk_option,
param,
index_,
False,
#properties=properties,
# properties=properties,
)
]
values = None
for subconfig in subconfigs:
callbk_option = subconfig.option
value = get_value(config_bag,
value = get_value(
config_bag,
subconfig,
param,
False,
@ -597,22 +739,22 @@ def manager_callback(callback: Callable,
value = values
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
return value
return {'name': callbk_option.impl_get_display_name(subconfig),
'value': value}
return {"name": callbk_option.impl_get_display_name(subconfig), "value": value}
def carry_out_calculation(subconfig: 'SubConfig',
def carry_out_calculation(
subconfig: "SubConfig",
callback: Callable,
callback_params: Optional[Params],
index: Optional[int],
config_bag: Optional[ConfigBag],
orig_value=undefined,
allow_value_error: bool=False,
force_value_warning: bool=False,
for_settings: bool=False,
allow_value_error: bool = False,
force_value_warning: bool = False,
for_settings: bool = False,
*,
validate_properties: bool=True,
):
validate_properties: bool = True,
):
"""a function that carries out a calculation for an option's value
:param option: the option
@ -630,18 +772,29 @@ def carry_out_calculation(subconfig: 'SubConfig',
if PropertiesOptionError)
Values could have multiple values only when key is ''."""
option = subconfig.option
if not option.impl_is_optiondescription() and option.impl_is_follower() and index is None:
raise ConfigError(f'the follower {option.impl_get_display_name(subconfig, with_quote=True)} must have index in carry_out_calculation!')
if (
not option.impl_is_optiondescription()
and option.impl_is_follower()
and index is None
):
raise ConfigError(
f"the follower {option.impl_get_display_name(subconfig, with_quote=True)} must have index in carry_out_calculation!"
)
def fake_items(iterator):
return ((None, i) for i in iterator)
args = []
kwargs = {}
config_bag = config_bag.copy()
config_bag.set_permissive()
if callback_params:
for key, param in chain(fake_items(callback_params.args), callback_params.kwargs.items()):
for key, param in chain(
fake_items(callback_params.args), callback_params.kwargs.items()
):
try:
value = manager_callback(callback,
value = manager_callback(
callback,
param,
subconfig,
index,
@ -659,49 +812,75 @@ def carry_out_calculation(subconfig: 'SubConfig',
raise err
if callback.__name__ in FUNCTION_WAITING_FOR_DICT:
if key is None:
args.append({'propertyerror': str(err), 'name': option.impl_get_display_name(subconfig)})
args.append(
{
"propertyerror": str(err),
"name": option.impl_get_display_name(subconfig),
}
)
else:
kwargs[key] = {'propertyerror': str(err), 'name': option.impl_get_display_name(subconfig)}
kwargs[key] = {
"propertyerror": str(err),
"name": option.impl_get_display_name(subconfig),
}
if callback.__name__ in FUNCTION_WAITING_FOR_ERROR:
if key is None:
args.append(err)
else:
kwargs[key] = err
ret = calculate(subconfig,
ret = calculate(
subconfig,
callback,
allow_value_error,
force_value_warning,
args,
kwargs,
)
if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \
not option.impl_is_optiondescription() and \
option.impl_is_follower() and not option.impl_is_submulti():
if (
isinstance(ret, list)
and not option.impl_is_dynoptiondescription()
and not option.impl_is_optiondescription()
and option.impl_is_follower()
and not option.impl_is_submulti()
):
if args or kwargs:
raise LeadershipError(_('the "{}" function with positional arguments "{}" '
raise LeadershipError(
_(
'the "{}" function with positional arguments "{}" '
'and keyword arguments "{}" must not return '
'a list ("{}") for the follower option {}'
'').format(callback.__name__,
""
).format(
callback.__name__,
args,
kwargs,
ret,
option.impl_get_display_name(subconfig, with_quote=True)))
option.impl_get_display_name(subconfig, with_quote=True),
)
)
else:
raise LeadershipError(_('the "{}" function must not return a list ("{}") '
'for the follower option {}'
'').format(callback.__name__,
raise LeadershipError(
_(
'the "{}" function must not return a list ("{}") '
"for the follower option {}"
""
).format(
callback.__name__,
ret,
option.impl_get_display_name(subconfig, with_quote=True)))
option.impl_get_display_name(subconfig, with_quote=True),
)
)
return ret
def calculate(subconfig,
def calculate(
subconfig,
callback: Callable,
allow_value_error: bool,
force_value_warning: bool,
args,
kwargs,
):
):
"""wrapper that launches the 'callback'
:param callback: callback function
@ -722,15 +901,20 @@ def calculate(subconfig,
except Exception as err:
error = err
if args or kwargs:
msg = _('unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" '
'for option {2}').format(str(error),
msg = _(
'unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" '
"for option {2}"
).format(
str(error),
callback.__name__,
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
args,
kwargs)
kwargs,
)
else:
msg = _('unexpected error "{0}" in function "{1}" for option {2}'
'').format(str(error),
msg = _('unexpected error "{0}" in function "{1}" for option {2}' "").format(
str(error),
callback.__name__,
subconfig.option.impl_get_display_name(subconfig, with_quote=True))
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
)
raise ConfigError(msg) from error

View file

@ -19,9 +19,9 @@ from time import time
class Cache:
"""cache object
"""
__slots__ = ('_cache',)
"""cache object"""
__slots__ = ("_cache",)
def __init__(self):
self._cache = {}
@ -35,29 +35,30 @@ class Cache:
index = subconfig.index
return path, index
def getcache(self,
def getcache(
self,
subconfig,
type_,
expiration=True,
):
"""get the cache value fot a specified path
"""
"""get the cache value fot a specified path"""
no_cache = False, None, False
path, index = self._get_path_index(subconfig)
if path not in self._cache or index not in self._cache[path]:
return no_cache
value, timestamp, validated = self._cache[path][index]
props = subconfig.config_bag.properties
if type_ == 'self_props':
if type_ == "self_props":
# cached value is self_props
self_props = value
else:
self_props = subconfig.properties
if 'cache' in props or \
'cache' in self_props:
if expiration and timestamp and \
('expire' in props or \
'expire' in self_props):
if "cache" in props or "cache" in self_props:
if (
expiration
and timestamp
and ("expire" in props or "expire" in self_props)
):
ntime = int(time())
if timestamp + subconfig.config_bag.expiration_time >= ntime:
return True, value, validated
@ -65,37 +66,38 @@ class Cache:
return True, value, validated
return no_cache
def setcache(self,
def setcache(
self,
subconfig,
val,
type_='values',
type_="values",
validated=True,
):
"""add val in cache for a specified path
if follower, add index
"""
if type_ == 'values':
if 'cache' not in subconfig.config_bag.properties and \
'cache' not in subconfig.properties:
if type_ == "values":
if (
"cache" not in subconfig.config_bag.properties
and "cache" not in subconfig.properties
):
return
elif (subconfig is None or 'cache' not in subconfig.config_bag.properties) and \
'cache' not in val:
elif (
subconfig is None or "cache" not in subconfig.config_bag.properties
) and "cache" not in val:
return
path, index = self._get_path_index(subconfig)
self._cache.setdefault(path, {})[index] = (val, int(time()), validated)
def delcache(self, path):
"""reset cache a a specified path
"""
"""reset cache a a specified path"""
if path in self._cache:
del self._cache[path]
def get_cached(self):
"""get cache values
"""
"""get cache values"""
return self._cache
def reset_all_cache(self):
"""reset all cache values
"""
"""reset all cache values"""
self._cache.clear()

File diff suppressed because it is too large Load diff

View file

@ -19,17 +19,18 @@ import weakref
from .i18n import _
def display_list(lst,
def display_list(
lst,
*,
separator='and',
separator="and",
add_quote=False,
) -> str():
) -> str():
if not lst:
return '""'
if separator == 'and':
separator = _('and')
elif separator == 'or':
separator = _('or')
if separator == "and":
separator = _("and")
elif separator == "or":
separator = _("or")
if isinstance(lst, tuple) or isinstance(lst, frozenset):
lst = list(lst)
if len(lst) == 1:
@ -51,30 +52,35 @@ def display_list(lst,
lst__.append(l)
lst__.sort()
last = lst__[-1]
return ', '.join(lst__[:-1]) + _(' {} ').format(separator) + '{}'.format(last)
return ", ".join(lst__[:-1]) + _(" {} ").format(separator) + "{}".format(last)
# Exceptions for an Option
class PropertiesOptionError(AttributeError):
"attempt to access to an option with a property that is not allowed"
def __init__(self,
def __init__(
self,
subconfig,
proptype,
settings,
opt_type=None,
name=None,
orig_opt=None,
help_properties=None):
help_properties=None,
):
if opt_type:
self._opt_type = opt_type
self._name = name
self._orig_opt = orig_opt
else:
if subconfig.option.impl_is_optiondescription():
self._opt_type = 'optiondescription'
self._opt_type = "optiondescription"
else:
self._opt_type = 'option'
self._name = subconfig.option.impl_get_display_name(subconfig, with_quote=True)
self._opt_type = "option"
self._name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True
)
self._orig_opt = None
self._subconfig = subconfig
self.proptype = proptype
@ -91,7 +97,7 @@ class PropertiesOptionError(AttributeError):
if self.msg is not None:
return self.msg
if self._settings is None:
return 'error'
return "error"
if self.help_properties:
properties = list(self.help_properties)
else:
@ -99,43 +105,44 @@ class PropertiesOptionError(AttributeError):
only_one = len(properties) == 1
properties_msg = display_list(properties, add_quote=True)
if only_one:
prop_msg = _('property')
prop_msg = _("property")
else:
prop_msg = _('properties')
if properties == ['frozen']:
prop_msg = _("properties")
if properties == ["frozen"]:
if self._orig_opt:
msg = 'cannot modify the {0} {1} because "{2}" has {3} {4}'
msg = _('cannot modify the {0} {1} because "{2}" has {3} {4}')
else:
msg = 'cannot modify the {0} {1} because has {2} {3}'
msg = _("cannot modify the {0} {1} because has {2} {3}")
else:
if self._orig_opt:
msg = 'cannot access to {0} {1} because "{2}" has {3} {4}'
msg = _('cannot access to {0} {1} because "{2}" has {3} {4}')
else:
msg = 'cannot access to {0} {1} because has {2} {3}'
msg = _("cannot access to {0} {1} because has {2} {3}")
if self._orig_opt:
# FIXME _orig_opt ?
self.msg = _(msg).format(self._opt_type,
self.msg = msg.format(
self._opt_type,
self._orig_opt.impl_get_display_name(subconfig, with_quote=True),
self._name,
prop_msg,
properties_msg)
properties_msg,
)
else:
self.msg = _(msg).format(self._opt_type,
self._name,
prop_msg,
properties_msg)
self.msg = msg.format(self._opt_type, self._name, prop_msg, properties_msg)
del self._opt_type, self._name
del self._settings, self._orig_opt
return self.msg
#____________________________________________________________
# ____________________________________________________________
# Exceptions for a Config
class ConfigError(Exception):
"""attempt to change an option's owner without a value
or in case of `_descr` is None
or if a calculation cannot be carried out"""
def __init__(self,
def __init__(
self,
exp,
ori_err=None,
):
@ -148,8 +155,8 @@ class ConflictError(Exception):
pass
#____________________________________________________________
# miscellaneous exceptions
# ____________________________________________________________
# miscellaneous exceptions
class LeadershipError(Exception):
"problem with a leadership's value length"
pass
@ -161,13 +168,7 @@ class ConstError(TypeError):
class _CommonError:
def __init__(self,
subconfig,
val,
display_type,
opt,
err_msg,
index):
def __init__(self, subconfig, val, display_type, opt, err_msg, index):
self.val = val
self.display_type = display_type
self.opt = weakref.ref(opt)
@ -180,24 +181,24 @@ class _CommonError:
try:
msg = self.prefix
except AttributeError:
self.prefix = self.tmpl.format(self.val,
_(self.display_type),
self.name)
self.prefix = self.tmpl.format(self.val, _(self.display_type), self.name)
msg = self.prefix
if self.err_msg:
if msg:
msg += ', {}'.format(self.err_msg)
msg += ", {}".format(self.err_msg)
else:
msg = self.err_msg
if not msg:
msg = _('invalid value')
msg = _("invalid value")
return msg
class ValueWarning(_CommonError, UserWarning):
tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
tmpl = None
def __init__(self, *args, **kwargs):
if ValueWarning.tmpl is None:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
if len(args) == 1 and not kwargs:
self.msg = args[0]
else:
@ -211,8 +212,18 @@ class ValueWarning(_CommonError, UserWarning):
class ValueOptionError(_CommonError, ValueError):
tmpl = _('"{0}" is an invalid {1} for "{2}"')
tmpl = None
def __init__(self, *args, **kwargs):
if ValueOptionError.tmpl is None:
ValueOptionError.tmpl = _('"{0}" is an invalid {1} for "{2}"')
super().__init__(*args, **kwargs)
class ValueErrorWarning(ValueWarning):
tmpl = _('"{0}" is an invalid {1} for "{2}"')
tmpl = None
def __init__(self, *args, **kwargs):
if ValueErrorWarning.tmpl is None:
ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for "{2}"')
super().__init__(*args, **kwargs)

View file

@ -48,73 +48,83 @@ def function_waiting_for_error(function):
@function_waiting_for_dict
def valid_network_netmask(network: dict,
def valid_network_netmask(
network: dict,
netmask: dict,
):
):
"""
validates if network and netmask are coherent
this validator must be set to netmask option
"""
if None in [network['value'], netmask['value']]:
if None in [network["value"], netmask["value"]]:
return
try:
ip_network(f'{network["value"]}/{netmask["value"]}')
except ValueError as err:
raise ValueError(_(f'network "{network["value"]}" ({network["name"]}) does not match '
'with this netmask')) from err
raise ValueError(
_('network "{0}" ({1}) does not match with this netmask').format(
network["value"], network["name"]
)
) from err
@function_waiting_for_dict
def valid_ip_netmask(ip: dict, # pylint: disable=invalid-name
def valid_ip_netmask(
ip: dict, # pylint: disable=invalid-name
netmask: dict,
):
):
"""validates if ip and netmask are coherent
this validator must be set to netmask option
"""
if None in [ip['value'], netmask['value']]:
if None in [ip["value"], netmask["value"]]:
return
ip_netmask = ip_interface(f'{ip["value"]}/{netmask["value"]}')
if ip_netmask.ip == ip_netmask.network.network_address:
msg = _(f'IP "{ip["value"]}" ({ip["name"]}) with this netmask is '
'in fact a network address')
msg = _('IP "{0}" ({1}) with this netmask is in fact a network address').format(
ip["value"], ip["name"]
)
raise ValueError(msg)
if ip_netmask.ip == ip_netmask.network.broadcast_address:
msg = _(f'IP "{ip["value"]}" ({ip["name"]}) with this netmask is '
'in fact a broacast address')
msg = _(
'IP "{0}" ({1}) with this netmask is in fact a broadcast address'
).format(ip["value"], ip["name"])
raise ValueError(msg)
@function_waiting_for_dict
def valid_broadcast(network: dict,
def valid_broadcast(
network: dict,
netmask: dict,
broadcast: dict,
):
"""validates if the broadcast is coherent with network and netmask
"""
if None in [network['value'], netmask['value'], broadcast['value']]:
):
"""validates if the broadcast is coherent with network and netmask"""
if None in [network["value"], netmask["value"], broadcast["value"]]:
return
if ip_network(f'{network["value"]}/{netmask["value"]}').broadcast_address != \
ip_address(broadcast['value']):
msg = _(f'broadcast invalid with network {network["value"]} ({network["name"]}) '
f'and netmask {netmask["value"]} ({netmask["name"]})')
if ip_network(
f'{network["value"]}/{netmask["value"]}'
).broadcast_address != ip_address(broadcast["value"]):
msg = _(
"broadcast invalid with network {0} ({1}) and netmask {2} ({3})"
).format(network["value"], network["name"], netmask["value"], netmask["name"])
raise ValueError(msg)
@function_waiting_for_dict
def valid_in_network(ip: dict, # pylint: disable=invalid-name
def valid_in_network(
ip: dict, # pylint: disable=invalid-name
network: dict,
netmask=Optional[dict],
):
):
"""validates if an IP is in a network
this validator must be set to ip option
"""
if None in [ip['value'], network['value']]:
if None in [ip["value"], network["value"]]:
return
if '/' in network['value']:
if "/" in network["value"]:
# it's a CIDR network
network_value = network['value']
network_value = network["value"]
else:
if netmask is None or netmask['value'] is None:
if netmask is None or netmask["value"] is None:
return
network_value = f'{network["value"]}/{netmask["value"]}'
network_obj = ip_network(network_value)
@ -123,56 +133,63 @@ def valid_in_network(ip: dict, # pylint: disable=invalid-name
if netmask is None:
msg = _('this IP is not in network {network["value"]} ({network["name"]})')
else:
msg = _('this IP is not in network {network["value"]} ({network["name"]}) '
'with netmask {netmask["value"]} ({netmask["name"]})')
msg = _(
'this IP is not in network {network["value"]} ({network["name"]}) '
'with netmask {netmask["value"]} ({netmask["name"]})'
)
raise ValueError(msg)
# test if ip is not network/broadcast IP
if ip_netmask.ip == ip_netmask.network.network_address:
msg = _(f'this IP with the network {network["value"]} ({network["value"]} '
'is in fact a network address')
msg = _(
"this IP with the network {0} ({1}) is in fact a network address"
).format(network["value"], network["name"])
raise ValueError(msg)
if ip_netmask.ip == ip_netmask.network.broadcast_address:
msg = _(f'this IP with the network {network["value"]} ({network["value"]} '
'is in fact a broadcast address')
msg = _(
"this IP with the network {0} ({1}) is in fact a broadcast address"
).format(network["value"], network["value"])
raise ValueError(msg)
@function_waiting_for_dict
def valid_not_equal(*values):
"""valid that two options have not same value
"""
"""valid that two options have not same value"""
equal = set()
for val in values[1:]:
if 'propertyerror' in val:
if "propertyerror" in val:
continue
if values[0]['value'] == val['value'] is not None:
equal.add(val['name'])
if values[0]["value"] == val["value"] is not None:
equal.add(val["name"])
if not equal:
return
msg = _(f'value is identical to {display_list(list(equal), add_quote=True)}')
msg = _("value is identical to {0}").format(
display_list(list(equal), add_quote=True)
)
raise ValueError(msg)
class CalcValue:
"""class to calc_value with different functions
"""
"""class to calc_value with different functions"""
# pylint: disable=too-many-instance-attributes
def __call__(self,
def __call__(
self,
*args: List[Any],
multi: bool=False,
default: Any=undefined,
condition: Any=undefined,
no_condition_is_invalid: bool=False,
expected: Any=undefined,
condition_operator: str='AND',
reverse_condition: bool=False,
allow_none: bool=False,
remove_duplicate_value: bool=False,
join: Optional[str]=None,
min_args_len: Optional[int]=None,
operator: Optional[str]=None,
index: Optional[int]=None,
**kwargs) -> Any:
multi: bool = False,
default: Any = undefined,
condition: Any = undefined,
no_condition_is_invalid: bool = False,
expected: Any = undefined,
condition_operator: str = "AND",
reverse_condition: bool = False,
allow_none: bool = False,
remove_duplicate_value: bool = False,
join: Optional[str] = None,
min_args_len: Optional[int] = None,
operator: Optional[str] = None,
index: Optional[int] = None,
**kwargs,
) -> Any:
# pylint: disable=too-many-statements,too-many-branches,too-many-nested-blocks,too-many-locals
"""calculate value
:param args: list of value
@ -336,8 +353,11 @@ class CalcValue:
self.condition_operator = condition_operator
self.reverse_condition = reverse_condition
self.kwargs = kwargs
self.no_condition_is_invalid = no_condition_is_invalid # pylint: disable=attribute-defined-outside-init
value = self.get_value(default,
self.no_condition_is_invalid = (
no_condition_is_invalid # pylint: disable=attribute-defined-outside-init
)
value = self.get_value(
default,
min_args_len,
)
if not multi:
@ -348,10 +368,11 @@ class CalcValue:
value = None
elif value and operator:
new_value = value[0]
oper = {'mul': mul,
'add': add,
'div': truediv,
'sub': sub,
oper = {
"mul": mul,
"add": add,
"div": truediv,
"sub": sub,
}[operator]
for val in value[1:]:
new_value = oper(new_value, val)
@ -376,8 +397,9 @@ class CalcValue:
break
lval = len(val)
if length_val is not None and length_val != lval:
msg = _('unexpected value in calc_value with join attribute '
f'"{val}" with invalid length "{length_val}"')
msg = _(
'unexpected value in calc_value with join attribute "{0}" with invalid length "{1}"'
).format(val, length_val)
raise ValueError(msg)
length_val = lval
new_value = []
@ -403,19 +425,16 @@ class CalcValue:
value = new_value
return value
def value_from_kwargs(self,
value: Any,
pattern: str,
to_dict: bool=False,
empty_test=undefined) -> Any:
"""get value from kwargs
"""
def value_from_kwargs(
self, value: Any, pattern: str, to_dict: bool = False, empty_test=undefined
) -> Any:
"""get value from kwargs"""
# pylint: disable=too-many-branches
# if value attribute exist return it's value
# otherwise pattern_0, pattern_1, ...
# otherwise undefined
if value is not empty_test:
if to_dict == 'all':
if to_dict == "all":
returns = {None: value}
else:
returns = value
@ -426,7 +445,7 @@ class CalcValue:
if key.startswith(pattern):
index = int(key[len_pattern:])
if isinstance(pattern_value, dict):
pattern_value = pattern_value['value']
pattern_value = pattern_value["value"]
kwargs_matches[index] = pattern_value
if not kwargs_matches:
returns = undefined
@ -443,26 +462,29 @@ class CalcValue:
returns.append(kwargs_matches[key])
return returns
def is_condition_matches(self,
def is_condition_matches(
self,
condition_value,
):
"""verify the condition
"""
"""verify the condition"""
# pylint: disable=too-many-branches
calculated_conditions = self.value_from_kwargs(condition_value,
'condition_',
to_dict='all',
calculated_conditions = self.value_from_kwargs(
condition_value,
"condition_",
to_dict="all",
)
if calculated_conditions is undefined:
is_matches = not self.no_condition_is_invalid
else:
is_matches = None
calculated_expected = self.value_from_kwargs(self.expected,
'expected_',
calculated_expected = self.value_from_kwargs(
self.expected,
"expected_",
to_dict=True,
)
calculated_reverse = self.value_from_kwargs(self.reverse_condition,
'reverse_condition_',
calculated_reverse = self.value_from_kwargs(
self.reverse_condition,
"reverse_condition_",
to_dict=True,
empty_test=False,
)
@ -470,11 +492,17 @@ class CalcValue:
if isinstance(calculated_expected, dict):
if idx is not None:
if isinstance(calculated_expected[idx], list):
current_matches = calculated_condition in calculated_expected[idx]
current_matches = (
calculated_condition in calculated_expected[idx]
)
else:
current_matches = calculated_condition == calculated_expected[idx]
current_matches = (
calculated_condition == calculated_expected[idx]
)
else:
current_matches = calculated_condition in calculated_expected.values()
current_matches = (
calculated_condition in calculated_expected.values()
)
else:
current_matches = calculated_condition == calculated_expected
if isinstance(calculated_reverse, dict) and idx in calculated_reverse:
@ -483,36 +511,41 @@ class CalcValue:
reverse_condition = False
if is_matches is None:
is_matches = current_matches
if self.condition_operator == 'AND':
if self.condition_operator == "AND":
is_matches = is_matches and current_matches
if reverse_condition:
is_matches = not is_matches
if not is_matches:
break
elif self.condition_operator == 'OR':
elif self.condition_operator == "OR":
is_matches = is_matches or current_matches
if reverse_condition:
is_matches = not is_matches
if is_matches:
break
else:
msg = _(f'unexpected {self.condition_operator} condition_operator '
'in calc_value')
msg = _(
"unexpected {0} condition_operator " "in calc_value"
).format(self.condition_operator)
raise ValueError(msg)
is_matches = is_matches and not self.reverse_condition \
or not is_matches and self.reverse_condition
is_matches = (
is_matches
and not self.reverse_condition
or not is_matches
and self.reverse_condition
)
return is_matches
def get_value(self,
def get_value(
self,
default,
min_args_len,
):
"""get the value from arguments
"""
"""get the value from arguments"""
# retrieve the condition
if isinstance(self.condition, dict):
if 'value' in self.condition:
condition_value = self.condition['value']
if "value" in self.condition:
condition_value = self.condition["value"]
else:
condition_value = undefined
else:
@ -527,8 +560,9 @@ class CalcValue:
value = []
if not value:
# default value
new_default = self.value_from_kwargs(default,
'default_',
new_default = self.value_from_kwargs(
default,
"default_",
)
if new_default is not undefined:
if not isinstance(new_default, list):
@ -538,34 +572,32 @@ class CalcValue:
return value
def get_args(self):
"""get all arguments
"""
"""get all arguments"""
return list(self.args)
class CalcValuePropertyHelp(CalcValue):
"""special class to display property error
"""
"""special class to display property error"""
def get_name(self):
"""get the condition name
"""
return self.condition['name']
"""get the condition name"""
return self.condition["name"]
def get_indexed_name(self, index: int) -> str:
"""get name for a specified index
"""
condition_index = self.kwargs.get(f'condition_{index}')
"""get name for a specified index"""
condition_index = self.kwargs.get(f"condition_{index}")
if condition_index is not None and not isinstance(condition_index, dict):
raise ValueError(_(f'unexpected condition_{index} must have "todict" argument'))
return condition_index['name']
raise ValueError(
_('unexpected condition_{0} must have "todict" argument').format(index)
)
return condition_index["name"]
def build_property_message(self,
def build_property_message(
self,
name: str,
value: Any,
) -> str:
"""prepare message to display error message if needed
"""
"""prepare message to display error message if needed"""
if not self.reverse_condition:
msg = _('the value of "{0}" is {1}').format(name, value)
else:
@ -575,21 +607,21 @@ class CalcValuePropertyHelp(CalcValue):
def get_args(self):
args = super().get_args()
action = args[0]
calculated_expected = self.value_from_kwargs(self.expected,
'expected_',
to_dict=True)
calculated_expected = self.value_from_kwargs(
self.expected, "expected_", to_dict=True
)
if self.condition is not undefined:
if 'propertyerror' in self.condition:
msg = self.condition['propertyerror']
if "propertyerror" in self.condition:
msg = self.condition["propertyerror"]
else:
name = self.get_name()
if isinstance(calculated_expected, dict):
calc_values = calculated_expected.values()
else:
calc_values = [calculated_expected]
display_value = display_list([str(val) for val in calc_values],
separator='or',
add_quote=True)
display_value = display_list(
[str(val) for val in calc_values], separator="or", add_quote=True
)
msg = self.build_property_message(name, display_value)
else:
msgs = []
@ -601,8 +633,10 @@ class CalcValuePropertyHelp(CalcValue):
calc_value = CalcValue()
calc_value.__name__ = 'calc_value' # pylint: disable=attribute-defined-outside-init
calc_value.__name__ = "calc_value" # pylint: disable=attribute-defined-outside-init
# function_waiting_for_dict(calc_value)
calc_value_property_help = CalcValuePropertyHelp()
calc_value_property_help.__name__ = 'calc_value_property_help' # pylint: disable=attribute-defined-outside-init
calc_value_property_help.__name__ = (
"calc_value_property_help" # pylint: disable=attribute-defined-outside-init
)
function_waiting_for_dict(calc_value_property_help)

View file

@ -18,55 +18,9 @@
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
"internationalisation utilities"
from .log import log
from gettext import translation
from pathlib import Path
from gettext import translation, NullTranslations
from platform import system
from pkg_resources import resource_filename
from os import environ
t = translation('tiramisu', str(Path(__file__).parent / 'locale'), fallback=True)
DEFAULT = 'en'
def get_translation() -> str:
"""Sets the user locale as langage
The default is set to english
"""
# Application name (without .i18n)
app_name = __name__[:-5]
translations_path = resource_filename(app_name, 'locale')
if 'TIRAMISU_LOCALE' in environ: # pragma: no cover
user_locale = environ['TIRAMISU_LOCALE']
else:
if 'Windows' in system(): # pragma: no cover
import ctypes
from locale import windows_locale
default_locale = windows_locale[ctypes.windll.kernel32.GetUserDefaultUILanguage()]
else:
from locale import getlocale
default_locale = getlocale()
if default_locale and isinstance(default_locale, tuple):
if default_locale[0] is not None:
user_locale = default_locale[0][:2]
else:
user_locale = DEFAULT
elif default_locale: # pragma: no cover
user_locale = default_locale[:2]
else: # pragma: no cover
user_locale = DEFAULT
try:
trans = translation(domain=app_name,
localedir=translations_path,
languages=[user_locale],
)
# codeset='UTF-8')
except FileNotFoundError: # pragma: no cover
log.debug('cannot found translation file for langage {} in localedir {}'.format(user_locale,
translations_path))
trans = NullTranslations()
return trans.gettext
_ = get_translation()
_ = t.gettext

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,711 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2023-11-19 21:26+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: tiramisu/api.py:61
msgid "Settings:"
msgstr ""
#: tiramisu/api.py:63
msgid "Access to option without verifying permissive properties"
msgstr ""
#: tiramisu/api.py:67
msgid "Access to option without property restriction"
msgstr ""
#: tiramisu/api.py:70
msgid "Do not warnings during validation"
msgstr ""
#: tiramisu/api.py:75
msgid "Call: {}"
msgstr ""
#: tiramisu/api.py:77
msgid "Commands:"
msgstr ""
#: tiramisu/api.py:185
msgid "unknown list type {}"
msgstr ""
#: tiramisu/api.py:187
msgid "unknown group_type: {0}"
msgstr ""
#: tiramisu/api.py:352
msgid "only multi value has defaultmulti"
msgstr ""
#: tiramisu/api.py:371 tiramisu/option/intoption.py:31
msgid "integer"
msgstr ""
#: tiramisu/api.py:374 tiramisu/option/domainnameoption.py:43
msgid "domain name"
msgstr ""
#: tiramisu/api.py:376
msgid "ip"
msgstr ""
#: tiramisu/api.py:376
msgid "netmask"
msgstr ""
#: tiramisu/api.py:376
msgid "network"
msgstr ""
#: tiramisu/api.py:471
msgid "cannot add this property: \"{0}\""
msgstr ""
#: tiramisu/api.py:621
msgid "cannot reduce length of the leader \"{}\""
msgstr ""
#: tiramisu/api.py:1088
msgid "properties must be a frozenset"
msgstr ""
#: tiramisu/api.py:1094 tiramisu/api.py:1118
msgid "unknown when {} (must be in append or remove)"
msgstr ""
#: tiramisu/api.py:1106 tiramisu/api.py:1127 tiramisu/config.py:1210
msgid "unknown type {}"
msgstr ""
#: tiramisu/api.py:1432
msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr ""
#: tiramisu/autolib.py:44
msgid "args in params must be a tuple"
msgstr ""
#: tiramisu/autolib.py:47 tiramisu/autolib.py:52
msgid "arg in params must be a Param"
msgstr ""
#: tiramisu/autolib.py:49
msgid "kwargs in params must be a dict"
msgstr ""
#: tiramisu/autolib.py:72
msgid "paramoption needs an option not {}"
msgstr ""
#: tiramisu/autolib.py:77
msgid "param must have a boolean not a {} for notraisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:78
msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:133
msgid "option in ParamInformation cannot be a symlinkoption"
msgstr ""
#: tiramisu/autolib.py:135
msgid "option in ParamInformation cannot be a follower"
msgstr ""
#: tiramisu/autolib.py:137
msgid "option in ParamInformation cannot be a dynamic option"
msgstr ""
#: tiramisu/autolib.py:166
msgid "first argument ({0}) must be a function"
msgstr ""
#: tiramisu/autolib.py:168
msgid "help_function ({0}) must be a function"
msgstr ""
#: tiramisu/autolib.py:316 tiramisu/autolib.py:362
msgid "unable to carry out a calculation for \"{}\", {}"
msgstr ""
#: tiramisu/autolib.py:319 tiramisu/autolib.py:365
msgid "the option \"{0}\" is used in a calculation but is invalid ({1})"
msgstr ""
#: tiramisu/autolib.py:402
msgid "option \"{}\" cannot be calculated: {}"
msgstr ""
#: tiramisu/autolib.py:411
msgid "option \"{}\" is not in a dynoptiondescription"
msgstr ""
#: tiramisu/autolib.py:562
msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option \"{}\""
msgstr ""
#: tiramisu/autolib.py:571
msgid "the \"{}\" function must not return a list (\"{}\") for the follower option \"{}\""
msgstr ""
#: tiramisu/autolib.py:604
msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option \"{2}\""
msgstr ""
#: tiramisu/autolib.py:611
msgid "unexpected error \"{0}\" in function \"{1}\" for option \"{2}\""
msgstr ""
#: tiramisu/config.py:82
msgid "there is no option description for this config (may be GroupConfig)"
msgstr ""
#: tiramisu/config.py:269
msgid "no option found in config with these criteria"
msgstr ""
#: tiramisu/config.py:532 tiramisu/option/optiondescription.py:72
msgid "option description seems to be part of an other config"
msgstr ""
#: tiramisu/config.py:790
msgid "cannot set leadership object has root optiondescription"
msgstr ""
#: tiramisu/config.py:792
msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr ""
#: tiramisu/config.py:840
msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr ""
#: tiramisu/config.py:1024
msgid "unknown config \"{}\""
msgstr ""
#: tiramisu/config.py:1047
msgid "child must be a Config, MixConfig or MetaConfig"
msgstr ""
#: tiramisu/config.py:1079
msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config"
msgstr ""
#: tiramisu/config.py:1085
msgid "force_default and force_dont_change_value cannot be set together"
msgstr ""
#: tiramisu/config.py:1208
msgid "config name must be uniq in groupconfig for {0}"
msgstr ""
#: tiramisu/config.py:1246
msgid "config added has no name, the name is mandatory"
msgstr ""
#: tiramisu/config.py:1248
msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr ""
#: tiramisu/config.py:1270
msgid "cannot find the config {}"
msgstr ""
#: tiramisu/config.py:1294
msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
#: tiramisu/config.py:1303
msgid "child must be a Config or MetaConfig"
msgstr ""
#: tiramisu/config.py:1307
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/config.py:1319
msgid "metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/error.py:26
msgid "and"
msgstr ""
#: tiramisu/error.py:28
msgid "or"
msgstr ""
#: tiramisu/error.py:50
msgid " {} "
msgstr ""
#: tiramisu/error.py:98
msgid "property"
msgstr ""
#: tiramisu/error.py:100
msgid "properties"
msgstr ""
#: tiramisu/error.py:187
msgid "invalid value"
msgstr ""
#: tiramisu/error.py:192
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/error.py:208 tiramisu/error.py:212
msgid "\"{0}\" is an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/function.py:113
msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]})"
msgstr ""
#: tiramisu/function.py:115
msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]}) with netmask {netmask[\"value\"]} ({netmask[\"name\"]})"
msgstr ""
#: tiramisu/function.py:559
msgid "the value of \"{0}\" is {1}"
msgstr ""
#: tiramisu/function.py:561
msgid "the value of \"{0}\" is not {1}"
msgstr ""
#: tiramisu/option/baseoption.py:70 tiramisu/option/symlinkoption.py:40
msgid "\"{0}\" is an invalid name for an option"
msgstr ""
#: tiramisu/option/baseoption.py:83
msgid "invalid properties type {0} for {1}, must be a frozenset"
msgstr ""
#: tiramisu/option/baseoption.py:89
msgid "invalid property type {0} for {1}, must be a string or a Calculation"
msgstr ""
#: tiramisu/option/baseoption.py:227
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr ""
#: tiramisu/option/baseoption.py:265
msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr ""
#: tiramisu/option/booloption.py:32
msgid "boolean"
msgstr ""
#: tiramisu/option/broadcastoption.py:33
msgid "broadcast address"
msgstr ""
#: tiramisu/option/broadcastoption.py:41
msgid "invalid string"
msgstr ""
#: tiramisu/option/choiceoption.py:38
msgid "choice"
msgstr ""
#: tiramisu/option/choiceoption.py:51
msgid "values must be a tuple or a calculation for {0}"
msgstr ""
#: tiramisu/option/choiceoption.py:67
msgid "the calculated values \"{0}\" for \"{1}\" is not a list"
msgstr ""
#: tiramisu/option/choiceoption.py:97
msgid "only \"{0}\" is allowed"
msgstr ""
#: tiramisu/option/choiceoption.py:99
msgid "only {0} are allowed"
msgstr ""
#: tiramisu/option/dateoption.py:33
msgid "date"
msgstr ""
#: tiramisu/option/domainnameoption.py:62
msgid "unknown type {0} for hostname"
msgstr ""
#: tiramisu/option/domainnameoption.py:65
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:67
msgid "allow_cidr_network must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:69
msgid "allow_without_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:71
msgid "allow_startswith_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:81
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:82
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded"
msgstr ""
#: tiramisu/option/domainnameoption.py:84
#: tiramisu/option/domainnameoption.py:85
msgid "could be a IP, otherwise {}"
msgstr ""
#: tiramisu/option/domainnameoption.py:125
msgid "invalid length (min 1)"
msgstr ""
#: tiramisu/option/domainnameoption.py:127
msgid "invalid length (max {0})"
msgstr ""
#: tiramisu/option/domainnameoption.py:133
msgid "must have dot"
msgstr ""
#: tiramisu/option/domainnameoption.py:135
msgid "invalid length (max 255)"
msgstr ""
#: tiramisu/option/domainnameoption.py:154
msgid "must not be an IP"
msgstr ""
#: tiramisu/option/domainnameoption.py:180
msgid "some characters are uppercase"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:65
msgid "identifiers in dynoptiondescription has to be a calculation"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:109
msgid "invalid identifier \"{}\" for option \"{}\""
msgstr ""
#: tiramisu/option/emailoption.py:34
msgid "email address"
msgstr ""
#: tiramisu/option/filenameoption.py:31
msgid "file name"
msgstr ""
#: tiramisu/option/filenameoption.py:38
msgid "must starts with \"/\""
msgstr ""
#: tiramisu/option/floatoption.py:32
msgid "float"
msgstr ""
#: tiramisu/option/ipoption.py:33
msgid "IP"
msgstr ""
#: tiramisu/option/ipoption.py:57
msgid "it's in fact a network address"
msgstr ""
#: tiramisu/option/ipoption.py:59
msgid "it's in fact a broacast address"
msgstr ""
#: tiramisu/option/ipoption.py:72
msgid "CIDR address must have a \"/\""
msgstr ""
#: tiramisu/option/ipoption.py:83
msgid "shouldn't be reserved IP"
msgstr ""
#: tiramisu/option/ipoption.py:85
msgid "mustn't be reserved IP"
msgstr ""
#: tiramisu/option/ipoption.py:89
msgid "should be private IP"
msgstr ""
#: tiramisu/option/ipoption.py:91
msgid "must be private IP"
msgstr ""
#: tiramisu/option/leadership.py:56
msgid "a leader and a follower are mandatories in leadership \"{}\""
msgstr ""
#: tiramisu/option/leadership.py:73
msgid "leader cannot have \"{}\" property"
msgstr ""
#: tiramisu/option/leadership.py:77
msgid "leadership \"{0}\" shall not have a symlinkoption"
msgstr ""
#: tiramisu/option/leadership.py:80
msgid "leadership \"{0}\" shall not have a subgroup"
msgstr ""
#: tiramisu/option/leadership.py:83
msgid "only multi option allowed in leadership \"{0}\" but option \"{1}\" is not a multi"
msgstr ""
#: tiramisu/option/macoption.py:34
msgid "mac address"
msgstr ""
#: tiramisu/option/netmaskoption.py:32
msgid "netmask address"
msgstr ""
#: tiramisu/option/networkoption.py:32
msgid "network address"
msgstr ""
#: tiramisu/option/networkoption.py:51
msgid "must use CIDR notation"
msgstr ""
#: tiramisu/option/networkoption.py:68
msgid "shouldn't be reserved network"
msgstr ""
#: tiramisu/option/networkoption.py:70
msgid "mustn't be reserved network"
msgstr ""
#: tiramisu/option/option.py:69
msgid "default_multi is set whereas multi is False in option: {0}"
msgstr ""
#: tiramisu/option/option.py:86
msgid "invalid multi type \"{}\" for \"{}\""
msgstr ""
#: tiramisu/option/option.py:102
msgid "validators must be a Calculation for \"{}\""
msgstr ""
#: tiramisu/option/option.py:127
msgid "invalid default_multi value \"{0}\" for option \"{1}\""
msgstr ""
#: tiramisu/option/option.py:137
msgid "invalid default_multi value \"{0}\" for option \"{1}\", must be a list for a submulti"
msgstr ""
#: tiramisu/option/option.py:291
msgid "the value \"{}\" is not unique"
msgstr ""
#: tiramisu/option/option.py:331
msgid "which must not be a list"
msgstr ""
#: tiramisu/option/option.py:373 tiramisu/option/option.py:399
msgid "which must be a list"
msgstr ""
#: tiramisu/option/option.py:392
msgid "which \"{}\" must be a list of list"
msgstr ""
#: tiramisu/option/optiondescription.py:109
msgid "duplicate option: {0}"
msgstr ""
#: tiramisu/option/optiondescription.py:306
msgid "children in optiondescription \"{}\" must be a list"
msgstr ""
#: tiramisu/option/optiondescription.py:329
msgid "duplicate option name: \"{0}\""
msgstr ""
#: tiramisu/option/optiondescription.py:374
msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr ""
#: tiramisu/option/optiondescription.py:378
msgid "group_type: {0} not allowed"
msgstr ""
#: tiramisu/option/passwordoption.py:32
msgid "password"
msgstr ""
#: tiramisu/option/permissionsoption.py:38
msgid "unix file permissions"
msgstr ""
#: tiramisu/option/permissionsoption.py:52
msgid "only 3 or 4 octal digits are allowed"
msgstr ""
#: tiramisu/option/permissionsoption.py:65
msgid "user"
msgstr ""
#: tiramisu/option/permissionsoption.py:66
#: tiramisu/option/permissionsoption.py:68
msgid "group"
msgstr ""
#: tiramisu/option/permissionsoption.py:69
msgid "other"
msgstr ""
#: tiramisu/option/permissionsoption.py:73
msgid "too weak"
msgstr ""
#: tiramisu/option/portoption.py:41
msgid "port"
msgstr ""
#: tiramisu/option/portoption.py:71
msgid "inconsistency in allowed range"
msgstr ""
#: tiramisu/option/portoption.py:76
msgid "max value is empty"
msgstr ""
#: tiramisu/option/portoption.py:91
msgid "range must have two values only"
msgstr ""
#: tiramisu/option/portoption.py:93
msgid "first port in range must be smaller than the second one"
msgstr ""
#: tiramisu/option/stroption.py:33
msgid "string"
msgstr ""
#: tiramisu/option/symlinkoption.py:44
msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr ""
#: tiramisu/option/symlinkoption.py:60
msgid "cannot set symlinkoption in a dynoptiondescription"
msgstr ""
#: tiramisu/option/urloption.py:39
msgid "URL"
msgstr ""
#: tiramisu/option/urloption.py:91
msgid "must start with http:// or https://"
msgstr ""
#: tiramisu/option/urloption.py:122
msgid "must ends with a valid resource name"
msgstr ""
#: tiramisu/option/usernameoption.py:35
msgid "unix username"
msgstr ""
#: tiramisu/option/usernameoption.py:42
msgid "unix groupname"
msgstr ""
#: tiramisu/setting.py:302
msgid "can't rebind {0}"
msgstr ""
#: tiramisu/setting.py:308
msgid "can't unbind {0}"
msgstr ""
#: tiramisu/setting.py:515
msgid "invalid property type {type(new_prop)} for {option_bag.option.impl_getname()} with {prop.function.__name__} function"
msgstr ""
#: tiramisu/setting.py:521
msgid "leader cannot have \"{new_prop}\" property"
msgstr ""
#: tiramisu/setting.py:591
msgid "leader cannot have \"{list(not_allowed_properties)}\" property"
msgstr ""
#: tiramisu/setting.py:595
msgid "a leader ({opt.impl_get_display_name()}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\""
msgstr ""
#: tiramisu/setting.py:626
msgid "permissive must be a frozenset"
msgstr ""
#: tiramisu/setting.py:635
msgid "cannot add those permissives: {0}"
msgstr ""
#: tiramisu/todict.py:352
msgid "option {} only works when remotable is not \"none\""
msgstr ""
#: tiramisu/todict.py:505
msgid "unable to transform tiramisu object to dict: {}"
msgstr ""
#: tiramisu/todict.py:816 tiramisu/todict.py:955
msgid "unknown form {}"
msgstr ""
#: tiramisu/todict.py:862
msgid "not in current area"
msgstr ""
#: tiramisu/todict.py:883
msgid "only multi option can have action \"add\", but \"{}\" is not a multi"
msgstr ""
#: tiramisu/todict.py:885
msgid "unknown action {}"
msgstr ""
#: tiramisu/value.py:506 tiramisu/value.py:722
msgid "set owner \"{0}\" is forbidden"
msgstr ""
#: tiramisu/value.py:636
msgid "index {index} is greater than the length {length} for option {option_bag.option.impl_get_display_name(with_quote=True)}"
msgstr ""
#: tiramisu/value.py:695
msgid "information's item not found: {0}"
msgstr ""

View file

@ -19,12 +19,12 @@ from logging import getLogger, DEBUG, StreamHandler, Formatter
import os
log = getLogger('tiramisu')
if os.environ.get('TIRAMISU_DEBUG') == 'True': # pragma: no cover
log = getLogger("tiramisu")
if os.environ.get("TIRAMISU_DEBUG") == "True": # pragma: no cover
log.setLevel(DEBUG)
handler = StreamHandler()
handler.setLevel(DEBUG)
formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
formatter = Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
log.addHandler(handler)

View file

@ -25,7 +25,8 @@ from .dynoptiondescription import DynOptionDescription
from .leadership import Leadership
from .baseoption import submulti
from .symlinkoption import SymLinkOption
#from .syndynoption import SynDynOption, SynDynOptionDescription, SynDynLeadership
# from .syndynoption import SynDynOption, SynDynOptionDescription, SynDynLeadership
from .option import Option
from .choiceoption import ChoiceOption
from .booloption import BoolOption
@ -48,12 +49,33 @@ from .macoption import MACOption
from .permissionsoption import PermissionsOption
__all__ = ('Leadership', 'OptionDescription', 'DynOptionDescription',
# 'SynDynOptionDescription', 'SynDynLeadership','SynDynOption',
'Option', 'SymLinkOption',
'ChoiceOption', 'BoolOption', 'DateOption',
'IntOption', 'FloatOption', 'StrOption',
'IPOption', 'PortOption', 'NetworkOption', 'NetmaskOption',
'BroadcastOption', 'DomainnameOption', 'EmailOption', 'URLOption',
'UsernameOption', 'GroupnameOption', 'FilenameOption', 'PasswordOption', 'submulti',
'RegexpOption', 'MACOption', 'PermissionsOption')
__all__ = (
"Leadership",
"OptionDescription",
"DynOptionDescription",
# 'SynDynOptionDescription', 'SynDynLeadership','SynDynOption',
"Option",
"SymLinkOption",
"ChoiceOption",
"BoolOption",
"DateOption",
"IntOption",
"FloatOption",
"StrOption",
"IPOption",
"PortOption",
"NetworkOption",
"NetmaskOption",
"BroadcastOption",
"DomainnameOption",
"EmailOption",
"URLOption",
"UsernameOption",
"GroupnameOption",
"FilenameOption",
"PasswordOption",
"submulti",
"RegexpOption",
"MACOption",
"PermissionsOption",
)

View file

@ -36,39 +36,40 @@ submulti = 2
def valid_name(name):
"""valid option name
"""
"""valid option name"""
if not isinstance(name, str):
return False
if '.' in name:
if "." in name:
return False
return True
#____________________________________________________________
# ____________________________________________________________
#
class Base:
"""Base use by all *Option* classes (Option, OptionDescription, SymLinkOption, ...)
"""
__slots__ = ('_name',
'_path',
'_informations',
'_subdyns',
'_properties',
'_has_dependency',
'_dependencies',
'_dependencies_information',
'_identifiers_dependencies',
'__weakref__'
"""Base use by all *Option* classes (Option, OptionDescription, SymLinkOption, ...)"""
__slots__ = (
"_name",
"_path",
"_informations",
"_subdyns",
"_properties",
"_has_dependency",
"_dependencies",
"_dependencies_information",
"_identifiers_dependencies",
"__weakref__",
)
def __init__(self,
def __init__(
self,
name: str,
doc: str,
informations: Optional[Dict],
*,
properties=None,
is_multi: bool=False,
is_multi: bool = False,
) -> None:
if not valid_name(name):
raise ValueError(_('"{0}" is an invalid name for an option').format(name))
@ -80,88 +81,99 @@ class Base:
# if option is a multi, it cannot be 'empty' (None not allowed in the list)
# and cannot have multiple time the same value
# 'empty' and 'unique' are removed for follower's option
if 'notunique' not in properties:
properties = properties | {'unique'}
if 'notempty' not in properties:
properties = properties | {'empty'}
assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
' must be a frozenset').format(type(properties),
name)
if "notunique" not in properties:
properties = properties | {"unique"}
if "notempty" not in properties:
properties = properties | {"empty"}
assert isinstance(properties, frozenset), _(
"invalid properties type {0} for {1}," " must be a frozenset"
).format(type(properties), name)
_setattr = object.__setattr__
_setattr(self, '_name', name)
_setattr(self, '_informations', {'doc': doc})
_setattr(self, "_name", name)
_setattr(self, "_informations", {"doc": doc})
for prop in properties:
if not isinstance(prop, str):
if not isinstance(prop, Calculation):
raise ValueError(_('invalid property type {0} for {1}, must be a string or a '
'Calculation').format(type(prop), name))
raise ValueError(
_(
"invalid property type {0} for {1}, must be a string or a "
"Calculation"
).format(type(prop), name)
)
for param in chain(prop.params.args, prop.params.kwargs.values()):
if isinstance(param, ParamOption):
param.option._add_dependency(self)
if properties:
_setattr(self, '_properties', properties)
_setattr(self, "_properties", properties)
self.set_informations(informations)
def set_informations(self,
def set_informations(
self,
informations: Optional[Dict],
) -> None:
if not informations:
return
for key, value in informations.items():
self._set_information(key,
self._set_information(
key,
value,
)
def impl_has_dependency(self,
self_is_dep: bool=True,
def impl_has_dependency(
self,
self_is_dep: bool = True,
) -> bool:
"""this has dependency
"""
"""this has dependency"""
if self_is_dep is True:
return getattr(self, '_has_dependency', False)
return hasattr(self, '_dependencies')
return getattr(self, "_has_dependency", False)
return hasattr(self, "_dependencies")
def get_dependencies(self,
def get_dependencies(
self,
context_od,
) -> Set[str]:
ret = set(getattr(self, '_dependencies', STATIC_TUPLE))
if context_od and hasattr(context_od, '_dependencies'):
ret = set(getattr(self, "_dependencies", STATIC_TUPLE))
if context_od and hasattr(context_od, "_dependencies"):
# add options that have context is set in calculation
return set(context_od._dependencies) | ret # pylint: disable=protected-access
return (
set(context_od._dependencies) | ret
) # pylint: disable=protected-access
return ret
def _get_identifiers_dependencies(self) -> Set[str]:
return getattr(self, '_identifiers_dependencies', STATIC_TUPLE)
return getattr(self, "_identifiers_dependencies", STATIC_TUPLE)
def _add_dependency(self,
def _add_dependency(
self,
option,
is_identifier: bool=False,
is_identifier: bool = False,
) -> None:
woption = weakref.ref(option)
options = self.get_dependencies(None)
options.add(woption)
self._dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
self._dependencies = tuple(
options
) # pylint: disable=attribute-defined-outside-init
if is_identifier:
options = list(self._get_identifiers_dependencies())
options.append(woption)
self._identifiers_dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
self._identifiers_dependencies = tuple(
options
) # pylint: disable=attribute-defined-outside-init
def impl_is_optiondescription(self) -> bool:
"""option is an option description
"""
"""option is an option description"""
return False
def impl_is_dynoptiondescription(self) -> bool:
"""option is not a dyn option description
"""
"""option is not a dyn option description"""
return False
def impl_is_sub_dyn_optiondescription(self):
return False
def impl_getname(self) -> str:
"""get name
"""
"""get name"""
return self._name # pylint: disable=no-member
def _set_readonly(self) -> None:
@ -170,40 +182,39 @@ class Base:
dico = self._informations # pylint: disable=no-member
keys = tuple(dico.keys())
if len(keys) == 1:
dico = dico['doc']
dico = dico["doc"]
else:
dico = tuple([keys, tuple(dico.values())])
_setattr(self, '_informations', dico)
extra = getattr(self, '_extra', None)
_setattr(self, "_informations", dico)
extra = getattr(self, "_extra", None)
if extra is not None:
_setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())]))
_setattr(
self, "_extra", tuple([tuple(extra.keys()), tuple(extra.values())])
)
def impl_is_readonly(self) -> str:
"""the option is readonly
"""
return hasattr(self, '_path')
"""the option is readonly"""
return hasattr(self, "_path")
def impl_getproperties(self) -> FrozenSet[str]:
"""get properties
"""
return getattr(self, '_properties', frozenset())
"""get properties"""
return getattr(self, "_properties", frozenset())
def _setsubdyn(self,
def _setsubdyn(
self,
subdyn,
) -> None:
# pylint: disable=attribute-defined-outside-init
if getattr(self, '_subdyns', None) is None:
if getattr(self, "_subdyns", None) is None:
self._subdyns = []
self._subdyns.append(subdyn)
def issubdyn(self) -> bool:
"""is sub dynoption
"""
return getattr(self, '_subdyns', None) is not None
"""is sub dynoption"""
return getattr(self, "_subdyns", None) is not None
def getsubdyn(self):
"""get sub dynoption
"""
"""get sub dynoption"""
return self._subdyns[0]()
def get_sub_dyns(self):
@ -211,10 +222,11 @@ class Base:
# ____________________________________________________________
# information
def _get_information(self,
def _get_information(
self,
subconfig: "SubConfig",
key: str,
default: Any=undefined,
default: Any = undefined,
) -> Any:
"""retrieves one information's item
@ -225,7 +237,7 @@ class Base:
if key in dico[0]:
return dico[1][dico[0].index(key)]
elif isinstance(dico, str):
if key == 'doc':
if key == "doc":
return dico
elif isinstance(dico, dict):
if key in dico:
@ -233,10 +245,14 @@ class Base:
if default is not undefined:
return default
# pylint: disable=no-member
raise ValueError(_(f'information\'s item for {self.impl_get_display_name(subconfig, with_quote=True)} '
f'not found: "{key}"'))
raise ValueError(
_('information\'s item for {0} not found: "{1}"').format(
self.impl_get_display_name(subconfig, with_quote=True), key
)
)
def _set_information(self,
def _set_information(
self,
key: str,
value: Any,
) -> None:
@ -247,20 +263,20 @@ class Base:
:param value: information's value (ex: "the help string")
"""
if self.impl_is_readonly():
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format(self.__class__.__name__,
self,
key))
raise AttributeError(
_("'{0}' ({1}) object attribute '{2}' is" " read-only").format(
self.__class__.__name__, self, key
)
)
self._informations[key] = value # pylint: disable=no-member
def _list_information(self) -> Any:
"""get the list of information keys
"""
"""get the list of information keys"""
dico = self._informations # pylint: disable=no-member
if isinstance(dico, tuple):
return list(dico[0])
if not isinstance(dico, dict):
return ['doc']
return ["doc"]
# it's a dict
return list(dico.keys())
@ -270,9 +286,11 @@ class BaseOption(Base):
in options that have to be set only once, it is of course done in the
__setattr__ method
"""
__slots__ = ('_display_name_function',)
def __setattr__(self,
__slots__ = ("_display_name_function",)
def __setattr__(
self,
name: str,
value: Any,
) -> Any:
@ -286,72 +304,78 @@ class BaseOption(Base):
"""
# never change _name in an option or attribute when object is readonly
if self.impl_is_readonly():
raise AttributeError(_('"{}" ({}) object attribute "{}" is'
' read-only').format(self.__class__.__name__,
self.impl_get_display_name(None),
name))
raise AttributeError(
_('"{}" ({}) object attribute "{}" is' " read-only").format(
self.__class__.__name__, self.impl_get_display_name(None), name
)
)
super().__setattr__(name, value)
def impl_getpath(self) -> str:
"""get the path of the option
"""
"""get the path of the option"""
try:
return self._path
except AttributeError as err:
raise AttributeError(_(f'{self.impl_get_display_name(None, with_quote=True)} not part of any Config')) \
from err
raise AttributeError(
_("{0} not part of any Config").format(
self.impl_get_display_name(None, with_quote=True)
)
) from err
def impl_get_display_name(self,
def impl_get_display_name(
self,
subconfig: "SubConfig",
*,
with_quote: bool=False,
with_quote: bool = False,
) -> str:
"""get display name
"""
if hasattr(self, '_display_name_function'):
return self._display_name_function(self,
"""get display name"""
if hasattr(self, "_display_name_function"):
return self._display_name_function(
self,
subconfig,
with_quote=with_quote,
)
name = self._get_information(subconfig, 'doc', None)
if name is None or name == '':
name = self._get_information(subconfig, "doc", None)
if name is None or name == "":
if subconfig and subconfig.path:
name = subconfig.path.rsplit('.', 1)[-1]
name = subconfig.path.rsplit(".", 1)[-1]
else:
name = self._name
if with_quote:
return f'"{name}"'
return name
def reset_cache(self,
def reset_cache(
self,
path: str,
config_bag: 'OptionBag',
config_bag: "OptionBag",
resetted_opts: List[Base], # pylint: disable=unused-argument
) -> None:
"""reset cache
"""
"""reset cache"""
context = config_bag.context
context.properties_cache.delcache(path)
context._impl_permissives_cache.delcache(path) # pylint: disable=protected-access
context._impl_permissives_cache.delcache(
path
) # pylint: disable=protected-access
if not self.impl_is_optiondescription():
context.get_values_cache().delcache(path) # pylint: disable=protected-access
context.get_values_cache().delcache(
path
) # pylint: disable=protected-access
def impl_is_symlinkoption(self) -> bool:
"""the option is not a symlinkoption
"""
"""the option is not a symlinkoption"""
return False
def get_dependencies_information(self) -> List[str]:
"""get dependencies information
"""
return getattr(self, '_dependencies_information', {})
"""get dependencies information"""
return getattr(self, "_dependencies_information", {})
def value_dependencies(self,
def value_dependencies(
self,
value: Any,
is_identifier: bool=False,
is_identifier: bool = False,
) -> Any:
"""parse dependancies to add dependencies
"""
"""parse dependancies to add dependencies"""
if isinstance(value, list):
for val in value:
if isinstance(value, list):
@ -361,12 +385,12 @@ class BaseOption(Base):
elif isinstance(value, Calculation):
self.value_dependency(value, is_identifier)
def value_dependency(self,
def value_dependency(
self,
value: Any,
is_identifier: bool=False,
is_identifier: bool = False,
) -> Any:
"""parse dependancy to add dependencies
"""
"""parse dependancy to add dependencies"""
for param in chain(value.params.args, value.params.kwargs.values()):
if isinstance(param, ParamOption):
# pylint: disable=protected-access
@ -382,7 +406,9 @@ class BaseOption(Base):
else:
param.set_self_option(self)
opt = None
if not getattr(dest, '_dependencies_information', {}):
if not getattr(dest, "_dependencies_information", {}):
dest._dependencies_information = {None: []}
dest._dependencies_information[None].append(param)
dest._dependencies_information.setdefault(param.information_name, []).append(opt)
dest._dependencies_information.setdefault(
param.information_name, []
).append(opt)

View file

@ -26,15 +26,15 @@ from .option import Option
class BoolOption(Option):
"""represents a choice between ``True`` and ``False``
"""
__slots__ = tuple()
_type = 'boolean'
"""represents a choice between ``True`` and ``False``"""
def validate(self,
__slots__ = tuple()
_type = "boolean"
def validate(
self,
value: bool,
) -> None:
"""validate value
"""
"""validate value"""
if not isinstance(value, bool):
raise ValueError()

View file

@ -27,21 +27,21 @@ from .option import Option
class BroadcastOption(Option):
"""represents the choice of a broadcast
"""
__slots__ = tuple()
_type = 'broadcast address'
"""represents the choice of a broadcast"""
def validate(self,
__slots__ = tuple()
_type = "broadcast address"
def validate(
self,
value: str,
) -> None:
"""validate
"""
"""validate"""
if not isinstance(value, str):
raise ValueError(_('invalid string'))
if value.count('.') != 3:
raise ValueError(_("invalid string"))
if value.count(".") != 3:
raise ValueError()
for val in value.split('.'):
for val in value.split("."):
if val.startswith("0") and len(val) > 1:
raise ValueError()
try:

View file

@ -34,55 +34,53 @@ class ChoiceOption(Option):
The option can also have the value ``None``
"""
__slots__ = tuple()
_type = 'choice'
def __init__(self,
name,
doc,
values,
*args,
**kwargs):
_type = "choice"
def __init__(self, name, doc, values, *args, **kwargs):
"""
:param values: is a list of values the option can possibly take
"""
if not isinstance(values, (Calculation, tuple)):
raise TypeError(_('values must be a tuple or a calculation for {0}'
).format(name))
raise TypeError(
_("values must be a tuple or a calculation for {0}").format(name)
)
self._choice_values = values
super().__init__(name,
doc,
*args,
**kwargs)
super().__init__(name, doc, *args, **kwargs)
def impl_get_values(self,
def impl_get_values(
self,
subconfig: "SubConfig",
uncalculated: bool=False,
uncalculated: bool = False,
):
"""get values allowed by option
"""
"""get values allowed by option"""
choices = self._choice_values
if isinstance(choices, tuple):
choices = list(choices)
if uncalculated:
return choices
values = get_calculated_value(subconfig,
values = get_calculated_value(
subconfig,
choices,
)[0]
if values != undefined and not isinstance(values, (list, tuple)):
raise ConfigError(_('the calculated values "{0}" for "{1}" is not a list'
'').format(values, self.impl_getname()))
raise ConfigError(
_('the calculated values "{0}" for "{1}" is not a list' "").format(
values, self.impl_getname()
)
)
return values
def validate(self,
def validate(
self,
value: Any,
) -> None:
"""nothing to valide
"""
"""nothing to valide"""
def validate_with_option(self,
def validate_with_option(
self,
value: Any,
subconfig: "SubConfig",
loaded: bool,
@ -92,15 +90,17 @@ class ChoiceOption(Option):
values = self.impl_get_values(subconfig)
self.validate_values(value, values)
def validate_values(self,
def validate_values(
self,
value,
values,
) -> None:
"""validate values
"""
"""validate values"""
if values is not undefined and value not in values:
if len(values) == 1:
raise ValueError(_('only "{0}" is allowed'
'').format(values[0]))
raise ValueError(_('only {0} are allowed'
'').format(display_list(values, add_quote=True)))
raise ValueError(_('only "{0}" is allowed' "").format(values[0]))
raise ValueError(
_("only {0} are allowed" "").format(
display_list(values, add_quote=True)
)
)

View file

@ -27,13 +27,12 @@ from .stroption import StrOption
class DateOption(StrOption):
"""represents the choice of a date
"""
__slots__ = tuple()
_type = 'date'
"""represents the choice of a date"""
def validate(self,
value: str) -> None:
__slots__ = tuple()
_type = "date"
def validate(self, value: str) -> None:
super().validate(value)
try:
datetime.strptime(value, "%Y-%m-%d")

View file

@ -39,69 +39,84 @@ class DomainnameOption(StrOption):
domainname:
fqdn: with tld, not supported yet
"""
__slots__ = tuple()
_type = 'domain name'
def __init__(self,
__slots__ = tuple()
_type = "domain name"
def __init__(
self,
name: str,
doc: str,
*args,
allow_ip: bool=False,
allow_cidr_network: bool=False,
type: str='domainname',
allow_without_dot: bool=False,
allow_startswith_dot: bool=False,
allow_ip: bool = False,
allow_cidr_network: bool = False,
type: str = "domainname",
allow_without_dot: bool = False,
allow_startswith_dot: bool = False,
**kwargs,
) -> None:
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
if type not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type {0} for hostname').format(type))
extra = {'_dom_type': type}
if type not in ["netbios", "hostname", "domainname"]:
raise ValueError(_("unknown type {0} for hostname").format(type))
extra = {"_dom_type": type}
if not isinstance(allow_ip, bool):
raise ValueError(_('allow_ip must be a boolean'))
raise ValueError(_("allow_ip must be a boolean"))
if not isinstance(allow_cidr_network, bool):
raise ValueError(_('allow_cidr_network must be a boolean'))
raise ValueError(_("allow_cidr_network must be a boolean"))
if not isinstance(allow_without_dot, bool):
raise ValueError(_('allow_without_dot must be a boolean'))
raise ValueError(_("allow_without_dot must be a boolean"))
if not isinstance(allow_startswith_dot, bool):
raise ValueError(_('allow_startswith_dot must be a boolean'))
extra['_allow_without_dot'] = allow_without_dot
if type == 'domainname':
raise ValueError(_("allow_startswith_dot must be a boolean"))
extra["_allow_without_dot"] = allow_without_dot
if type == "domainname":
if allow_without_dot:
min_time = 0
else:
min_time = 1
regexp = r'((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}'.format(self._get_len(type), min_time)
regexp = r"((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}".format(
self._get_len(type), min_time
)
else:
regexp = r'((?!-)[a-z0-9-]{{1,{0}}})'.format(self._get_len(type))
msg = _('must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed')
msg_warning = _('must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded')
regexp = r"((?!-)[a-z0-9-]{{1,{0}}})".format(self._get_len(type))
msg = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
)
msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
)
if allow_ip:
msg = _('could be a IP, otherwise {}').format(msg)
msg_warning = _('could be a IP, otherwise {}').format(msg_warning)
msg = _("could be a IP, otherwise {}").format(msg)
msg_warning = _("could be a IP, otherwise {}").format(msg_warning)
if not allow_cidr_network:
regexp = r'(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))'.format(regexp)
regexp = r"(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))".format(
regexp
)
else:
regexp = r'(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[0-9][0-9]))'.format(regexp)
regexp = r'^{0}$'.format(regexp)
extra['_domain_re'] = re.compile(regexp)
extra['_domain_re_message'] = msg
extra['_domain_re_message_warning'] = msg_warning
extra['_has_upper'] = re.compile('[A-Z]')
regexp = r"(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[0-9][0-9]))".format(
regexp
)
regexp = r"^{0}$".format(regexp)
extra["_domain_re"] = re.compile(regexp)
extra["_domain_re_message"] = msg
extra["_domain_re_message_warning"] = msg_warning
extra["_has_upper"] = re.compile("[A-Z]")
if allow_ip:
extra['_ip'] = IPOption(name,
extra["_ip"] = IPOption(
name,
doc,
)
extra['_allow_ip'] = allow_ip
extra["_allow_ip"] = allow_ip
if allow_cidr_network:
extra['_network'] = NetworkOption(name,
extra["_network"] = NetworkOption(
name,
doc,
cidr=True,
)
extra['_allow_cidr_network'] = allow_cidr_network
extra['_allow_startswith_dot'] = allow_startswith_dot
extra["_allow_cidr_network"] = allow_cidr_network
extra["_allow_startswith_dot"] = allow_startswith_dot
super().__init__(name,
super().__init__(
name,
doc,
*args,
extra=extra,
@ -109,56 +124,54 @@ class DomainnameOption(StrOption):
)
def _get_len(self, type_):
if type_ == 'netbios':
if type_ == "netbios":
return 15
return 63
def _validate_domain(self,
value: str) -> None:
def _validate_domain(self, value: str) -> None:
def _valid_length(val):
if len(val) < 1:
raise ValueError(_("invalid length (min 1)"))
if len(val) > part_name_length:
raise ValueError(_("invalid length (max {0})"
"").format(part_name_length))
raise ValueError(
_("invalid length (max {0})" "").format(part_name_length)
)
part_name_length = self._get_len(self.impl_get_extra('_dom_type'))
if self.impl_get_extra('_dom_type') == 'domainname':
if not self.impl_get_extra('_allow_without_dot') and not "." in value:
part_name_length = self._get_len(self.impl_get_extra("_dom_type"))
if self.impl_get_extra("_dom_type") == "domainname":
if not self.impl_get_extra("_allow_without_dot") and not "." in value:
raise ValueError(_("must have dot"))
if len(value) > 255:
raise ValueError(_("invalid length (max 255)"))
if self.impl_get_extra('_allow_startswith_dot') and value.startswith('.'):
if self.impl_get_extra("_allow_startswith_dot") and value.startswith("."):
val = value[1:]
else:
val = value
if val.endswith('.'):
if val.endswith("."):
nval = val[:-1]
else:
nval = val
for dom in nval.split('.'):
for dom in nval.split("."):
_valid_length(dom)
else:
_valid_length(value)
def _validate_ip_network(self,
value: str) -> None:
allow_ip = self.impl_get_extra('_allow_ip')
allow_cidr_network = self.impl_get_extra('_allow_cidr_network')
def _validate_ip_network(self, value: str) -> None:
allow_ip = self.impl_get_extra("_allow_ip")
allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
if allow_ip is False and allow_cidr_network is False:
raise ValueError(_('must not be an IP'))
raise ValueError(_("must not be an IP"))
if allow_ip is True:
try:
self.impl_get_extra('_ip').validate(value)
self.impl_get_extra("_ip").validate(value)
return
except ValueError as err:
if allow_cidr_network is False:
raise err
if allow_cidr_network is True:
self.impl_get_extra('_network').validate(value)
self.impl_get_extra("_network").validate(value)
def validate(self,
value: str) -> None:
def validate(self, value: str) -> None:
super().validate(value)
try:
# check if it's an IP or network
@ -168,42 +181,40 @@ class DomainnameOption(StrOption):
else:
self._validate_ip_network(value)
def _second_level_validation_domain(self,
value: str,
warnings_only: bool) -> None:
if self.impl_get_extra('_has_upper').search(value):
raise ValueError(_('some characters are uppercase'))
if self.impl_get_extra('_allow_startswith_dot') and value.startswith('.'):
def _second_level_validation_domain(self, value: str, warnings_only: bool) -> None:
if self.impl_get_extra("_has_upper").search(value):
raise ValueError(_("some characters are uppercase"))
if self.impl_get_extra("_allow_startswith_dot") and value.startswith("."):
val = value[1:]
else:
val = value
if val.endswith('.'):
if val.endswith("."):
nval = val[:-1]
else:
nval = val
if not self.impl_get_extra('_domain_re').search(nval):
if not self.impl_get_extra("_domain_re").search(nval):
if warnings_only:
raise ValueError(self.impl_get_extra('_domain_re_message_warning'))
raise ValueError(self.impl_get_extra('_domain_re_message'))
raise ValueError(self.impl_get_extra("_domain_re_message_warning"))
raise ValueError(self.impl_get_extra("_domain_re_message"))
def _second_level_validation_ip_network(self,
value: str,
warnings_only: bool) -> None:
allow_ip = self.impl_get_extra('_allow_ip')
allow_cidr_network = self.impl_get_extra('_allow_cidr_network')
def _second_level_validation_ip_network(
self, value: str, warnings_only: bool
) -> None:
allow_ip = self.impl_get_extra("_allow_ip")
allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
# it's an IP so validate with IPOption
if allow_ip is True and allow_cidr_network is False:
try:
self.impl_get_extra('_ip').second_level_validation(value, warnings_only)
self.impl_get_extra("_ip").second_level_validation(value, warnings_only)
return
except ValueError as err:
raise err
if allow_cidr_network is True:
self.impl_get_extra('_network').second_level_validation(value, warnings_only)
self.impl_get_extra("_network").second_level_validation(
value, warnings_only
)
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
def second_level_validation(self, value: str, warnings_only: bool) -> None:
try:
# check if it's an IP or network
ip_interface(value)

View file

@ -35,15 +35,19 @@ from ..error import ConfigError
from ..autolib import Calculation, get_calculated_value
NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
NAME_REGEXP = re.compile(r"^[a-zA-Z\d\-_]*$")
class DynOptionDescription(OptionDescription):
"""dyn option description
"""
__slots__ = ('_identifiers',
'_subdyns',
"""dyn option description"""
__slots__ = (
"_identifiers",
"_subdyns",
)
def __init__(self,
def __init__(
self,
name: str,
doc: str,
children: List[BaseOption],
@ -51,7 +55,8 @@ class DynOptionDescription(OptionDescription):
**kwargs,
) -> None:
# pylint: disable=too-many-arguments
super().__init__(name,
super().__init__(
name,
doc,
children,
**kwargs,
@ -64,44 +69,45 @@ class DynOptionDescription(OptionDescription):
self.value_dependencies(identifiers, is_identifier=True)
self._identifiers = identifiers
def convert_identifier_to_path(self,
def convert_identifier_to_path(
self,
identifier: Any,
) -> str:
"""convert identifier to use it to a path
"""
"""convert identifier to use it to a path"""
if identifier is None:
return None
if not isinstance(identifier, str):
identifier = str(identifier)
if '.' in identifier:
identifier = identifier.replace('.', '_')
if "." in identifier:
identifier = identifier.replace(".", "_")
return identifier
def impl_is_dynoptiondescription(self) -> bool:
return True
def option_is_self(self,
def option_is_self(
self,
option,
) -> bool:
return option == self
def impl_getname(self, identifier=None) -> str:
"""get name
"""
"""get name"""
name = super().impl_getname()
if identifier is None:
return name
path_identifier = self.convert_identifier_to_path(identifier)
return name + path_identifier
def get_identifiers(self,
parent: 'SubConfig',
def get_identifiers(
self,
parent: "SubConfig",
*,
uncalculated: bool=False,
uncalculated: bool = False,
) -> List[str]:
"""get dynamic identifiers
"""
subconfig = parent.get_child(self,
"""get dynamic identifiers"""
subconfig = parent.get_child(
self,
None,
False,
properties=None,
@ -111,7 +117,8 @@ class DynOptionDescription(OptionDescription):
identifiers = identifiers.copy()
if uncalculated:
return identifiers
values = get_calculated_value(subconfig,
values = get_calculated_value(
subconfig,
identifiers,
validate_properties=False,
)[0]
@ -120,19 +127,28 @@ class DynOptionDescription(OptionDescription):
values_ = []
if __debug__:
if not isinstance(values, list):
raise ValueError(_('DynOptionDescription identifiers for '
f'option {self.impl_get_display_name(subconfig, with_quote=True)}, is not '
f'a list ({values})'))
raise ValueError(
_(
"DynOptionDescription identifiers for option {0}, is not a list ({1})"
).format(
self.impl_get_display_name(subconfig, with_quote=True), values
)
)
for val in values:
cval = self.convert_identifier_to_path(val)
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:
if __debug__ and cval is not None:
raise ValueError(_('invalid identifier "{}" for option {}'
'').format(cval,
self.impl_get_display_name(subconfig, with_quote=True)))
raise ValueError(
_('invalid identifier "{}" for option {}' "").format(
cval, self.impl_get_display_name(subconfig, with_quote=True)
)
)
else:
values_.append(val)
if __debug__ and len(values_) > len(set(values_)):
raise ValueError(_(f'DynOptionDescription "{self._name}" identifiers return a list with '
f'same values "{values_}"'''))
raise ValueError(
_(
'DynOptionDescription "{0}" identifiers return a list with same values "{1}"'
).format(self._name, values_)
)
return values_

View file

@ -27,8 +27,10 @@ from .stroption import RegexpOption
class EmailOption(RegexpOption):
"""represents a choice of an email
"""
"""represents a choice of an email"""
__slots__ = tuple()
_regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
_type = 'email address'
_regexp = re.compile(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"
)
_type = "email address"

View file

@ -28,45 +28,54 @@ from .stroption import StrOption
class FilenameOption(StrOption):
"""validate file or directory name
"""
__slots__ = tuple()
_type = 'file name'
"""validate file or directory name"""
def __init__(self,
__slots__ = tuple()
_type = "file name"
def __init__(
self,
name: str,
*args,
allow_relative=False,
test_existence=False,
types=['file', 'directory'],
**kwargs):
types=["file", "directory"],
**kwargs,
):
if not isinstance(types, list):
raise ValueError(_(f'types parameter must be a list, not "{types}" for "{name}"'))
raise ValueError(
_('types parameter must be a list, not "{0}" for "{1}"').format(
types, name
)
)
for typ in types:
if typ not in ['file', 'directory']:
if typ not in ["file", "directory"]:
raise ValueError(f'unknown type "{typ}" for "{name}"')
extra = {'_allow_relative': allow_relative,
'_test_existence': test_existence,
'_types': types,
extra = {
"_allow_relative": allow_relative,
"_test_existence": test_existence,
"_types": types,
}
super().__init__(name,
*args,
extra=extra,
**kwargs)
super().__init__(name, *args, extra=extra, **kwargs)
def validate(self,
def validate(
self,
value: str,
) -> None:
super().validate(value)
if not self.impl_get_extra('_allow_relative') and not value.startswith('/'):
if not self.impl_get_extra("_allow_relative") and not value.startswith("/"):
raise ValueError(_('must starts with "/"'))
if value is not None and self.impl_get_extra('_test_existence'):
types = self.impl_get_extra('_types')
if value is not None and self.impl_get_extra("_test_existence"):
types = self.impl_get_extra("_types")
file = Path(value)
found = False
if 'file' in types and file.is_file():
if "file" in types and file.is_file():
found = True
if not found and 'directory' in types and file.is_dir():
if not found and "directory" in types and file.is_dir():
found = True
if not found:
raise ValueError(_(f'cannot find {display_list(types, separator="or")} "{value}"'))
raise ValueError(
_('cannot find {0} "{1}"').format(
display_list(types, separator="or"), value
)
)

View file

@ -26,12 +26,11 @@ from .option import Option
class FloatOption(Option):
"""represents a choice of a floating point number
"""
__slots__ = tuple()
_type = 'float'
"""represents a choice of a floating point number"""
def validate(self,
value: float) -> None:
__slots__ = tuple()
_type = "float"
def validate(self, value: float) -> None:
if not isinstance(value, float):
raise ValueError()

View file

@ -28,40 +28,35 @@ from .option import Option
class IntOption(Option):
"represents a choice of an integer"
__slots__ = tuple()
_type = 'integer'
_type = "integer"
def __init__(self,
*args,
min_number=None,
max_number=None,
**kwargs):
def __init__(self, *args, min_number=None, max_number=None, **kwargs):
extra = {}
if min_number is not None:
extra['min_number'] = min_number
extra["min_number"] = min_number
if max_number is not None:
extra['max_number'] = max_number
extra["max_number"] = max_number
super().__init__(*args, extra=extra, **kwargs)
def validate(self,
def validate(
self,
value: int,
) -> None:
if not isinstance(value, int):
raise ValueError()
def second_level_validation(self,
value,
warnings_only):
min_number = self.impl_get_extra('min_number')
def second_level_validation(self, value, warnings_only):
min_number = self.impl_get_extra("min_number")
if min_number is not None and value < min_number:
if warnings_only:
msg = 'value should be equal or greater than "{0}"'
msg = _('value should be equal or greater than "{0}"')
else:
msg = 'value must be equal or greater than "{0}"'
raise ValueError(_(msg).format(min_number))
max_number = self.impl_get_extra('max_number')
msg = _('value must be equal or greater than "{0}"')
raise ValueError(msg.format(min_number))
max_number = self.impl_get_extra("max_number")
if max_number is not None and value > max_number:
if warnings_only:
msg = 'value should be less than "{0}"'
msg = _('value should be less than "{0}"')
else:
msg = 'value must be less than "{0}"'
raise ValueError(_(msg).format(max_number))
msg = _('value must be less than "{0}"')
raise ValueError(msg.format(max_number))

View file

@ -27,26 +27,26 @@ from .stroption import StrOption
class IPOption(StrOption):
"""represents the choice of an ip
"""
__slots__ = tuple()
_type = 'IP'
"""represents the choice of an ip"""
def __init__(self,
__slots__ = tuple()
_type = "IP"
def __init__(
self,
*args,
private_only=False,
allow_reserved=False,
cidr=False,
extra=None,
**kwargs):
**kwargs,
):
if extra is None:
extra = {}
extra['_private_only'] = private_only
extra['_allow_reserved'] = allow_reserved
extra['_cidr'] = cidr
super().__init__(*args,
extra=extra,
**kwargs)
extra["_private_only"] = private_only
extra["_allow_reserved"] = allow_reserved
extra["_cidr"] = cidr
super().__init__(*args, extra=extra, **kwargs)
def _validate_cidr(self, value):
try:
@ -64,27 +64,24 @@ class IPOption(StrOption):
except ValueError as err:
raise ValueError() from err
def validate(self,
value: str) -> None:
def validate(self, value: str) -> None:
super().validate(value)
if self.impl_get_extra('_cidr'):
if '/' not in value:
if self.impl_get_extra("_cidr"):
if "/" not in value:
raise ValueError(_('CIDR address must have a "/"'))
self._validate_cidr(value)
else:
self._validate_ip(value)
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
def second_level_validation(self, value: str, warnings_only: bool) -> None:
ip_obj = ip_interface(value)
if not self.impl_get_extra('_allow_reserved') and ip_obj.is_reserved:
if not self.impl_get_extra("_allow_reserved") and ip_obj.is_reserved:
if warnings_only:
msg = _("shouldn't be reserved IP")
else:
msg = _("mustn't be reserved IP")
raise ValueError(msg)
if self.impl_get_extra('_private_only') and not ip_obj.is_private:
if self.impl_get_extra("_private_only") and not ip_obj.is_private:
if warnings_only:
msg = _("should be private IP")
else:

View file

@ -26,6 +26,7 @@ from typing import List, Iterator, Optional
from ..i18n import _
from ..setting import groups, undefined, ALLOWED_LEADER_PROPERTIES
from .optiondescription import OptionDescription
# from .syndynoption import SynDynLeadership
from .baseoption import BaseOption
from .option import Option
@ -34,22 +35,27 @@ from ..autolib import Calculation
class Leadership(OptionDescription):
"""Leadership
"""
"""Leadership"""
# pylint: disable=too-many-arguments
__slots__ = ('leader',
'followers',
__slots__ = (
"leader",
"followers",
)
def __init__(self,
def __init__(
self,
name: str,
doc,
children: List[BaseOption],
**kwargs,
) -> None:
if 'group_type' in kwargs:
raise LeadershipError(_('cannot set "group_type" attribute for a Leadership'))
super().__init__(name,
if "group_type" in kwargs:
raise LeadershipError(
_('cannot set "group_type" attribute for a Leadership')
)
super().__init__(
name,
doc,
children,
**kwargs,
@ -57,8 +63,11 @@ class Leadership(OptionDescription):
self._group_type = groups.leadership
followers = []
if len(children) < 2:
raise ValueError(_('a leader and a follower are mandatories in leadership "{}"'
'').format(name))
raise ValueError(
_(
'a leader and a follower are mandatories in leadership "{}"' ""
).format(name)
)
for idx, child in enumerate(children):
if __debug__:
self._check_child_is_valid(child, idx, children)
@ -66,34 +75,51 @@ class Leadership(OptionDescription):
if __debug__:
self._check_default_value(child)
# remove empty property for follower
child._properties = frozenset(child._properties - {'empty', 'unique'})
child._properties = frozenset(child._properties - {"empty", "unique"})
followers.append(child)
child._add_dependency(self)
child._leadership = weakref.ref(self)
if __debug__:
leader = children[0]
for prop in leader.impl_getproperties():
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
raise LeadershipError(_('leader cannot have "{}" property').format(prop))
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(
prop, Calculation
):
raise LeadershipError(
_('leader cannot have "{}" property').format(prop)
)
def _check_child_is_valid(self,
def _check_child_is_valid(
self,
child: BaseOption,
index: int,
children: [BaseOption],
) -> None:
if child.impl_is_symlinkoption():
if not index:
raise ValueError(_('leadership {0} shall not have '
"a symlinkoption").format(self.impl_get_display_name(None, with_quote=True)))
raise ValueError(
_("leadership {0} shall not have " "a symlinkoption").format(
self.impl_get_display_name(None, with_quote=True)
)
)
return
if not isinstance(child, Option):
raise ValueError(_('leadership {0} shall not have '
'a subgroup').format(self.impl_get_display_name(None, with_quote=True)))
raise ValueError(
_("leadership {0} shall not have " "a subgroup").format(
self.impl_get_display_name(None, with_quote=True)
)
)
if not child.impl_is_multi():
raise ValueError(_('only multi option allowed in leadership {0} but option '
'{1} is not a multi'
'').format(self.impl_get_display_name(None, with_quote=True),
child.impl_get_display_name(None, with_quote=True)))
raise ValueError(
_(
"only multi option allowed in leadership {0} but option "
"{1} is not a multi"
""
).format(
self.impl_get_display_name(None, with_quote=True),
child.impl_get_display_name(None, with_quote=True),
)
)
def _check_default_value(self, child: BaseOption):
if child.impl_is_symlinkoption():
@ -111,78 +137,84 @@ class Leadership(OptionDescription):
else:
calculation = isinstance(default, Calculation)
if not calculation:
raise ValueError(_('not allowed default value for follower option '
f'{child.impl_get_display_name(None, with_quote=True)} in leadership '
f'{self.impl_get_display_name(None, with_quote=True)}'))
raise ValueError(
_(
"not allowed default value for follower option {0} in leadership {1}"
).format(
child.impl_get_display_name(None, with_quote=True),
self.impl_get_display_name(None, with_quote=True),
)
)
def _setsubdyn(self,
def _setsubdyn(
self,
subdyn,
) -> None:
for chld in self._children[1]:
chld._setsubdyn(subdyn)
super()._setsubdyn(subdyn)
def is_leader(self,
def is_leader(
self,
opt: Option,
) -> bool:
"""the option is the leader
"""
"""the option is the leader"""
leader = self.get_leader()
if opt.impl_is_dynsymlinkoption():
opt = opt.opt
return opt == leader
def get_leader(self) -> Option:
"""get leader
"""
"""get leader"""
return self._children[1][0]
def get_followers(self) -> Iterator[Option]:
"""get all followers
"""
"""get all followers"""
for follower in self._children[1][1:]:
yield follower
def in_same_leadership(self,
def in_same_leadership(
self,
opt: Option,
) -> bool:
"""check if followers are in same leadership
"""
"""check if followers are in same leadership"""
if opt.impl_is_dynsymlinkoption():
opt = opt.opt
return opt in self._children[1]
def reset(self, parent: "SubConfig") -> None:
"""reset follower value
"""
"""reset follower value"""
values = parent.config_bag.context.get_values()
for follower in self.get_followers():
subconfig_follower = parent.get_child(follower,
subconfig_follower = parent.get_child(
follower,
None,
False,
)
values.reset(subconfig_follower,
values.reset(
subconfig_follower,
validate=False,
)
def follower_force_store_value(self,
def follower_force_store_value(
self,
value,
subconfig: 'SubConfig',
subconfig: "SubConfig",
owner,
) -> None:
"""apply force_store_value to follower
"""
"""apply force_store_value to follower"""
if not value:
return
config_bag = subconfig.config_bag
values = config_bag.context.get_values()
for idx, follower in enumerate(self.get_children()):
sub_subconfig = subconfig.get_child(follower,
sub_subconfig = subconfig.get_child(
follower,
None,
False,
config_bag=config_bag,
)
if 'force_store_value' not in sub_subconfig.properties:
if "force_store_value" not in sub_subconfig.properties:
continue
self_path = sub_subconfig.path
if not idx:
@ -193,25 +225,27 @@ class Leadership(OptionDescription):
apply_requires = False
indexes = range(len(value))
for index in indexes:
i_sub_subconfig = subconfig.get_child(follower,
i_sub_subconfig = subconfig.get_child(
follower,
index,
False,
config_bag=config_bag,
)
values.set_storage_value(self_path,
values.set_storage_value(
self_path,
index,
values.get_value(i_sub_subconfig)[0],
owner,
)
def pop(self,
subconfig: 'SubConfig',
def pop(
self,
subconfig: "SubConfig",
index: int,
*,
followers: Optional[List[Option]]=undefined,
followers: Optional[List[Option]] = undefined,
) -> None:
"""pop leader value and follower's one
"""
"""pop leader value and follower's one"""
if followers is undefined:
# followers are not undefined only in SynDynLeadership
followers = self.get_followers()
@ -219,7 +253,8 @@ class Leadership(OptionDescription):
config_bag.remove_validation()
values = config_bag.context.get_values()
for follower in followers:
sub_subconfig = subconfig.parent.get_child(follower,
sub_subconfig = subconfig.parent.get_child(
follower,
index,
True,
properties=set(), # do not check force_default_on_freeze
@ -228,39 +263,45 @@ class Leadership(OptionDescription):
)
values.reduce_index(sub_subconfig)
def reset_cache(self,
def reset_cache(
self,
path: str,
config_bag: 'ConfigBag',
config_bag: "ConfigBag",
resetted_opts: List[Option],
) -> None:
self._reset_cache(path,
self._reset_cache(
path,
self.get_leader(),
self.get_followers(),
config_bag,
resetted_opts,
)
def _reset_cache(self,
def _reset_cache(
self,
path: str,
leader: Option,
followers: List[Option],
config_bag: 'ConfigBag',
config_bag: "ConfigBag",
resetted_opts: List[Option],
) -> None:
super().reset_cache(path,
super().reset_cache(
path,
config_bag,
resetted_opts,
)
leader_path = leader.impl_getpath()
if leader_path not in resetted_opts:
leader.reset_cache(leader_path,
leader.reset_cache(
leader_path,
config_bag,
resetted_opts,
)
for follower in followers:
follower_path = follower.impl_getpath()
if follower_path not in resetted_opts:
follower.reset_cache(follower_path,
follower.reset_cache(
follower_path,
config_bag,
resetted_opts,
)

View file

@ -27,8 +27,8 @@ from .stroption import RegexpOption
class MACOption(RegexpOption):
"""represents the choice of a mac address
"""
"""represents the choice of a mac address"""
__slots__ = tuple()
_regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$")
_type = 'mac address'
_type = "mac address"

View file

@ -26,18 +26,17 @@ from .stroption import StrOption
class NetmaskOption(StrOption):
"""represents the choice of a netmask
"""
__slots__ = tuple()
_type = 'netmask address'
"""represents the choice of a netmask"""
def validate(self,
value: str) -> None:
__slots__ = tuple()
_type = "netmask address"
def validate(self, value: str) -> None:
super().validate(value)
for val in value.split('.'):
for val in value.split("."):
if val.startswith("0") and len(val) > 1:
raise ValueError()
try:
ip_network(f'0.0.0.0/{value}')
ip_network(f"0.0.0.0/{value}")
except ValueError as err:
raise ValueError() from err

View file

@ -29,30 +29,24 @@ from .stroption import StrOption
class NetworkOption(StrOption):
"represents the choice of a network"
__slots__ = tuple()
_type = 'network address'
_type = "network address"
def __init__(self,
*args,
cidr=False,
**kwargs):
extra = {'_cidr': cidr}
super().__init__(*args,
extra=extra,
**kwargs)
def __init__(self, *args, cidr=False, **kwargs):
extra = {"_cidr": cidr}
super().__init__(*args, extra=extra, **kwargs)
def validate(self,
value: str) -> None:
def validate(self, value: str) -> None:
super().validate(value)
if value.count('.') != 3:
if value.count(".") != 3:
raise ValueError()
cidr = self.impl_get_extra('_cidr')
cidr = self.impl_get_extra("_cidr")
if cidr:
if '/' not in value:
raise ValueError(_('must use CIDR notation'))
value_ = value.split('/')[0]
if "/" not in value:
raise ValueError(_("must use CIDR notation"))
value_ = value.split("/")[0]
else:
value_ = value
for val in value_.split('.'):
for val in value_.split("."):
if val.startswith("0") and len(val) > 1:
raise ValueError()
try:
@ -60,9 +54,7 @@ class NetworkOption(StrOption):
except ValueError as err:
raise ValueError() from err
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
def second_level_validation(self, value: str, warnings_only: bool) -> None:
if ip_network(value).network_address.is_reserved:
if warnings_only:
msg = _("shouldn't be reserved network")

View file

@ -37,37 +37,43 @@ class Option(BaseOption):
Reminder: an Option object is **not** a container for the value.
"""
__slots__ = ('_extra',
'_warnings_only',
__slots__ = (
"_extra",
"_warnings_only",
# multi
'_multi',
"_multi",
# value
'_default',
'_default_multi',
"_default",
"_default_multi",
#
'_validators',
"_validators",
#
'_leadership',
'_choice_values',
'_choice_values_params',
"_leadership",
"_choice_values",
"_choice_values_params",
)
_type = None
def __init__(self,
def __init__(
self,
name: str,
doc: str,
default: Any=undefined,
default_multi: Any=None,
multi: bool=False,
validators: Optional[List[Calculation]]=None,
properties: Optional[List[str]]=None,
warnings_only: bool=False,
extra: Optional[Dict]=None,
informations: Optional[Dict]=None,
default: Any = undefined,
default_multi: Any = None,
multi: bool = False,
validators: Optional[List[Calculation]] = None,
properties: Optional[List[str]] = None,
warnings_only: bool = False,
extra: Optional[Dict] = None,
informations: Optional[Dict] = None,
):
_setattr = object.__setattr__
if not multi and default_multi is not None:
raise ValueError(_("default_multi is set whereas multi is False"
" in option: {0}").format(name))
raise ValueError(
_(
"default_multi is set whereas multi is False" " in option: {0}"
).format(name)
)
if default is undefined:
if multi is False:
default = None
@ -83,14 +89,18 @@ class Option(BaseOption):
is_multi = True
_multi = submulti
else:
raise ValueError(_('invalid multi type "{}" for "{}"').format(multi,
raise ValueError(
_('invalid multi type "{}" for "{}"').format(
multi,
name,
))
)
)
if _multi != 1:
_setattr(self, '_multi', _multi)
_setattr(self, "_multi", _multi)
if multi is not False and default is None:
default = []
super().__init__(name,
super().__init__(
name,
doc,
informations,
properties=properties,
@ -98,102 +108,121 @@ class Option(BaseOption):
)
if validators is not None:
if __debug__ and not isinstance(validators, list):
raise ValueError(_(f'validators must be a list of Calculation for "{name}"'))
raise ValueError(
_('validators must be a list of Calculation for "{0}"').format(name)
)
for validator in validators:
if __debug__ and not isinstance(validator, Calculation):
raise ValueError(_('validators must be a Calculation for "{}"').format(name))
raise ValueError(
_('validators must be a Calculation for "{0}"').format(name)
)
self.value_dependency(validator)
self._validators = tuple(validators)
if extra is not None and extra != {}:
_setattr(self, '_extra', extra)
_setattr(self, "_extra", extra)
if warnings_only is True:
_setattr(self, '_warnings_only', warnings_only)
_setattr(self, "_warnings_only", warnings_only)
if is_multi and default_multi is not None:
def test_multi_value(value):
if isinstance(value, Calculation):
return
# option_bag = OptionBag(self,
# None,
# undefined,
# properties=None,
# )
# option_bag = OptionBag(self,
# None,
# undefined,
# properties=None,
# )
try:
self.validate(value)
self.validate_with_option(value,
self.validate_with_option(
value,
None,
loaded=True,
)
except ValueError as err:
str_err = str(err)
if not str_err:
raise ValueError(_('invalid default_multi value "{0}" '
'for option {1}').format(str(value),
self.impl_get_display_name(None, with_quote=True))
raise ValueError(
_(
'invalid default_multi value "{0}" ' "for option {1}"
).format(
str(value),
self.impl_get_display_name(None, with_quote=True),
)
) from err
raise ValueError(_(f'invalid default_multi value "{value}" for option '
f'{self.impl_get_display_name(None, with_quote=True)}, {str_err}')
raise ValueError(
_(
'invalid default_multi value "{0}" for option {1}, {2}'
).format(
value,
self.impl_get_display_name(None, with_quote=True),
str_err,
)
) from err
if _multi is submulti:
if not isinstance(default_multi, Calculation):
if not isinstance(default_multi, list):
raise ValueError(_('invalid default_multi value "{0}" '
'for option {1}, must be a list for a submulti'
'').format(str(default_multi),
self.impl_get_display_name(None, with_quote=True)))
raise ValueError(
_(
'invalid default_multi value "{0}" '
"for option {1}, must be a list for a submulti"
""
).format(
str(default_multi),
self.impl_get_display_name(None, with_quote=True),
)
)
for value in default_multi:
test_multi_value(value)
else:
test_multi_value(default_multi)
_setattr(self, '_default_multi', default_multi)
# option_bag = OptionBag(self,
# None,
# undefined,
# properties=None,
# )
self.impl_validate(None,
_setattr(self, "_default_multi", default_multi)
# option_bag = OptionBag(self,
# None,
# undefined,
# properties=None,
# )
self.impl_validate(
None,
default,
loaded=True,
)
self.impl_validate(None,
self.impl_validate(
None,
default,
check_error=False,
loaded=True,
)
self.value_dependencies(default)
if (is_multi and default != []) or \
(not is_multi and default is not None):
if (is_multi and default != []) or (not is_multi and default is not None):
if is_multi and isinstance(default, list):
default = tuple(default)
_setattr(self, '_default', default)
_setattr(self, "_default", default)
#__________________________________________________________________________
# __________________________________________________________________________
# option's information
def impl_is_multi(self) -> bool:
"""is it a multi option
"""
return getattr(self, '_multi', 1) != 1
"""is it a multi option"""
return getattr(self, "_multi", 1) != 1
def impl_is_submulti(self) -> bool:
"""is it a submulti option
"""
return getattr(self, '_multi', 1) == 2
"""is it a submulti option"""
return getattr(self, "_multi", 1) == 2
def impl_is_dynsymlinkoption(self) -> bool:
"""is a dynsymlinkoption?
"""
"""is a dynsymlinkoption?"""
return False
def get_type(self) -> str:
"""get the type of option
"""
"""get the type of option"""
return self._type
def impl_getdefault(self) -> Any:
"""accessing the default value
"""
"""accessing the default value"""
is_multi = self.impl_is_multi()
default = getattr(self, '_default', undefined)
default = getattr(self, "_default", undefined)
if default is undefined:
if is_multi:
default = []
@ -204,72 +233,77 @@ class Option(BaseOption):
return default
def impl_getdefault_multi(self) -> Any:
"""accessing the default value for a multi
"""
"""accessing the default value for a multi"""
if self.impl_is_submulti():
default_value = []
else:
default_value = None
return getattr(self, '_default_multi', default_value)
return getattr(self, "_default_multi", default_value)
def impl_get_extra(self,
def impl_get_extra(
self,
key: str,
) -> Any:
"""if extra parameters are store get it
"""
extra = getattr(self, '_extra', {})
"""if extra parameters are store get it"""
extra = getattr(self, "_extra", {})
if isinstance(extra, tuple):
if key in extra[0]:
return extra[1][extra[0].index(key)]
return None
return extra.get(key)
#__________________________________________________________________________
# __________________________________________________________________________
# validator
def impl_validate(self,
def impl_validate(
self,
subconfig: Optional["SubConfig"],
value: Any,
*,
check_error: bool=True,
loaded: bool=False,
check_error: bool = True,
loaded: bool = False,
) -> bool:
"""Return True if value is really valid
If not validate or invalid return it returns False
"""
if check_error and subconfig and \
not 'validator' in subconfig.config_bag.properties:
if (
check_error
and subconfig
and not "validator" in subconfig.config_bag.properties
):
return False
if subconfig:
force_index = subconfig.index
else:
force_index = None
is_warnings_only = getattr(self, '_warnings_only', False)
is_warnings_only = getattr(self, "_warnings_only", False)
def _is_not_unique(value):
# if set(value) has not same length than value
if not subconfig or not check_error or \
'unique' not in subconfig.properties:
if not subconfig or not check_error or "unique" not in subconfig.properties:
return
lvalue = [val for val in value if val is not None]
if len(set(lvalue)) == len(lvalue):
return
for idx, val in enumerate(value):
if val not in value[idx+1:]:
if val not in value[idx + 1 :]:
continue
raise ValueError(_('the value "{}" is not unique'
'').format(val))
raise ValueError(_('the value "{}" is not unique' "").format(val))
def calculation_validator(val,
def calculation_validator(
val,
_index,
):
for validator in getattr(self, '_validators', []):
calc_is_warnings_only = hasattr(validator, 'warnings_only') and \
validator.warnings_only
if ((check_error and not calc_is_warnings_only) or
(not check_error and calc_is_warnings_only)):
for validator in getattr(self, "_validators", []):
calc_is_warnings_only = (
hasattr(validator, "warnings_only") and validator.warnings_only
)
if (check_error and not calc_is_warnings_only) or (
not check_error and calc_is_warnings_only
):
try:
kwargs = {'allow_value_error': True,
'force_value_warning': calc_is_warnings_only,
kwargs = {
"allow_value_error": True,
"force_value_warning": calc_is_warnings_only,
}
if _index is not None and subconfig.index == _index:
lsubconfig = subconfig
@ -277,21 +311,25 @@ class Option(BaseOption):
identifier = subconfig.identifiers
if identifier is not None:
identifier = identifier[-1]
lsubconfig = subconfig.parent.get_child(subconfig.option,
lsubconfig = subconfig.parent.get_child(
subconfig.option,
_index,
False,
properties=subconfig.properties,
identifier=identifier,
name=subconfig.path.rsplit('.', 1)[-1],
name=subconfig.path.rsplit(".", 1)[-1],
check_index=False,
)
kwargs['orig_value'] = value
kwargs["orig_value"] = value
validator.execute(lsubconfig,
validator.execute(
lsubconfig,
**kwargs,
)
except ValueWarning as warn:
warnings.warn_explicit(ValueWarning(subconfig,
warnings.warn_explicit(
ValueWarning(
subconfig,
val,
_(self.get_type()),
self,
@ -299,65 +337,80 @@ class Option(BaseOption):
_index,
),
ValueWarning,
self.__class__.__name__, 319)
self.__class__.__name__,
319,
)
def do_validation(_value,
def do_validation(
_value,
_index,
):
#
if _value is None:
return
if isinstance(_value, list):
raise ValueError(_('which must not be a list'))
raise ValueError(_("which must not be a list"))
if isinstance(_value, Calculation) and not subconfig:
return
# option validation
if check_error:
self.validate(_value)
self.validate_with_option(_value,
self.validate_with_option(
_value,
subconfig,
loaded=loaded,
)
# second level validation
if (check_error and not is_warnings_only) or (not check_error and is_warnings_only):
if (check_error and not is_warnings_only) or (
not check_error and is_warnings_only
):
try:
self.second_level_validation(_value,
is_warnings_only)
self.second_level_validation(_value, is_warnings_only)
except ValueError as err:
if is_warnings_only:
warnings.warn_explicit(ValueWarning(subconfig,
warnings.warn_explicit(
ValueWarning(
subconfig,
_value,
_(self.get_type()),
self,
str(err),
_index),
_index,
),
ValueWarning,
self.__class__.__name__, 0)
self.__class__.__name__,
0,
)
else:
raise err from err
# ?
if not loaded:
calculation_validator(_value,
calculation_validator(
_value,
_index,
)
val = value
err_index = force_index
try:
if not self.impl_is_multi():
do_validation(val,
do_validation(
val,
None,
)
elif force_index is not None:
if self.impl_is_submulti():
if not isinstance(value, list):
raise ValueError(_('which must be a list'))
raise ValueError(_("which must be a list"))
for val in value:
do_validation(val,
do_validation(
val,
force_index,
)
_is_not_unique(value)
else:
do_validation(val,
do_validation(
val,
force_index,
)
elif isinstance(value, Calculation) and not subconfig:
@ -367,83 +420,79 @@ class Option(BaseOption):
if isinstance(lval, Calculation):
continue
if not isinstance(lval, list):
raise ValueError(_('which "{}" must be a list of list'
'').format(lval))
raise ValueError(
_('which "{}" must be a list of list' "").format(lval)
)
for val in lval:
do_validation(val,
err_index)
do_validation(val, err_index)
_is_not_unique(lval)
elif not isinstance(value, list):
raise ValueError(_('which must be a list'))
raise ValueError(_("which must be a list"))
else:
# FIXME suboptimal, not several time for whole=True!
for err_index, val in enumerate(value):
do_validation(val,
do_validation(
val,
err_index,
)
_is_not_unique(value)
except ValueError as err:
if not subconfig or \
'demoting_error_warning' not in subconfig.config_bag.properties:
raise ValueOptionError(subconfig,
val,
_(self.get_type()),
self,
str(err),
err_index) from err
warnings.warn_explicit(ValueErrorWarning(subconfig,
val,
_(self.get_type()),
self,
str(err),
err_index),
if (
not subconfig
or "demoting_error_warning" not in subconfig.config_bag.properties
):
raise ValueOptionError(
subconfig, val, _(self.get_type()), self, str(err), err_index
) from err
warnings.warn_explicit(
ValueErrorWarning(
subconfig, val, _(self.get_type()), self, str(err), err_index
),
ValueErrorWarning,
self.__class__.__name__, 0)
self.__class__.__name__,
0,
)
return False
return True
def validate_with_option(self,
def validate_with_option(
self,
value: Any,
subconfig: "SubConfig",
*,
loaded: bool,
) -> None:
"""validation function with option
"""
"""validation function with option"""
def second_level_validation(self,
def second_level_validation(
self,
value: Any,
warnings_only: bool,
) -> None:
"""less import validation function
"""
"""less import validation function"""
def impl_is_leader(self):
"""check if option is a leader in a leadership
"""
"""check if option is a leader in a leadership"""
leadership = self.impl_get_leadership()
if leadership is None:
return False
return leadership.is_leader(self)
def impl_is_follower(self):
"""check if option is a leader in a follower
"""
"""check if option is a leader in a follower"""
leadership = self.impl_get_leadership()
if leadership is None:
return False
return not leadership.is_leader(self)
def impl_get_leadership(self):
"""get leadership
"""
leadership = getattr(self, '_leadership', None)
"""get leadership"""
leadership = getattr(self, "_leadership", None)
if leadership is None:
return leadership
#pylint: disable=not-callable
# pylint: disable=not-callable
return leadership()
def validate(self, value: Any):
"""option needs a validate function
"""
"""option needs a validate function"""
raise NotImplementedError()

View file

@ -27,33 +27,34 @@ from typing import Optional, Iterator, Union, List, Dict
from ..i18n import _
from ..setting import ConfigBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption
# from .syndynoption import SubDynOptionDescription, SynDynOptionDescription
from ..error import ConfigError, ConflictError
class CacheOptionDescription(BaseOption):
"""manage cache for option description
"""
__slots__ = ('_cache_force_store_values',
'_cache_dependencies_information',
"""manage cache for option description"""
__slots__ = (
"_cache_force_store_values",
"_cache_dependencies_information",
)
def impl_already_build_caches(self) -> bool:
"""is a readonly option?
"""
"""is a readonly option?"""
return self.impl_is_readonly()
def _build_cache(self,
def _build_cache(
self,
display_name,
_consistencies=None,
_consistencies_id=0,
currpath: List[str]=None,
currpath: List[str] = None,
cache_option=None,
force_store_values=None,
dependencies_information=None,
) -> None:
"""validate options and set option has readonly option
"""
"""validate options and set option has readonly option"""
# pylint: disable=too-many-branches,too-many-arguments
# _consistencies is None only when we start to build cache
if _consistencies is None:
@ -69,16 +70,18 @@ class CacheOptionDescription(BaseOption):
if self.impl_is_readonly():
# cache already set
raise ConfigError(_('option description seems to be part of an other '
'config'))
raise ConfigError(
_("option description seems to be part of an other " "config")
)
for option in self.get_children():
if __debug__:
cache_option.append(option)
sub_currpath = currpath + [option.impl_getname()]
subpath = '.'.join(sub_currpath)
subpath = ".".join(sub_currpath)
if isinstance(option, OptionDescription):
# pylint: disable=protected-access
option._build_cache(display_name,
option._build_cache(
display_name,
_consistencies,
_consistencies_id,
sub_currpath,
@ -91,78 +94,100 @@ class CacheOptionDescription(BaseOption):
if informations:
for param in informations.pop(None):
del param.self_option
for information, options in option.get_dependencies_information().items():
for (
information,
options,
) in option.get_dependencies_information().items():
if None in options:
dependencies_information.setdefault(information, []).append(option)
dependencies_information.setdefault(information, []).append(
option
)
properties = option.impl_getproperties()
if 'force_store_value' in properties:
if "force_store_value" in properties:
force_store_values.append(option)
if option.impl_is_readonly():
raise ConflictError(_('duplicate option: {0}').format(option))
raise ConflictError(_("duplicate option: {0}").format(option))
if not self.impl_is_readonly() and display_name:
option._display_name_function = display_name # pylint: disable=protected-access
option._display_name_function = (
display_name # pylint: disable=protected-access
)
option._path = subpath # pylint: disable=protected-access
option._set_readonly() # pylint: disable=protected-access
if init:
self._cache_force_store_values = force_store_values # pylint: disable=attribute-defined-outside-init
self._cache_force_store_values = (
force_store_values # pylint: disable=attribute-defined-outside-init
)
self._cache_dependencies_information = dependencies_information # pylint: disable=attribute-defined-outside-init
self._path = None # pylint: disable=attribute-defined-outside-init,no-member
self._path = (
None # pylint: disable=attribute-defined-outside-init,no-member
)
self._set_readonly()
def impl_build_force_store_values(self,
def impl_build_force_store_values(
self,
config_bag: ConfigBag,
) -> None:
"""set value to force_store_values option
"""
"""set value to force_store_values option"""
# pylint: disable=too-many-branches
context = config_bag.context
if 'force_store_value' not in config_bag.properties:
if "force_store_value" not in config_bag.properties:
return
values = config_bag.context.get_values()
for option in self._cache_force_store_values:
if option.issubdyn():
paths = option.impl_getpath().split('.')
paths = option.impl_getpath().split(".")
parents = [config_bag.context.get_root(config_bag)]
for name in paths:
new_parents = []
for parent in parents:
doption = parent.option.get_child(name,
doption = parent.option.get_child(
name,
config_bag,
parent,
allow_dynoption=True,
)
if doption.impl_is_dynoptiondescription():
new_parents.extend(parent.dyn_to_subconfig(doption,
new_parents.extend(
parent.dyn_to_subconfig(
doption,
True,
)
)
else:
new_parents.append(parent.get_child(doption,
new_parents.append(
parent.get_child(
doption,
None,
True,
name=name,
))
)
)
parents = new_parents
subconfigs = new_parents
else:
subconfigs = [context.get_sub_config(config_bag,
subconfigs = [
context.get_sub_config(
config_bag,
option.impl_getpath(),
None,
properties=None,
validate_properties=False,
)]
)
]
if option.impl_is_follower():
for follower_subconfig in subconfigs:
parent = follower_subconfig.parent
follower_len = parent.get_length_leadership()
for index in range(follower_len):
if values.hasvalue(follower_subconfig.path,
if values.hasvalue(
follower_subconfig.path,
index=index,
):
continue
idx_follower_subconfig = parent.get_child(follower_subconfig.option,
idx_follower_subconfig = parent.get_child(
follower_subconfig.option,
index,
validate_properties=False,
)
@ -170,7 +195,8 @@ class CacheOptionDescription(BaseOption):
value = values.get_value(idx_follower_subconfig)[0]
if value is None:
continue
values.set_storage_value(follower_subconfig.path,
values.set_storage_value(
follower_subconfig.path,
index,
value,
owners.forced,
@ -183,7 +209,8 @@ class CacheOptionDescription(BaseOption):
continue
if values.hasvalue(subconfig.path):
continue
values.set_storage_value(subconfig.path,
values.set_storage_value(
subconfig.path,
None,
value,
owners.forced,
@ -191,42 +218,48 @@ class CacheOptionDescription(BaseOption):
class OptionDescriptionWalk(CacheOptionDescription):
"""get child of option description
"""
__slots__ = ('_children',)
"""get child of option description"""
def get_path(self,
__slots__ = ("_children",)
def get_path(
self,
config_bag,
):
if config_bag is undefined or \
config_bag.context.get_description() == self:
return ''
if config_bag is undefined or config_bag.context.get_description() == self:
return ""
return self.impl_getpath()
def get_child_not_dynamic(self,
def get_child_not_dynamic(
self,
name,
allow_dynoption,
):
if name in self._children[0]: # pylint: disable=no-member
option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member
option = self._children[1][
self._children[0].index(name)
] # pylint: disable=no-member
if option.impl_is_dynoptiondescription() and not allow_dynoption:
raise AttributeError(_(f'unknown option "{name}" '
"in root optiondescription (it's a dynamic option)"
))
raise AttributeError(
_(
'unknown option "{0}" in root optiondescription (it\'s a dynamic option)'
).format(name)
)
return option
def get_child(self,
def get_child(
self,
name: str,
config_bag: ConfigBag,
parent: 'SubConfig',
parent: "SubConfig",
*,
with_identifier: bool=False,
allow_dynoption: bool=False,
with_identifier: bool = False,
allow_dynoption: bool = False,
) -> Union[BaseOption]:
"""get a child
"""
"""get a child"""
# if not dyn
option = self.get_child_not_dynamic(name,
option = self.get_child_not_dynamic(
name,
allow_dynoption,
)
if option:
@ -242,39 +275,42 @@ class OptionDescriptionWalk(CacheOptionDescription):
return child
return identifier, child
if self.impl_get_group_type() == groups.root: # pylint: disable=no-member
raise AttributeError(_(f'unknown option "{name}" '
'in root optiondescription'
))
raise AttributeError(_(f'unknown option "{name}" '
f'in optiondescription {self.impl_get_display_name(parent, with_quote=True)}'
))
raise AttributeError(
_('unknown option "{0}" in root optiondescription').format(name)
)
raise AttributeError(
_('unknown option "{0}" in optiondescription {1}').format(
name, self.impl_get_display_name(parent, with_quote=True)
)
)
def get_children(self) -> List[BaseOption]:
"""get children
"""
"""get children"""
return self._children[1]
def get_children_recursively(self,
def get_children_recursively(
self,
bytype: Optional[BaseOption],
byname: Optional[str],
config_bag: ConfigBag,
self_opt: BaseOption=None,
self_opt: BaseOption = None,
*,
option_identifiers: Optional[list]=None
option_identifiers: Optional[list] = None,
) -> Iterator[Union[BaseOption]]:
"""get children recursively
"""
"""get children recursively"""
if self_opt is None:
self_opt = self
for option in self_opt.get_children():
if option.impl_is_optiondescription():
for subopt in option.get_children_recursively(bytype,
for subopt in option.get_children_recursively(
bytype,
byname,
config_bag,
):
yield subopt
elif (byname is None or option.impl_getname() == byname) and \
(bytype is None or isinstance(option, bytype)):
elif (byname is None or option.impl_getname() == byname) and (
bytype is None or isinstance(option, bytype)
):
yield option
@ -282,24 +318,28 @@ class OptionDescription(OptionDescriptionWalk):
"""Config's schema (organisation, group) and container of Options
The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
"""
__slots__ = ('_group_type',)
def __init__(self,
__slots__ = ("_group_type",)
def __init__(
self,
name: str,
doc: str,
children: List[BaseOption],
*,
properties=None,
informations: Optional[Dict]=None,
group_type: Optional[groups.GroupType]=groups.default,
informations: Optional[Dict] = None,
group_type: Optional[groups.GroupType] = groups.default,
) -> None:
"""
:param children: a list of options (including optiondescriptions)
"""
assert isinstance(children, list), _('children in optiondescription "{}" '
'must be a list').format(name)
super().__init__(name,
assert isinstance(children, list), _(
'children in optiondescription "{}" ' "must be a list"
).format(name)
super().__init__(
name,
doc,
informations,
properties=properties,
@ -322,20 +362,25 @@ class OptionDescription(OptionDescriptionWalk):
old = None
for child in child_names:
if child == old:
raise ConflictError(_('duplicate option name: '
'"{0}"').format(child))
raise ConflictError(
_("duplicate option name: " '"{0}"').format(child)
)
if dynopt_names:
for dynopt in dynopt_names:
if child != dynopt and child.startswith(dynopt):
raise ConflictError(_(f'the option\'s name "{child}" start as '
f'the dynoptiondescription\'s name "{dynopt}"'))
raise ConflictError(
_(
'the option\'s name "{0}" start as the dynoptiondescription\'s name "{1}"'
).format(child, dynopt)
)
old = child
self._children = children_
# the group_type is useful for filtering OptionDescriptions in a config
self._group_type = None
self.impl_set_group_type(group_type)
def _setsubdyn(self,
def _setsubdyn(
self,
subdyn,
) -> None:
for child in self._children[1]:
@ -343,22 +388,20 @@ class OptionDescription(OptionDescriptionWalk):
super()._setsubdyn(subdyn)
def impl_is_optiondescription(self) -> bool:
"""the option is an option description
"""
"""the option is an option description"""
return True
def impl_is_dynoptiondescription(self) -> bool:
"""the option is not dynamic
"""
"""the option is not dynamic"""
return False
def impl_is_leadership(self) -> bool:
"""the option is not a leadership
"""
"""the option is not a leadership"""
return False
# ____________________________________________________________
def impl_set_group_type(self,
def impl_set_group_type(
self,
group_type: groups.GroupType,
) -> None:
"""sets a given group object to an OptionDescription
@ -368,22 +411,23 @@ class OptionDescription(OptionDescriptionWalk):
"""
if __debug__:
if self._group_type is not None and self._group_type != groups.default:
raise ValueError(_('cannot change group_type if already set '
'(old {0}, new {1})').format(self._group_type,
group_type))
raise ValueError(
_(
"cannot change group_type if already set " "(old {0}, new {1})"
).format(self._group_type, group_type)
)
if not isinstance(group_type, groups.GroupType):
raise ValueError(_('group_type: {0}'
' not allowed').format(group_type))
raise ValueError(_("group_type: {0}" " not allowed").format(group_type))
if isinstance(group_type, groups.LeadershipGroupType):
raise ConfigError('please use Leadership object instead of OptionDescription')
raise ConfigError(
"please use Leadership object instead of OptionDescription"
)
self._group_type = group_type
def impl_get_group_type(self) -> groups.GroupType:
"""get the group type of option description
"""
"""get the group type of option description"""
return self._group_type
def impl_is_dynsymlinkoption(self) -> bool:
"""option is not a dyn symlink option
"""
"""option is not a dyn symlink option"""
return False

View file

@ -26,7 +26,7 @@ from .stroption import StrOption
class PasswordOption(StrOption):
"""represents the choice of a password
"""
"""represents the choice of a password"""
__slots__ = tuple()
_type = 'password'
_type = "password"

View file

@ -33,27 +33,25 @@ class PermissionsOption(IntOption):
If a fourth digit is present to the setuid bit, the setgid bit and the sticky bit attributes.
This option is an integer value.
"""
__slots__ = tuple()
perm_re = re.compile(r"^[0-7]{3,4}$")
_type = 'unix file permissions'
_type = "unix file permissions"
def __init__(self,
def __init__(
self,
*args,
**kwargs,
) -> None:
#do not display intoption attributs
super().__init__(*args,
**kwargs)
# do not display intoption attributs
super().__init__(*args, **kwargs)
def validate(self,
value: str) -> None:
def validate(self, value: str) -> None:
super().validate(value)
if not self.perm_re.search(str(value)):
raise ValueError(_('only 3 or 4 octal digits are allowed'))
raise ValueError(_("only 3 or 4 octal digits are allowed"))
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
def second_level_validation(self, value: str, warnings_only: bool) -> None:
old_digit = 7
str_value = str(value)
if len(str_value) == 4:
@ -62,12 +60,12 @@ class PermissionsOption(IntOption):
new_digit = int(digit)
if old_digit < new_digit:
if idx == 1:
old = _('user')
new = _('group')
old = _("user")
new = _("group")
else:
old = _('group')
new = _('other')
raise ValueError(_(f'{new} has more right than {old}'))
old = _("group")
new = _("other")
raise ValueError(_("{0} has more right than {1}").format(new, old))
old_digit = new_digit
if str_value == '777':
raise ValueError(_('too weak'))
if str_value == "777":
raise ValueError(_("too weak"))

View file

@ -36,62 +36,64 @@ class PortOption(StrOption):
Port number 0 is reserved and can't be used.
see: http://en.wikipedia.org/wiki/Port_numbers
"""
__slots__ = tuple()
port_re = re.compile(r"^[0-9]*$")
_type = 'port'
_type = "port"
def __init__(self,
def __init__(
self,
*args,
allow_range: bool=False,
allow_zero: bool=False,
allow_wellknown: bool=True,
allow_registred: bool=True,
allow_protocol: bool=False,
allow_private: bool=False,
**kwargs) -> None:
allow_range: bool = False,
allow_zero: bool = False,
allow_wellknown: bool = True,
allow_registred: bool = True,
allow_protocol: bool = False,
allow_private: bool = False,
**kwargs,
) -> None:
extra = {'_allow_range': allow_range,
'_allow_protocol': allow_protocol,
'_min_value': None,
'_max_value': None,
extra = {
"_allow_range": allow_range,
"_allow_protocol": allow_protocol,
"_min_value": None,
"_max_value": None,
}
ports_min = [0, 1, 1024, 49152]
ports_max = [0, 1023, 49151, 65535]
is_finally = False
for index, allowed in enumerate([allow_zero,
allow_wellknown,
allow_registred,
allow_private]):
if extra['_min_value'] is None:
for index, allowed in enumerate(
[allow_zero, allow_wellknown, allow_registred, allow_private]
):
if extra["_min_value"] is None:
if allowed:
extra['_min_value'] = ports_min[index]
extra["_min_value"] = ports_min[index]
elif not allowed:
is_finally = True
elif allowed and is_finally:
raise ValueError(_('inconsistency in allowed range'))
raise ValueError(_("inconsistency in allowed range"))
if allowed:
extra['_max_value'] = ports_max[index]
extra["_max_value"] = ports_max[index]
if extra['_max_value'] is None:
raise ValueError(_('max value is empty'))
if extra["_max_value"] is None:
raise ValueError(_("max value is empty"))
super().__init__(*args,
extra=extra,
**kwargs)
super().__init__(*args, extra=extra, **kwargs)
def validate(self,
value: str) -> None:
def validate(self, value: str) -> None:
super().validate(value)
if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or
value.startswith('udp:')):
if self.impl_get_extra("_allow_protocol") and (
value.startswith("tcp:") or value.startswith("udp:")
):
value = [value[4:]]
elif self.impl_get_extra('_allow_range') and ":" in str(value):
value = value.split(':')
elif self.impl_get_extra("_allow_range") and ":" in str(value):
value = value.split(":")
if len(value) != 2:
raise ValueError(_('range must have two values only'))
raise ValueError(_("range must have two values only"))
if not value[0] < value[1]:
raise ValueError(_('first port in range must be'
' smaller than the second one'))
raise ValueError(
_("first port in range must be" " smaller than the second one")
)
else:
value = [value]
@ -99,22 +101,29 @@ class PortOption(StrOption):
if not self.port_re.search(val):
raise ValueError()
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or
value.startswith('udp:')):
def second_level_validation(self, value: str, warnings_only: bool) -> None:
if self.impl_get_extra("_allow_protocol") and (
value.startswith("tcp:") or value.startswith("udp:")
):
value = [value[4:]]
elif ':' in value:
value = value.split(':')
elif ":" in value:
value = value.split(":")
else:
value = [value]
for val in value:
val = int(val)
if not self.impl_get_extra('_min_value') <= val <= self.impl_get_extra('_max_value'):
if (
not self.impl_get_extra("_min_value")
<= val
<= self.impl_get_extra("_max_value")
):
if warnings_only:
msg = 'should be between {0} and {1}'
msg = _("should be between {0} and {1}")
else:
msg = 'must be between {0} and {1}'
raise ValueError(_(msg).format(self.impl_get_extra('_min_value'),
self.impl_get_extra('_max_value')))
msg = _("must be between {0} and {1}")
raise ValueError(
msg.format(
self.impl_get_extra("_min_value"),
self.impl_get_extra("_max_value"),
)
)

View file

@ -27,31 +27,31 @@ from .option import Option
class StrOption(Option):
"""represents a string
"""
__slots__ = tuple()
_type = 'string'
"""represents a string"""
def validate(self,
__slots__ = tuple()
_type = "string"
def validate(
self,
value: str,
) -> None:
"""validation
"""
"""validation"""
if not isinstance(value, str):
raise ValueError()
class RegexpOption(StrOption):
"""regexp validation, this is base option use to do a custom's one
"""
"""regexp validation, this is base option use to do a custom's one"""
__slots__ = tuple()
def validate(self,
def validate(
self,
value: Any,
) -> None:
# pylint: disable=no-member
"""validation
"""
"""validation"""
super().validate(value)
match = self._regexp.search(value)
if not match:

View file

@ -27,61 +27,66 @@ from ..i18n import _
class SymLinkOption(BaseOption):
"""SymLinkOption link to an other option
"""
__slots__ = ('_opt',
'_leadership',
"""SymLinkOption link to an other option"""
__slots__ = (
"_opt",
"_leadership",
)
def __init__(self,
def __init__(
self,
name: str,
opt: BaseOption,
) -> None:
# pylint: disable=super-init-not-called
if not valid_name(name):
raise ValueError(_('"{0}" is an invalid name for an option').format(name))
if not isinstance(opt, BaseOption) or \
opt.impl_is_optiondescription() or \
opt.impl_is_symlinkoption():
raise ValueError(_(f'malformed symlink second parameters must be an option for "{name}", not {opt}'))
if (
not isinstance(opt, BaseOption)
or opt.impl_is_optiondescription()
or opt.impl_is_symlinkoption()
):
raise ValueError(
_(
'malformed symlink second parameters must be an option for "{0}", not {1}'
).format(name, opt)
)
self._name = name
self._opt = opt
self._leadership = None
opt._add_dependency(self)
def __getattr__(self,
def __getattr__(
self,
name: str,
) -> Any:
if name == '_subdyns':
if name == "_subdyns":
return None
if name == '_path':
if name == "_path":
raise AttributeError()
return getattr(self._opt, name)
def impl_is_symlinkoption(self) -> bool:
"""it's a symlinkoption
"""
"""it's a symlinkoption"""
return True
def impl_is_leader(self) -> bool:
return False
def impl_is_follower(self):
"""check if option is a leader in a follower
"""
"""check if option is a leader in a follower"""
leadership = self._leadership
if leadership is None:
return False
return not leadership().is_leader(self)
def impl_getopt(self) -> BaseOption:
"""get to linked option
"""
"""get to linked option"""
return self._opt
def impl_is_multi(self) -> bool:
"""is it a multi?
"""
"""is it a multi?"""
if self._opt.impl_is_multi():
return True
if self._opt.issubdyn() or self.issubdyn():
@ -91,6 +96,5 @@ class SymLinkOption(BaseOption):
return False
def impl_is_submulti(self) -> bool:
"""is it a submulti?
"""
"""is it a submulti?"""
return self._opt.impl_is_submulti()

View file

@ -32,93 +32,97 @@ from .portoption import PortOption
class URLOption(StrOption):
"""URLOption to check url value
"""
"""URLOption to check url value"""
__slots__ = tuple()
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
_type = 'URL'
_type = "URL"
def __init__(self,
def __init__(
self,
name: str,
doc: str,
*args,
allow_ip: bool=False,
type: str='domainname',
allow_ip: bool = False,
type: str = "domainname",
allow_without_dot=False,
allow_range: bool=False,
allow_zero: bool=False,
allow_wellknown: bool=True,
allow_registred: bool=True,
allow_private: bool=False,
allow_range: bool = False,
allow_zero: bool = False,
allow_wellknown: bool = True,
allow_registred: bool = True,
allow_private: bool = False,
**kwargs,
) -> None:
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
extra = {'_domainname': DomainnameOption(name,
extra = {
"_domainname": DomainnameOption(
name,
doc,
allow_ip=allow_ip,
type=type,
allow_without_dot=allow_without_dot),
'_port': PortOption(name,
allow_without_dot=allow_without_dot,
),
"_port": PortOption(
name,
doc,
allow_range=allow_range,
allow_zero=allow_zero,
allow_wellknown=allow_wellknown,
allow_registred=allow_registred,
allow_private=allow_private)}
super().__init__(name,
allow_private=allow_private,
),
}
super().__init__(
name,
doc,
extra=extra,
*args,
**kwargs,
)
def _get_domain_port_files(self,
value: str) -> (str, str):
if value.startswith('http://'):
type_ = 'http'
def _get_domain_port_files(self, value: str) -> (str, str):
if value.startswith("http://"):
type_ = "http"
value = value[7:]
elif value.startswith('https://'):
type_ = 'https'
elif value.startswith("https://"):
type_ = "https"
value = value[8:]
else:
raise ValueError(_('must start with http:// or '
'https://'))
raise ValueError(_("must start with http:// or " "https://"))
# get domain/files
splitted = value.split('/', 1)
splitted = value.split("/", 1)
if len(splitted) == 1:
domain = value
files = None
else:
domain, files = splitted
# if port in domain
splitted = domain.split(':', 1)
splitted = domain.split(":", 1)
if len(splitted) == 1:
domain = splitted[0]
port = {'http': '80',
'https': '443'}[type_]
port = {"http": "80", "https": "443"}[type_]
else:
domain, port = splitted
return domain, port, files
def validate(self,
value: str) -> None:
def validate(self, value: str) -> None:
super().validate(value)
domain, port, files = self._get_domain_port_files(value)
# validate port
portoption = self.impl_get_extra('_port')
portoption = self.impl_get_extra("_port")
portoption.validate(port)
# validate domainname
domainnameoption = self.impl_get_extra('_domainname')
domainnameoption = self.impl_get_extra("_domainname")
domainnameoption.validate(domain)
# validate files
if files is not None and files != '' and not self.path_re.search(files):
raise ValueError(_('must ends with a valid resource name'))
if files is not None and files != "" and not self.path_re.search(files):
raise ValueError(_("must ends with a valid resource name"))
def second_level_validation(self, value, warnings_only):
domain, port, _ = self._get_domain_port_files(value)
# validate port
portoption = self.impl_get_extra('_port')
portoption = self.impl_get_extra("_port")
portoption.second_level_validation(port, warnings_only)
# validate domainname
domainnameoption = self.impl_get_extra('_domainname')
domainnameoption = self.impl_get_extra("_domainname")
domainnameoption.second_level_validation(domain, warnings_only)

View file

@ -27,16 +27,16 @@ from .stroption import RegexpOption
class UsernameOption(RegexpOption):
"""UsernameOption to check unix username value
"""
"""UsernameOption to check unix username value"""
__slots__ = tuple()
#regexp build with 'man 8 adduser' informations
# regexp build with 'man 8 adduser' informations
_regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
_type = 'unix username'
_type = "unix username"
class GroupnameOption(UsernameOption):
"""GroupnameOption to check unix group value
"""
"""GroupnameOption to check unix group value"""
__slots__ = tuple()
_type = 'unix groupname'
_type = "unix groupname"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -26,14 +26,17 @@ class Values:
"""This class manage value (default value, stored value or calculated value
It's also responsible of a caching utility.
"""
# pylint: disable=too-many-public-methods
__slots__ = ('_values',
'_informations',
'__weakref__',
__slots__ = (
"_values",
"_informations",
"__weakref__",
)
def __init__(self,
default_values: Union[None, dict]=None,
def __init__(
self,
default_values: Union[None, dict] = None,
) -> None:
"""
Initializes the values's dict.
@ -47,9 +50,10 @@ class Values:
default_values = {None: {None: [None, owners.user]}}
self._values = default_values
#______________________________________________________________________
# ______________________________________________________________________
# get value
def get_cached_value(self,
def get_cached_value(
self,
subconfig: "SubConfig",
) -> Any:
"""get value directly in cache if set
@ -60,26 +64,30 @@ class Values:
# try to retrive value in cache
setting_properties = subconfig.config_bag.properties
cache = subconfig.config_bag.context.get_values_cache()
is_cached, value, validated = cache.getcache(subconfig,
'values',
is_cached, value, validated = cache.getcache(
subconfig,
"values",
)
# no cached value so get value
if not is_cached:
value, has_calculation = self.get_value(subconfig)
# validates and warns value
if not validated:
validate = subconfig.option.impl_validate(subconfig,
validate = subconfig.option.impl_validate(
subconfig,
value,
check_error=True,
)
if 'warnings' in setting_properties:
subconfig.option.impl_validate(subconfig,
if "warnings" in setting_properties:
subconfig.option.impl_validate(
subconfig,
value,
check_error=False,
)
# set value to cache
if not is_cached and not has_calculation:
cache.setcache(subconfig,
cache.setcache(
subconfig,
value,
validated=validate,
)
@ -89,7 +97,8 @@ class Values:
# and return it
return value
def get_value(self,
def get_value(
self,
subconfig: "SubConfig",
) -> Any:
"""actually retrieves the stored value or the default value (value modified by user)
@ -98,20 +107,27 @@ class Values:
"""
# get owner and value from store
default_value = [undefined, owners.default]
value, owner = self._values.get(subconfig.path, {}).get(subconfig.index, default_value)
if owner == owners.default or \
('frozen' in subconfig.properties and \
('force_default_on_freeze' in subconfig.properties or \
self.check_force_to_metaconfig(subconfig))):
value, owner = self._values.get(subconfig.path, {}).get(
subconfig.index, default_value
)
if owner == owners.default or (
"frozen" in subconfig.properties
and (
"force_default_on_freeze" in subconfig.properties
or self.check_force_to_metaconfig(subconfig)
)
):
# the value is a default value
# get it
value = self.get_default_value(subconfig)
value, has_calculation = get_calculated_value(subconfig,
value, has_calculation = get_calculated_value(
subconfig,
value,
)
return value, has_calculation
def get_default_value(self,
def get_default_value(
self,
subconfig: "SubConfig",
) -> Any:
"""get default value:
@ -122,15 +138,24 @@ class Values:
msubconfig = self._get_modified_parent(subconfig)
if msubconfig is not None:
# retrieved value from parent config
return msubconfig.config_bag.context.get_values().get_cached_value(msubconfig)
return msubconfig.config_bag.context.get_values().get_cached_value(
msubconfig
)
# now try to get calculated value:
value, _has_calculation = get_calculated_value(subconfig,
value, _has_calculation = get_calculated_value(
subconfig,
subconfig.option.impl_getdefault(),
)
if subconfig.index is not None and isinstance(value, (list, tuple)) \
and (not subconfig.option.impl_is_submulti() or \
not value or isinstance(value[0], list)):
if (
subconfig.index is not None
and isinstance(value, (list, tuple))
and (
not subconfig.option.impl_is_submulti()
or not value
or isinstance(value[0], list)
)
):
# if index (so slave), must return good value for this index
# for submulti, first index is a list, assume other data are list too
index = subconfig.index
@ -139,44 +164,48 @@ class Values:
else:
# no value for this index, retrieve default multi value
# default_multi is already a list for submulti
value, _has_calculation = get_calculated_value(subconfig,
value, _has_calculation = get_calculated_value(
subconfig,
subconfig.option.impl_getdefault_multi(),
)
self.reset_cache_after_calculation(subconfig,
self.reset_cache_after_calculation(
subconfig,
value,
)
return value
#______________________________________________________________________
def check_force_to_metaconfig(self,
# ______________________________________________________________________
def check_force_to_metaconfig(
self,
subconfig: "OptionBag",
) -> bool:
"""Check if the value must be retrieve from parent metaconfig or not
"""
"""Check if the value must be retrieve from parent metaconfig or not"""
# force_metaconfig_on_freeze is set to an option and context is a kernelconfig
# => to metaconfig
# force_metaconfig_on_freeze is set *explicitly* to an option and context is a
# kernelmetaconfig => to sub metaconfig
if 'force_metaconfig_on_freeze' in subconfig.properties:
if "force_metaconfig_on_freeze" in subconfig.properties:
settings = subconfig.config_bag.context.get_settings()
if subconfig.config_bag.context.impl_type == 'config':
if subconfig.config_bag.context.impl_type == "config":
return True
# it's a not a config, force to metaconfig only in *explicitly* set
return 'force_metaconfig_on_freeze' in settings.get_stored_properties(subconfig.path,
return "force_metaconfig_on_freeze" in settings.get_stored_properties(
subconfig.path,
subconfig.index,
frozenset(),
)
return False
def reset_cache_after_calculation(self,
def reset_cache_after_calculation(
self,
subconfig,
value,
):
"""if value is modification after calculation, invalid cache
"""
"""if value is modification after calculation, invalid cache"""
cache = subconfig.config_bag.context.get_values_cache()
is_cache, cache_value, _ = cache.getcache(subconfig,
'values',
is_cache, cache_value, _ = cache.getcache(
subconfig,
"values",
expiration=False,
)
if not is_cache or cache_value == value:
@ -186,17 +215,18 @@ class Values:
# calculated value is a new value, so reset cache
subconfig.config_bag.context.reset_cache(subconfig)
# and manage force_store_value
self._set_force_value_identifier(subconfig,
self._set_force_value_identifier(
subconfig,
value,
)
def isempty(self,
def isempty(
self,
subconfig: "SubConfig",
value: Any,
force_allow_empty_list: bool,
) -> bool:
"""convenience method to know if an option is empty
"""
"""convenience method to know if an option is empty"""
index = subconfig.index
option = subconfig.option
if index is None and option.impl_is_submulti():
@ -206,134 +236,158 @@ class Values:
isempty = self._isempty_multi(val, force_allow_empty_list)
if isempty:
break
elif (index is None or \
(index is not None and option.impl_is_submulti())) and \
option.impl_is_multi():
elif (
index is None or (index is not None and option.impl_is_submulti())
) and option.impl_is_multi():
# it's a single list
isempty = self._isempty_multi(value, force_allow_empty_list)
else:
isempty = value is None or value == ''
isempty = value is None or value == ""
return isempty
def _isempty_multi(self,
def _isempty_multi(
self,
value: Any,
force_allow_empty_list: bool,
) -> bool:
if not isinstance(value, list):
return False
return (not force_allow_empty_list and value == []) or None in value or '' in value
return (
(not force_allow_empty_list and value == []) or None in value or "" in value
)
#______________________________________________________________________
# ______________________________________________________________________
# set value
def set_value(self,
def set_value(
self,
subconfig: "SubConfig",
value: Any,
) -> None:
"""set value to option
"""
"""set value to option"""
owner = self.get_context_owner()
setting_properties = subconfig.config_bag.properties
ori_value = value
if 'validator' in setting_properties:
value, has_calculation = self.setvalue_validation(subconfig,
if "validator" in setting_properties:
value, has_calculation = self.setvalue_validation(
subconfig,
value,
)
elif isinstance(value, list):
# copy
value = value.copy()
self._setvalue(subconfig,
self._setvalue(
subconfig,
ori_value,
owner,
)
if 'force_store_value' in setting_properties and subconfig.option.impl_is_leader():
if (
"force_store_value" in setting_properties
and subconfig.option.impl_is_leader()
):
leader = subconfig.option.impl_get_leadership()
parent = subconfig.parent
parent._length = len(value)
leader.follower_force_store_value(value,
leader.follower_force_store_value(
value,
parent,
owners.forced,
)
validator = 'validator' in setting_properties and \
'demoting_error_warning' not in setting_properties
validator = (
"validator" in setting_properties
and "demoting_error_warning" not in setting_properties
)
if validator and not has_calculation:
cache = subconfig.config_bag.context.get_values_cache()
cache.setcache(subconfig,
cache.setcache(
subconfig,
value,
validated=validator,
)
elif 'validator' in setting_properties and has_calculation:
elif "validator" in setting_properties and has_calculation:
cache = subconfig.config_bag.context.get_values_cache()
cache.delcache(subconfig.path)
def setvalue_validation(self,
def setvalue_validation(
self,
subconfig: "SubConfig",
value: Any,
):
"""validate value before set value
"""
"""validate value before set value"""
settings = subconfig.config_bag.context.get_settings()
# First validate properties with this value
opt = subconfig.option
settings.validate_frozen(subconfig)
val, has_calculation = get_calculated_value(subconfig,
val, has_calculation = get_calculated_value(
subconfig,
value,
)
settings.validate_mandatory(subconfig,
settings.validate_mandatory(
subconfig,
val,
)
# Value must be valid for option
opt.impl_validate(subconfig,
opt.impl_validate(
subconfig,
val,
check_error=True,
)
if 'warnings' in subconfig.config_bag.properties:
if "warnings" in subconfig.config_bag.properties:
# No error found so emit warnings
opt.impl_validate(subconfig,
opt.impl_validate(
subconfig,
val,
check_error=False,
)
return val, has_calculation
def _setvalue(self,
def _setvalue(
self,
subconfig: "SubConfig",
value: Any,
owner: str,
) -> None:
subconfig.config_bag.context.reset_cache(subconfig)
self.set_storage_value(subconfig.path,
self.set_storage_value(
subconfig.path,
subconfig.index,
value,
owner,
)
self._set_force_value_identifier(subconfig,
self._set_force_value_identifier(
subconfig,
value,
)
def set_storage_value(self,
def set_storage_value(
self,
path,
index,
value,
owner,
):
"""set a value
"""
"""set a value"""
self._values.setdefault(path, {})[index] = [value, owner]
def _set_force_value_identifier(self,
subconfig: 'SubConfig',
def _set_force_value_identifier(
self,
subconfig: "SubConfig",
identifier_values,
) -> None:
""" force store value for an option for identifiers
"""
"""force store value for an option for identifiers"""
# pylint: disable=too-many-locals
if 'force_store_value' not in subconfig.config_bag.properties:
if "force_store_value" not in subconfig.config_bag.properties:
return
config_bag = subconfig.config_bag
context = config_bag.context
for woption in subconfig.option._get_identifiers_dependencies(): # pylint: disable=protected-access
options = subconfig.get_common_child(woption(),
for (
woption
) in (
subconfig.option._get_identifiers_dependencies()
): # pylint: disable=protected-access
options = subconfig.get_common_child(
woption(),
true_path=subconfig.path,
validate_properties=False,
)
@ -343,29 +397,38 @@ class Values:
parent = option.parent
for identifier in identifier_values:
name = option.option.impl_getname(identifier)
opt_subconfig = parent.get_child(option.option,
opt_subconfig = parent.get_child(
option.option,
None,
False,
identifier=identifier,
name=name,
)
for walk_subconfig in context.walk(opt_subconfig,
for walk_subconfig in context.walk(
opt_subconfig,
no_value=True,
validate_properties=False,
):
if 'force_store_value' not in walk_subconfig.properties:
if "force_store_value" not in walk_subconfig.properties:
continue
default_value = [self.get_value(walk_subconfig)[0], owners.forced]
self._values.setdefault(walk_subconfig.path, {})[walk_subconfig.index] = default_value
default_value = [
self.get_value(walk_subconfig)[0],
owners.forced,
]
self._values.setdefault(walk_subconfig.path, {})[
walk_subconfig.index
] = default_value
def _get_modified_parent(self,
def _get_modified_parent(
self,
subconfig: "SubConfig",
) -> Optional["SubConfig"]:
""" Search in differents parents a Config with a modified value
"""Search in differents parents a Config with a modified value
If not found, return None
For follower option, return the Config where leader is modified
"""
def build_option_bag(subconfig, parent):
doption_bag = subconfig.copy()
config_bag = subconfig.config_bag.copy()
@ -376,17 +439,20 @@ class Values:
for parent in subconfig.config_bag.context.get_parents():
doption_bag = build_option_bag(subconfig, parent)
if 'force_metaconfig_on_freeze' in subconfig.properties:
if "force_metaconfig_on_freeze" in subconfig.properties:
# remove force_metaconfig_on_freeze only if option in metaconfig
# hasn't force_metaconfig_on_freeze properties
ori_properties = doption_bag.properties
settings = doption_bag.config_bag.context.get_settings()
doption_bag.properties = settings.getproperties(doption_bag)
if not self.check_force_to_metaconfig(doption_bag):
doption_bag.properties = ori_properties - {'force_metaconfig_on_freeze'}
doption_bag.properties = ori_properties - {
"force_metaconfig_on_freeze"
}
else:
doption_bag.properties = ori_properties
parent_owner = parent.get_values().getowner(doption_bag,
parent_owner = parent.get_values().getowner(
doption_bag,
parent,
only_default=True,
)
@ -395,23 +461,27 @@ class Values:
return None
#______________________________________________________________________
# ______________________________________________________________________
# owner
def is_default_owner(self,
def is_default_owner(
self,
subconfig: "SubConfig",
*,
validate_meta: bool=True,
validate_meta: bool = True,
) -> bool:
"""is default owner for an option
"""
return self.getowner(subconfig,
"""is default owner for an option"""
return (
self.getowner(
subconfig,
validate_meta=validate_meta,
only_default=True,
) == owners.default
)
== owners.default
)
def hasvalue(self,
def hasvalue(
self,
path,
*,
index=None,
@ -426,7 +496,8 @@ class Values:
return index in self._values[path]
return False
def getowner(self,
def getowner(
self,
subconfig: "SubConfig",
*,
validate_meta=True,
@ -440,38 +511,46 @@ class Values:
was present
:returns: a `setting.owners.Owner` object
"""
# context = subconfig.config_bag.context
# settings = context.get_settings()
# settings.validate_properties(subconfig)
if 'frozen' in subconfig.properties and \
'force_default_on_freeze' in subconfig.properties:
# context = subconfig.config_bag.context
# settings = context.get_settings()
# settings.validate_properties(subconfig)
if (
"frozen" in subconfig.properties
and "force_default_on_freeze" in subconfig.properties
):
return owners.default
if only_default:
if self.hasvalue(subconfig.path,
if self.hasvalue(
subconfig.path,
index=subconfig.index,
):
owner = 'not_default'
owner = "not_default"
else:
owner = owners.default
else:
owner = self._values.get(subconfig.path, {}).get(subconfig.index,
owner = self._values.get(subconfig.path, {}).get(
subconfig.index,
[undefined, owners.default],
)[1]
if validate_meta is not False and (owner is owners.default or
'frozen' in subconfig.properties and
'force_metaconfig_on_freeze' in subconfig.properties):
if validate_meta is not False and (
owner is owners.default
or "frozen" in subconfig.properties
and "force_metaconfig_on_freeze" in subconfig.properties
):
msubconfig = self._get_modified_parent(subconfig)
if msubconfig is not None:
values = msubconfig.config_bag.context.get_values()
owner = values.getowner(msubconfig,
owner = values.getowner(
msubconfig,
parent,
only_default=only_default,
)
elif 'force_metaconfig_on_freeze' in subconfig.properties:
elif "force_metaconfig_on_freeze" in subconfig.properties:
return owners.default
return owner
def set_owner(self,
def set_owner(
self,
subconfig,
owner,
):
@ -484,33 +563,40 @@ class Values:
if owner in forbidden_owners:
raise ValueError(_('set owner "{0}" is forbidden').format(str(owner)))
if not self.hasvalue(subconfig.path,
if not self.hasvalue(
subconfig.path,
index=subconfig.index,
):
raise ConfigError(_(f'"{subconfig.path}" is a default value, so we cannot change owner to "{owner}"'))
raise ConfigError(
_(
'"{0}" is a default value, so we cannot change owner to "{1}"'
).format(subconfig.path, owner)
)
subconfig.config_bag.context.get_settings().validate_frozen(subconfig)
self._values[subconfig.path][subconfig.index][1] = owner
#______________________________________________________________________
# ______________________________________________________________________
# reset
def reset(self,
def reset(
self,
subconfig: "SubConfig",
*,
validate: bool=True,
validate: bool = True,
) -> None:
"""reset value for an option
"""
"""reset value for an option"""
config_bag = subconfig.config_bag
hasvalue = self.hasvalue(subconfig.path)
context = config_bag.context
setting_properties = config_bag.properties
if validate:
if hasvalue and 'validator' in setting_properties:
if hasvalue and "validator" in setting_properties:
fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation()
fake_config_bag.context = fake_context
fake_subconfig = fake_context.get_sub_config(fake_config_bag,
fake_subconfig = fake_context.get_sub_config(
fake_config_bag,
subconfig.path,
subconfig.index,
validate_properties=False,
@ -519,18 +605,22 @@ class Values:
fake_values.reset(fake_subconfig)
fake_subconfig.config_bag.properties = setting_properties
value = fake_values.get_default_value(fake_subconfig)
fake_values.setvalue_validation(fake_subconfig,
fake_values.setvalue_validation(
fake_subconfig,
value,
)
# if hasvalue:
# if hasvalue:
opt = subconfig.option
if opt.impl_is_leader():
opt.impl_get_leadership().reset(subconfig.parent)
if 'force_store_value' in setting_properties and \
'force_store_value' in subconfig.properties:
if (
"force_store_value" in setting_properties
and "force_store_value" in subconfig.properties
):
value = self.get_default_value(subconfig)
self._setvalue(subconfig,
self._setvalue(
subconfig,
value,
owners.forced,
)
@ -538,44 +628,50 @@ class Values:
value = None
if subconfig.path in self._values:
del self._values[subconfig.path]
if 'force_store_value' in setting_properties and subconfig.option.impl_is_leader():
if (
"force_store_value" in setting_properties
and subconfig.option.impl_is_leader()
):
if value is None:
value = self.get_default_value(subconfig)
leader = subconfig.option.impl_get_leadership()
leader.follower_force_store_value(value,
leader.follower_force_store_value(
value,
subconfig.parent,
owners.forced,
)
context.reset_cache(subconfig)
#______________________________________________________________________
# ______________________________________________________________________
# Follower
def get_max_length(self, path: str) -> int:
"""get max index for a follower and determine the length of the follower
"""
"""get max index for a follower and determine the length of the follower"""
values = self._values.get(path, {})
if values:
return max(values) + 1
return 0
def reset_follower(self,
def reset_follower(
self,
subconfig: "SubConfig",
) -> None:
"""reset value for a follower
"""
if not self.hasvalue(subconfig.path,
"""reset value for a follower"""
if not self.hasvalue(
subconfig.path,
index=subconfig.index,
):
return
config_bag = subconfig.config_bag
context = config_bag.context
setting_properties = config_bag.properties
if 'validator' in setting_properties:
if "validator" in setting_properties:
fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation()
fake_config_bag.context = fake_context
fake_subconfig = fake_context.get_sub_config(fake_config_bag,
fake_subconfig = fake_context.get_sub_config(
fake_config_bag,
subconfig.path,
subconfig.index,
validate_properties=False,
@ -584,15 +680,20 @@ class Values:
fake_values.reset_follower(fake_subconfig)
fake_subconfig.config_bag.properties = setting_properties
value = fake_values.get_default_value(fake_subconfig)
fake_values.setvalue_validation(fake_subconfig,
fake_values.setvalue_validation(
fake_subconfig,
value,
)
if 'force_store_value' in setting_properties and \
'force_store_value' in subconfig.properties:
value = self.get_default_value(subconfig,
if (
"force_store_value" in setting_properties
and "force_store_value" in subconfig.properties
):
value = self.get_default_value(
subconfig,
)
self._setvalue(subconfig,
self._setvalue(
subconfig,
value,
owners.forced,
)
@ -600,50 +701,63 @@ class Values:
self.resetvalue_index(subconfig)
context.reset_cache(subconfig)
def resetvalue_index(self,
def resetvalue_index(
self,
subconfig: "SubConfig",
) -> None:
"""reset a value for a follower at an index
"""
if subconfig.path in self._values and subconfig.index in self._values[subconfig.path]:
"""reset a value for a follower at an index"""
if (
subconfig.path in self._values
and subconfig.index in self._values[subconfig.path]
):
del self._values[subconfig.path][subconfig.index]
def reduce_index(self,
def reduce_index(
self,
subconfig: "SubConfig",
) -> None:
"""reduce follower's value from a specified index
"""
"""reduce follower's value from a specified index"""
self.resetvalue_index(subconfig)
for index in range(subconfig.index + 1, self.get_max_length(subconfig.path)):
if self.hasvalue(subconfig.path,
if self.hasvalue(
subconfig.path,
index=index,
):
self._values[subconfig.path][index - 1] = self._values[subconfig.path].pop(index)
self._values[subconfig.path][index - 1] = self._values[
subconfig.path
].pop(index)
def reset_leadership(self,
def reset_leadership(
self,
subconfig: "SubConfig",
index: int,
) -> None:
"""reset leadership from an index
"""
"""reset leadership from an index"""
current_value = self.get_cached_value(subconfig)
length = len(current_value)
if index >= length:
raise IndexError(_('index {index} is greater than the length {length} '
'for option {subconfig.option.impl_get_display_name(with_quote=True)}'))
raise IndexError(
_(
"index {index} is greater than the length {length} "
"for option {subconfig.option.impl_get_display_name(with_quote=True)}"
)
)
current_value.pop(index)
leadership_subconfig = subconfig.parent
leadership_subconfig.option.pop(subconfig,
leadership_subconfig.option.pop(
subconfig,
index,
)
self.set_value(subconfig,
self.set_value(
subconfig,
current_value,
)
#______________________________________________________________________
# ______________________________________________________________________
# information
def set_information(self,
def set_information(
self,
subconfig,
key,
value,
@ -670,21 +784,26 @@ class Values:
continue
option = woption()
if option.issubdyn():
option_subconfigs = subconfig.get_common_child(option,
option_subconfigs = subconfig.get_common_child(
option,
validate_properties=False,
)
if not isinstance(option_subconfigs, list):
option_subconfigs = [option_subconfigs]
else:
option_subconfigs = [context.get_sub_config(config_bag,
option_subconfigs = [
context.get_sub_config(
config_bag,
option.impl_getpath(),
None,
validate_properties=False,
)]
)
]
for option_subconfig in option_subconfigs:
context.reset_cache(option_subconfig)
def get_information(self,
def get_information(
self,
subconfig,
name,
default,
@ -704,44 +823,44 @@ class Values:
except KeyError as err:
pass
if option is not None:
return option._get_information(subconfig,
return option._get_information(
subconfig,
name,
default,
)
return subconfig.config_bag.context.get_description()._get_information(subconfig,
return subconfig.config_bag.context.get_description()._get_information(
subconfig,
name,
default,
)
def del_information(self,
def del_information(
self,
key: Any,
raises: bool=True,
path: str=None,
raises: bool = True,
path: str = None,
):
"""delete information for a specified key
"""
"""delete information for a specified key"""
if path in self._informations and key in self._informations[path]:
del self._informations[path][key]
elif raises:
raise ValueError(_(f"information's item not found \"{key}\""))
raise ValueError(_('information\'s item not found "{}"').format(key))
def list_information(self,
path: str=None,
def list_information(
self,
path: str = None,
) -> List[str]:
"""list all informations keys for a specified path
"""
"""list all informations keys for a specified path"""
return list(self._informations.get(path, {}).keys())
#____________________________________________________________
# ____________________________________________________________
# default owner methods
def set_context_owner(self, owner: str) -> None:
"""set the context owner
"""
"""set the context owner"""
if owner in forbidden_owners:
raise ValueError(_('set owner "{0}" is forbidden').format(str(owner)))
self._values[None][None][1] = owner
def get_context_owner(self) -> str:
"""get the context owner
"""
"""get the context owner"""
return self._values[None][None][1]