feat(40): better conflict error message with dynamic name
This commit is contained in:
parent
c8d5656094
commit
9ee03b22bb
21 changed files with 244 additions and 26 deletions
|
|
@ -24,10 +24,13 @@ details.
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from .__version__ import __version__
|
||||
|
||||
try:
|
||||
from .convert import Rougail
|
||||
from .config import RougailConfig
|
||||
|
||||
__all__ = ("Rougail", "RougailConfig", "__version__")
|
||||
except ModuleNotFoundError as err:
|
||||
__all__ = ("__version__",)
|
||||
|
|
|
|||
|
|
@ -162,6 +162,16 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
)
|
||||
if calculated_variable is not None:
|
||||
if calculated_variable.multi is None:
|
||||
if (
|
||||
isinstance(calculated_variable.default, VariableCalculation)
|
||||
and variable.path == calculated_variable.default.path
|
||||
):
|
||||
msg = _(
|
||||
'the "{0}" default value is a calculation with itself'.format(
|
||||
variable.path
|
||||
)
|
||||
)
|
||||
raise DictConsistencyError(msg, 75, variable.xmlfiles)
|
||||
self._convert_variable_multi(calculated_variable)
|
||||
variable.multi = calc_multi_for_type_variable(
|
||||
variable,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ from ..tiramisu import normalize_family
|
|||
from ..convert import RougailConvert
|
||||
from ..convert.object_model import get_convert_option_types
|
||||
|
||||
#import rougail.structural_commandline.object_model
|
||||
|
||||
RENAMED = {
|
||||
"dictionaries_dir": "main_dictionaries",
|
||||
|
|
@ -179,7 +178,7 @@ class _RougailConfig:
|
|||
yield f"{option.path()}: {option.value.get()}"
|
||||
|
||||
def __repr__(self):
|
||||
print(self.config)
|
||||
self.generate_config()
|
||||
self.config.property.read_write()
|
||||
try:
|
||||
values = "\n".join(self.parse(self.config))
|
||||
|
|
|
|||
|
|
@ -1164,4 +1164,3 @@ class RougailConvert(ParserVariable):
|
|||
tiramisu.write(output)
|
||||
# print(output)
|
||||
return output
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ from pydantic import (
|
|||
)
|
||||
import tiramisu
|
||||
from tiramisu.config import get_common_path
|
||||
from ..utils import get_jinja_variable_to_param, calc_multi_for_type_variable, undefined, PROPERTY_ATTRIBUTE
|
||||
from ..utils import (
|
||||
get_jinja_variable_to_param,
|
||||
calc_multi_for_type_variable,
|
||||
undefined,
|
||||
PROPERTY_ATTRIBUTE,
|
||||
)
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError, VariableCalculationDependencyError
|
||||
from ..tiramisu import CONVERT_OPTION
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ from importlib.util import (
|
|||
from unicodedata import normalize, combining
|
||||
from jinja2 import StrictUndefined, DictLoader
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
from re import findall
|
||||
from tiramisu import DynOptionDescription, calc_value, function_waiting_for_error
|
||||
from tiramisu.error import (
|
||||
ValueWarning,
|
||||
|
|
@ -134,6 +135,16 @@ CONVERT_OPTION = {
|
|||
}
|
||||
|
||||
|
||||
def get_identifier_from_dynamic_family(true_name, name) -> str:
|
||||
if true_name == "{{ identifier }}":
|
||||
return name
|
||||
regexp = true_name.replace("{{ identifier }}", "(.*)")
|
||||
finded = findall(regexp, name)
|
||||
if len(finded) != 1 or not finded[0]:
|
||||
return None
|
||||
return finded[0]
|
||||
|
||||
|
||||
def raise_carry_out_calculation_error(subconfig, *args, **kwargs):
|
||||
try:
|
||||
ori_raise_carry_out_calculation_error(subconfig, *args, **kwargs)
|
||||
|
|
@ -423,3 +434,11 @@ class ConvertDynOptionDescription(DynOptionDescription):
|
|||
self.convert_identifier_to_path(self.get_identifiers(subconfig)[-1]),
|
||||
)
|
||||
return display
|
||||
|
||||
def name_could_conflict(self, dynchild, child):
|
||||
return (
|
||||
get_identifier_from_dynamic_family(
|
||||
dynchild.impl_getname(), child.impl_getname()
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
"""
|
||||
|
||||
from typing import List
|
||||
from re import findall
|
||||
|
||||
from tiramisu import Calculation, owners
|
||||
from tiramisu.error import (
|
||||
|
|
@ -31,7 +30,11 @@ from tiramisu.error import (
|
|||
CancelParam,
|
||||
)
|
||||
from .utils import undefined
|
||||
from .tiramisu import normalize_family, CONVERT_OPTION
|
||||
from .tiramisu import (
|
||||
normalize_family,
|
||||
CONVERT_OPTION,
|
||||
get_identifier_from_dynamic_family,
|
||||
)
|
||||
from .error import DictConsistencyError
|
||||
|
||||
from .i18n import _
|
||||
|
|
@ -41,13 +44,14 @@ class UserDatas:
|
|||
def __init__(self, config) -> None:
|
||||
self.config = config
|
||||
|
||||
def user_datas(self,
|
||||
user_datas: List[dict],
|
||||
*,
|
||||
return_values_not_error=False,
|
||||
user_datas_type: str="user_datas",
|
||||
only_default: bool=False,
|
||||
):
|
||||
def user_datas(
|
||||
self,
|
||||
user_datas: List[dict],
|
||||
*,
|
||||
return_values_not_error=False,
|
||||
user_datas_type: str = "user_datas",
|
||||
only_default: bool = False,
|
||||
):
|
||||
self.values = {}
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
|
|
@ -126,7 +130,9 @@ class UserDatas:
|
|||
if not tconfig.isdynamic(only_self=True):
|
||||
# it's not a dynamic variable
|
||||
continue
|
||||
identifier = self._get_identifier(tconfig.name(), name)
|
||||
identifier = get_identifier_from_dynamic_family(
|
||||
tconfig.name(), name
|
||||
)
|
||||
if identifier != normalize_family(identifier):
|
||||
msg = _(
|
||||
'cannot load variable path "{0}", the identifier "{1}" is not valid in {2}'
|
||||
|
|
@ -240,7 +246,8 @@ class UserDatas:
|
|||
else:
|
||||
if "source" in self.values[path]:
|
||||
option_without_index.information.set(
|
||||
"loaded_from", _("loaded from {0}").format(self.values[path]["source"])
|
||||
"loaded_from",
|
||||
_("loaded from {0}").format(self.values[path]["source"]),
|
||||
)
|
||||
# value is correctly set, remove variable to the set
|
||||
if index is not None:
|
||||
|
|
@ -256,15 +263,6 @@ class UserDatas:
|
|||
if not value_is_set:
|
||||
break
|
||||
|
||||
def _get_identifier(self, true_name, name) -> str:
|
||||
if true_name == "{{ identifier }}":
|
||||
return name
|
||||
regexp = true_name.replace("{{ identifier }}", "(.*)")
|
||||
finded = findall(regexp, name)
|
||||
if len(finded) != 1 or not finded[0]:
|
||||
return None
|
||||
return finded[0]
|
||||
|
||||
def _display_value(self, option, value):
|
||||
if not self.show_secrets and option.type() == "password":
|
||||
return "*" * 10
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
from re import compile as re_compile
|
||||
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||
load_functions('../rougail-tests/funcs/test.py')
|
||||
try:
|
||||
groups.namespace
|
||||
except:
|
||||
groups.addgroup('namespace')
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||
option_4 = StrOption(name="address", doc="{{ identifier }} address", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/60_8family_dynamic_same_name_1/rougail/00-base.yml'], 'type': 'string'})
|
||||
optiondescription_3 = OptionDescription(name="https_proxy", doc="{{ identifier }} Proxy", children=[option_4], properties=frozenset({"basic"}), informations={'ymlfiles': ['../rougail-tests/structures/60_8family_dynamic_same_name_1/rougail/00-base.yml']})
|
||||
option_6 = StrOption(name="address", doc="{{ identifier }} address", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/60_8family_dynamic_same_name_1/rougail/00-base.yml'], 'type': 'string'})
|
||||
optiondescription_5 = ConvertDynOptionDescription(name="{{ identifier }}_proxy", doc="{{ identifier }} Proxy", identifiers=["HTTPS", "SOCKS"], children=[option_6], properties=frozenset({"basic"}), informations={'ymlfiles': ['../rougail-tests/structures/60_8family_dynamic_same_name_1/rougail/00-base.yml']})
|
||||
optiondescription_2 = OptionDescription(name="manual", doc="manual", children=[optiondescription_3, optiondescription_5], properties=frozenset({"basic"}), informations={'ymlfiles': ['../rougail-tests/structures/60_8family_dynamic_same_name_1/rougail/00-base.yml']})
|
||||
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[optiondescription_2], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
from re import compile as re_compile
|
||||
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||
load_functions('../rougail-tests/funcs/test.py')
|
||||
try:
|
||||
groups.namespace
|
||||
except:
|
||||
groups.addgroup('namespace')
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||
option_4 = StrOption(name="address", doc="{{ identifier }} address", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['tests/errors/80unknown_default_variable_same_name_1/rougail/00-base.yml'], 'type': 'string'})
|
||||
optiondescription_3 = OptionDescription(name="https_proxy", doc="{{ identifier }} Proxy", children=[option_4], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/errors/80unknown_default_variable_same_name_1/rougail/00-base.yml']})
|
||||
option_6 = StrOption(name="address", doc="{{ identifier }} address", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['tests/errors/80unknown_default_variable_same_name_1/rougail/00-base.yml'], 'type': 'string'})
|
||||
optiondescription_5 = ConvertDynOptionDescription(name="{{ identifier }}_proxy", doc="{{ identifier }} Proxy", identifiers=["HTTPS", "SOCKS"], children=[option_6], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/errors/80unknown_default_variable_same_name_1/rougail/00-base.yml']})
|
||||
optiondescription_2 = OptionDescription(name="manual", doc="manual", children=[optiondescription_3, optiondescription_5], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/errors/80unknown_default_variable_same_name_1/rougail/00-base.yml']})
|
||||
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[optiondescription_2], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
||||
0
tests/errors/24_1family_dynamic_calc/force_namespace
Normal file
0
tests/errors/24_1family_dynamic_calc/force_namespace
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
version: 1.1
|
||||
manual:
|
||||
|
||||
use_for_https:
|
||||
description: Also use this proxy for HTTPS
|
||||
default: true
|
||||
|
||||
"{{ identifier }}_proxy":
|
||||
description: "{{ identifier }} Proxy"
|
||||
dynamic:
|
||||
- HTTPS
|
||||
- SOCKS
|
||||
hidden:
|
||||
variable: rougail.manual.use_for_https
|
||||
|
||||
address:
|
||||
description: "{{ identifier }} address"
|
||||
default:
|
||||
variable: rougail.manual.http_proxy.address
|
||||
|
||||
port:
|
||||
description: "{{ identifier }} port"
|
||||
default:
|
||||
variable: rougail.manual.http_proxy.port
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
version: 1.1
|
||||
manual:
|
||||
|
||||
use_for_https:
|
||||
description: Also use this proxy for HTTPS
|
||||
default: true
|
||||
|
||||
"{{ identifier }}_proxy":
|
||||
description: "{{ identifier }} Proxy"
|
||||
dynamic:
|
||||
- HTTPS
|
||||
- SOCKS
|
||||
hidden:
|
||||
variable: rougail.manual.use_for_https
|
||||
|
||||
address:
|
||||
description: "{{ identifier }} address"
|
||||
default:
|
||||
variable: rougail.manual.http_proxy.address
|
||||
|
||||
port:
|
||||
description: "{{ identifier }} port"
|
||||
default:
|
||||
variable: rougail.manual.http_proxy.port
|
||||
|
|
@ -49,7 +49,7 @@ test_ok -= excludes
|
|||
test_raise -= excludes
|
||||
# test_ok = ['04_5validators_multi3']
|
||||
#test_ok = []
|
||||
# test_raise = ['22_0calculation_variable_leader_follower_multi']
|
||||
# test_raise = ['80unknown_default_variable_inside_dynamic_family']
|
||||
#test_raise = []
|
||||
test_multi = True
|
||||
#test_multi = False
|
||||
|
|
@ -181,7 +181,7 @@ def test_dictionary_namespace(test_dir):
|
|||
assert getcwd() == ORI_DIR
|
||||
|
||||
|
||||
def test_error_dictionary(test_dir_error):
|
||||
def test_error_dictionary_namespace(test_dir_error):
|
||||
assert getcwd() == ORI_DIR
|
||||
test_dir_ = join(errors_dirs, test_dir_error)
|
||||
errno = []
|
||||
|
|
@ -202,3 +202,28 @@ def test_error_dictionary(test_dir_error):
|
|||
if isdir(tiramisu_tmp_dir):
|
||||
rmtree(tiramisu_tmp_dir)
|
||||
assert getcwd() == ORI_DIR
|
||||
|
||||
|
||||
def test_error_dictionary(test_dir_error):
|
||||
assert getcwd() == ORI_DIR
|
||||
test_dir_ = join(errors_dirs, test_dir_error)
|
||||
if isfile(join(test_dir_, 'force_namespace')):
|
||||
return
|
||||
errno = []
|
||||
rougailconfig = RougailConfig.copy()
|
||||
rougailconfig['main_namespace'] = None
|
||||
eolobj = load_rougail_object(test_dir_, rougailconfig, namespace=False)
|
||||
if eolobj is None:
|
||||
return
|
||||
for i in listdir(test_dir_):
|
||||
if i.startswith('errno_'):
|
||||
errno.append(int(i.split('_')[1]))
|
||||
if not errno:
|
||||
errno.append(0)
|
||||
with raises(DictConsistencyError) as err:
|
||||
save(test_dir_, eolobj, error=True)
|
||||
assert err.value.errno in errno, f'expected errno: {errno}, errno: {err.value.errno}, msg: {err}'
|
||||
tiramisu_tmp_dir = dirname(get_tiramisu_filename(test_dir_, 'tmp', False, True))
|
||||
if isdir(tiramisu_tmp_dir):
|
||||
rmtree(tiramisu_tmp_dir)
|
||||
assert getcwd() == ORI_DIR
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import logging
|
|||
|
||||
from rougail import Rougail, RougailConfig
|
||||
from rougail.error import DictConsistencyError
|
||||
from tiramisu.error import ConflictError
|
||||
|
||||
from rougail_tests.utils import config_to_dict
|
||||
|
||||
|
|
@ -95,3 +96,74 @@ def test_namespace():
|
|||
'ns2.var2': 'NS2',
|
||||
'ns3.var1': 'NS3',
|
||||
'ns3.var2': 'NS3'}
|
||||
#
|
||||
#
|
||||
#def test_duplicate_0():
|
||||
# rougailconfig = RougailConfig.copy()
|
||||
# rougailconfig['main_namespace'] = None
|
||||
# rougailconfig['dictionaries_dir'] = ['tests/duplicates/0/']
|
||||
# eolobj = Rougail(rougailconfig=rougailconfig)
|
||||
# cfg = eolobj.run()
|
||||
# cfg.value.get()
|
||||
# cfg.option('od.od_val1').value.get()
|
||||
# cfg.option('od.od_val2').value.get()
|
||||
|
||||
|
||||
def test_duplicate_1():
|
||||
rougailconfig = RougailConfig.copy()
|
||||
rougailconfig['main_namespace'] = None
|
||||
rougailconfig['dictionaries_dir'] = ['tests/duplicates/1/']
|
||||
eolobj = Rougail(rougailconfig=rougailconfig)
|
||||
cfg = eolobj.run()
|
||||
with raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_duplicate_2():
|
||||
rougailconfig = RougailConfig.copy()
|
||||
rougailconfig['main_namespace'] = None
|
||||
rougailconfig['dictionaries_dir'] = ['tests/duplicates/2/']
|
||||
eolobj = Rougail(rougailconfig=rougailconfig)
|
||||
cfg = eolobj.run()
|
||||
with raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_duplicate_3():
|
||||
rougailconfig = RougailConfig.copy()
|
||||
rougailconfig['main_namespace'] = None
|
||||
rougailconfig['dictionaries_dir'] = ['tests/duplicates/3/']
|
||||
eolobj = Rougail(rougailconfig=rougailconfig)
|
||||
cfg = eolobj.run()
|
||||
with raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
#
|
||||
#
|
||||
#def test_duplicate_4():
|
||||
# rougailconfig = RougailConfig.copy()
|
||||
# rougailconfig['main_namespace'] = None
|
||||
# rougailconfig['dictionaries_dir'] = ['tests/duplicates/4/']
|
||||
# eolobj = Rougail(rougailconfig=rougailconfig)
|
||||
# cfg = eolobj.run()
|
||||
# with raises(ConflictError):
|
||||
# cfg.value.get()
|
||||
# with raises(ConflictError):
|
||||
# cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_duplicate_5():
|
||||
rougailconfig = RougailConfig.copy()
|
||||
rougailconfig['main_namespace'] = None
|
||||
rougailconfig['dictionaries_dir'] = ['tests/duplicates/5/']
|
||||
eolobj = Rougail(rougailconfig=rougailconfig)
|
||||
cfg = eolobj.run()
|
||||
with raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
|
|
|||
Loading…
Reference in a new issue