diff --git a/doc/param/simple.md b/doc/param/simple.md
index 6e060e141..02accb7ff 100644
--- a/doc/param/simple.md
+++ b/doc/param/simple.md
@@ -42,3 +42,11 @@ Le paramètre peut être une valeur null (None en python) :
```
```
+
+# Paramètre de type "space"
+
+Les paramètres sont chargés en supprimer les espaces en début ou fin de chaîne. Ce qui rend impossible d'avoir un paramètre " ". Avec le type "space", le paramètre sera donc un simple espace :
+
+```
+
+```
diff --git a/src/rougail/annotator/family.py b/src/rougail/annotator/family.py
index dba5d1dcc..43f2c90d1 100644
--- a/src/rougail/annotator/family.py
+++ b/src/rougail/annotator/family.py
@@ -268,7 +268,7 @@ class Annotator(Walk):
for family in self.get_families():
if 'dynamic' not in vars(family):
continue
- family.suffixes = self.objectspace.paths.get_variable(family.dynamic)
+ family.suffixes = self.objectspace.paths.get_variable(family.dynamic, family.xmlfiles)
del family.dynamic
if not family.suffixes.multi:
msg = _(f'dynamic family "{family.name}" must be linked '
diff --git a/src/rougail/annotator/param.py b/src/rougail/annotator/param.py
index 64e7a2e04..ac59aa8b2 100644
--- a/src/rougail/annotator/param.py
+++ b/src/rougail/annotator/param.py
@@ -59,7 +59,7 @@ class ParamAnnotator:
if param.type in ['suffix', 'index']:
msg = _(f'"{param.type}" parameter must not have a value')
raise DictConsistencyError(msg, 28, obj.xmlfiles)
- if param.type == 'nil':
+ if param.type in ['nil', 'space']:
if param.text is not None:
msg = _(f'"{param.type}" parameter must not have a value')
raise DictConsistencyError(msg, 40, obj.xmlfiles)
@@ -116,6 +116,9 @@ class ParamAnnotator:
raise DictConsistencyError(msg, 60, obj.xmlfiles)
elif param.type == 'nil':
param.text = None
+ elif param.type == 'space':
+ param.type = 'string'
+ param.text = ' '
elif param.type == 'string':
param.text = ''
if variable_type:
diff --git a/src/rougail/annotator/variable.py b/src/rougail/annotator/variable.py
index 0fafa9b03..b193a91dd 100644
--- a/src/rougail/annotator/variable.py
+++ b/src/rougail/annotator/variable.py
@@ -163,6 +163,8 @@ class Annotator(Walk): # pylint: disable=R0903
raise DictConsistencyError(msg, 5, choice.xmlfiles)
if choice.type == 'nil':
choice.name = None
+ elif choice.type == 'space':
+ choice.name = ' '
elif choice.type == 'variable':
choice.name = self.objectspace.paths.get_variable(choice.name)
if not choice.name.multi:
diff --git a/src/rougail/data/rougail.dtd b/src/rougail/data/rougail.dtd
index 8c81b8e85..d86c5371e 100644
--- a/src/rougail/data/rougail.dtd
+++ b/src/rougail/data/rougail.dtd
@@ -115,10 +115,10 @@
-
+
-
+
@@ -137,7 +137,7 @@
-
+
diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py
index 033c5db45..82c3602f0 100644
--- a/src/rougail/tiramisureflector.py
+++ b/src/rougail/tiramisureflector.py
@@ -296,7 +296,7 @@ class Common:
):
"""Populate variable parameters
"""
- if param.type in ['number', 'boolean', 'nil', 'string', 'port', 'choice']:
+ if param.type in ['number', 'boolean', 'nil', 'string', 'port', 'choice', 'space']:
value = param.text
if param.type == 'string' and value is not None:
value = self.convert_str(value)
diff --git a/tests/dictionaries/40space_param/00_base.xml b/tests/dictionaries/40space_param/00_base.xml
new file mode 100644
index 000000000..3b02f50c2
--- /dev/null
+++ b/tests/dictionaries/40space_param/00_base.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+ 3127
+
+
+
+
+
+
+ toto1
+
+
+
diff --git a/tests/dictionaries/40space_param/__init__.py b/tests/dictionaries/40space_param/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/40space_param/makedict/after.json b/tests/dictionaries/40space_param/makedict/after.json
new file mode 100644
index 000000000..b88ddc479
--- /dev/null
+++ b/tests/dictionaries/40space_param/makedict/after.json
@@ -0,0 +1,10 @@
+{
+ "rougail.proxy_authentifie.toto1": {
+ "owner": "default",
+ "value": null
+ },
+ "rougail.proxy_authentifie.toto2": {
+ "owner": "default",
+ "value": "3127"
+ }
+}
diff --git a/tests/dictionaries/40space_param/makedict/base.json b/tests/dictionaries/40space_param/makedict/base.json
new file mode 100644
index 000000000..36d66f3d7
--- /dev/null
+++ b/tests/dictionaries/40space_param/makedict/base.json
@@ -0,0 +1,4 @@
+{
+ "rougail.proxy_authentifie.toto1": null,
+ "rougail.proxy_authentifie.toto2": "3127"
+}
diff --git a/tests/dictionaries/40space_param/makedict/before.json b/tests/dictionaries/40space_param/makedict/before.json
new file mode 100644
index 000000000..b88ddc479
--- /dev/null
+++ b/tests/dictionaries/40space_param/makedict/before.json
@@ -0,0 +1,10 @@
+{
+ "rougail.proxy_authentifie.toto1": {
+ "owner": "default",
+ "value": null
+ },
+ "rougail.proxy_authentifie.toto2": {
+ "owner": "default",
+ "value": "3127"
+ }
+}
diff --git a/tests/dictionaries/40space_param/tiramisu/base.py b/tests/dictionaries/40space_param/tiramisu/base.py
new file mode 100644
index 000000000..5ead32cdf
--- /dev/null
+++ b/tests/dictionaries/40space_param/tiramisu/base.py
@@ -0,0 +1,21 @@
+from importlib.machinery import SourceFileLoader as _SourceFileLoader
+from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
+class func:
+ pass
+_loader = _SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
+_spec = _spec_from_loader(_loader.name, _loader)
+_func = _module_from_spec(_spec)
+_loader.exec_module(_func)
+for function in dir(_func):
+ if function.startswith('_'):
+ continue
+ setattr(func, function, getattr(_func, function))
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+option_3 = PortOption(name="toto1", doc="Port d'écoute du proxy", default=Calculation(func.calc_multi_condition, Params((ParamValue(" ")))), allow_private=True, properties=frozenset({"expert"}))
+option_4 = PortOption(name="toto2", doc="Port d'écoute du proxy NTLM", default="3127", allow_private=True, properties=frozenset({"expert", "mandatory"}))
+option_2 = OptionDescription(name="proxy_authentifie", doc="proxy authentifié", children=[option_3, option_4], properties=frozenset({"expert"}))
+option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
+option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
diff --git a/tests/test_2_makedict.py b/tests/test_2_makedict.py
index 7ffbb90a9..9d3d41992 100644
--- a/tests/test_2_makedict.py
+++ b/tests/test_2_makedict.py
@@ -70,7 +70,7 @@ async def launch_flattener(test_dir):
if not isfile(makedict_file):
raise Exception('dict is not empty')
with open(makedict_file, 'r') as fh:
- assert load(fh) == loads(dumps(config_dict))
+ assert load(fh) == loads(dumps(config_dict)), f"error in file {makedict_file}"
#
await value_owner(makedict_before, config)
# deploy
@@ -110,7 +110,8 @@ async def value_owner(makedict_value_owner, config):
dump(ret, fh, indent=4)
fh.write('\n')
with open(makedict_value_owner, 'r') as fh:
- assert load(fh) == loads(dumps(ret))
+ assert load(fh) == loads(dumps(ret)), f"error in file {makedict_value_owner}"
+
@mark.asyncio