diff --git a/.gitignore b/.gitignore
index 4802f20..a5c71d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,5 +3,4 @@
*.pyc
*.mo
*.swp
-version.in
build/
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..2835134
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,8 @@
+# Include the README
+include *.rst
+
+# Include the license file
+include LICENSE.txt
+
+# Include the data files
+recursive-include tiramisu *.py *.mo
diff --git a/Makefile b/Makefile
index 0755b8f..57e8232 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ INSTALL_DATA := install -m 644
INSTALL_PROGRAM := install -m 755
INSTALL_DIR := install -m 755 -d
-TRADUC_DIR = translations
+TRADUC_DIR = tiramisu/locale
TRADUC_DEST = $(DESTDIR)/usr/share/locale
PYTHON_OPTS =
@@ -26,7 +26,7 @@ define gettext
else \
P="pygettext.py" ; \
fi ; \
- $$P -p translations/ -o $(PACKAGE).pot `find $(PACKAGE)/ -name "*.py"`
+ $$P -p $(TRADUC_DIR)/ -o $(PACKAGE).pot `find $(PACKAGE)/ -name "*.py"`
endef
# Build translation files
@@ -69,12 +69,12 @@ build-pot:
build-lang:
$(call build_translation, $(TRADUC_DIR))
-install-lang:
- $(INSTALL_DIR) $(TRADUC_DEST)
- $(call install_translation, $(TRADUC_DIR))
+# install-lang:
+# $(INSTALL_DIR) $(TRADUC_DEST)
+# $(call install_translation, $(TRADUC_DIR))
-install: install-lang
- python setup.py install --no-compile $(PYTHON_OPTS)
+install: # install-lang
+ python3 setup.py install --no-compile $(PYTHON_OPTS)
dist:
git archive --format=tar --prefix $(PACKAGE)-$(VERSION)/ HEAD | gzip -9 > $(PACKAGE)-$(VERSION).tar.gz
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 1f7391f..0000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-master
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..95fdaf2
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,17 @@
+[build-system]
+requires = ["flit"]
+build-backend = "flit.buildapi"
+
+[tool.flit.metadata]
+module = "tiramisu"
+author = "Emmanuel Garette"
+author-email = "gnunux@gnunux.info"
+home-page = "https://framagit.org/tiramisu/tiramisu"
+classifiers = [
+ "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
+ "Programming Language :: Python :: 3",
+ "Natural Language :: English",
+ "Natural Language :: French",
+ "Operating System :: OS Independent",
+]
+requires-python = ">=3.5"
diff --git a/setup.py b/setup.py
index 0374778..84c557d 100644
--- a/setup.py
+++ b/setup.py
@@ -1,57 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from distutils.core import setup
+from setuptools import setup, find_packages
from os.path import dirname, abspath, join, normpath, isdir
from os import listdir
import os
+from tiramisu import __version__
-package_name = os.environ.get('PACKAGE_DST', 'tiramisu')
-
-def fetch_version():
- """Get version from version.in"""
- return open('VERSION', 'r').readline().strip()
-
-
-def return_files(component):
- here = dirname(abspath(__file__))
- path = normpath(join(here, 'tiramisu', component))
- dir_content = [content for content in listdir(path)
- if not content == '__pycache__']
- paths = filter(isdir, [join(path, content)
- for content in dir_content])
- lst = ['.'.join(path.split('/')[-3:]) for path in paths]
- #lst = [package_name + '.' + '.'.join(path.split('/')[-2:]) for path in paths]
- return lst
-
-
-packages = [package_name, package_name + '.storage', package_name + '.option']
-packages.extend(return_files('storage'))
-packages.extend(return_files('option'))
-
-if package_name != 'tiramisu':
- package_dir = {package_name: 'tiramisu'}
-else:
- package_dir = {}
+PACKAGE_NAME = os.environ.get('PACKAGE_DST', 'tiramisu')
setup(
+ version=__version__,
author="Tiramisu's team",
- author_email='contact@cadoles.com',
- name=package_name,
- version=fetch_version(),
+ author_email='gnunux@gnunux.info',
+ name=PACKAGE_NAME,
description='an options controller tool',
- url='http://tiramisu.labs.libre-entreprise.org/',
+ url='https://framagit.org/tiramisu/tiramisu',
+ license='GNU Library or Lesser General Public License (LGPL)',
classifiers=[
- "Programming Language :: Python",
- "Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
- "Development Status :: 4 - Beta",
- "Environment :: Other Environment",
- "Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
- "Topic :: Software Development :: Libraries :: Python Modules",
- "Topic :: Text Processing :: Linguistic"
+ "Natural Language :: English",
+ "Natural Language :: French",
],
long_description="""\
An options controller tool
@@ -67,8 +38,8 @@ Tiramisu is an options handler and an options controller, wich aims at
producing flexible and fast options access.
-This version requires Python 2.6 or later.
+This version requires Python 3.5 or later.
""",
- packages=packages,
- package_dir=package_dir
+ include_package_data=True,
+ packages=find_packages(include=['tiramisu'])
)
diff --git a/tiramisu/__init__.py b/tiramisu/__init__.py
index db7fad8..bc7a6f1 100644
--- a/tiramisu/__init__.py
+++ b/tiramisu/__init__.py
@@ -12,6 +12,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
+"""Configuration management library written in python
+"""
from .function import Params, ParamOption, ParamValue, ParamContext, \
tiramisu_copy, calc_value
from .option import *
@@ -43,3 +45,4 @@ allfuncs.extend(all_options)
del(all_options)
__all__ = tuple(allfuncs)
del(allfuncs)
+__version__ = "3.0rc5"
diff --git a/tiramisu/i18n.py b/tiramisu/i18n.py
index 0a7601d..c872da7 100644
--- a/tiramisu/i18n.py
+++ b/tiramisu/i18n.py
@@ -18,34 +18,43 @@
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
"internationalisation utilities"
-import gettext
-import os
-import sys
-import locale
+from gettext import translation, NullTranslations
+from platform import system
+from pkg_resources import resource_filename
+from .log import log
-# Application Name
-APP_NAME = 'tiramisu'
-# Traduction dir
-APP_DIR = os.path.join(sys.prefix, 'share')
-LOCALE_DIR = os.path.join(APP_DIR, 'locale')
+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')
-# Default Lanugage
-DEFAULT_LANG = os.environ.get('LANG', '').split(':')
-DEFAULT_LANG += ['en_US']
+ if 'Windows' in system():
+ import ctypes
+ from locale import windows_locale
+ default_locale = windows_locale[ctypes.windll.kernel32.GetUserDefaultUILanguage()]
+ else:
+ from locale import getdefaultlocale
+ default_locale = getdefaultlocale()
+ if default_locale and isinstance(default_locale, tuple):
+ user_locale = default_locale[0][:2]
+ elif default_locale:
+ user_locale = default_locale[:2]
+ else:
+ user_locale = 'en'
+ try:
+ trans = translation(domain=app_name,
+ localedir=translations_path,
+ languages=[user_locale],
+ codeset='UTF-8')
+ except FileNotFoundError:
+ log.debug('cannot found translation file for langage {} in localedir {}'.format(user_locale,
+ translations_path))
+ trans = NullTranslations()
+ return trans.gettext
-languages = []
-lc, encoding = locale.getdefaultlocale()
-if lc:
- languages = [lc]
-languages += DEFAULT_LANG
-mo_location = LOCALE_DIR
-
-gettext.find(APP_NAME, mo_location)
-gettext.textdomain(APP_NAME)
-gettext.bind_textdomain_codeset(APP_NAME, "UTF-8")
-
-t = gettext.translation(APP_NAME, fallback=True)
-
-_ = t.gettext
+_ = get_translation()
diff --git a/translations/fr/tiramisu.po b/tiramisu/locale/fr/LC_MESSAGES/tiramisu.po
similarity index 100%
rename from translations/fr/tiramisu.po
rename to tiramisu/locale/fr/LC_MESSAGES/tiramisu.po
diff --git a/translations/tiramisu.pot b/tiramisu/locale/tiramisu.pot
similarity index 76%
rename from translations/tiramisu.pot
rename to tiramisu/locale/tiramisu.pot
index b3fb9b8..e77bb93 100644
--- a/translations/tiramisu.pot
+++ b/tiramisu/locale/tiramisu.pot
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2019-03-05 08:46+CET\n"
+"POT-Creation-Date: 2019-04-07 10:39+CEST\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -55,31 +55,31 @@ msgstr ""
msgid "cannot add this property: \"{0}\""
msgstr ""
-#: tiramisu/api.py:503 tiramisu/config.py:252
+#: tiramisu/api.py:504 tiramisu/config.py:252
msgid "can't delete a SymLinkOption"
msgstr ""
-#: tiramisu/api.py:636 tiramisu/api.py:1322
+#: tiramisu/api.py:637 tiramisu/api.py:1326
msgid "please specify a valid sub function ({})"
msgstr ""
-#: tiramisu/api.py:699 tiramisu/api.py:1145
+#: tiramisu/api.py:700 tiramisu/api.py:1146
msgid "unknown list type {}"
msgstr ""
-#: tiramisu/api.py:701 tiramisu/api.py:1147
+#: tiramisu/api.py:702 tiramisu/api.py:1148
msgid "unknown group_type: {0}"
msgstr ""
-#: tiramisu/api.py:974
+#: tiramisu/api.py:975
msgid "properties must be a set"
msgstr ""
-#: tiramisu/api.py:980 tiramisu/api.py:1002
+#: tiramisu/api.py:981 tiramisu/api.py:1003
msgid "unknown when {} (must be in append or remove)"
msgstr ""
-#: tiramisu/api.py:992 tiramisu/api.py:1014 tiramisu/config.py:1227
+#: tiramisu/api.py:993 tiramisu/api.py:1015 tiramisu/config.py:1249
msgid "unknown type {}"
msgstr ""
@@ -171,7 +171,7 @@ msgstr ""
msgid "unknown config \"{}\""
msgstr ""
-#: tiramisu/config.py:987 tiramisu/config.py:1202
+#: tiramisu/config.py:987 tiramisu/config.py:1224
msgid "{}config's children should be config, not {}"
msgstr ""
@@ -191,26 +191,34 @@ msgstr ""
msgid "force_default and force_dont_change_value cannot be set together"
msgstr ""
-#: tiramisu/config.py:1189
-msgid "MetaConfig with optiondescription must have string has child, not {}"
+#: tiramisu/config.py:1176
+msgid "config is already in a metaconfig"
msgstr ""
-#: tiramisu/config.py:1208
-msgid "child must be a Config or MetaConfig"
-msgstr ""
-
-#: tiramisu/config.py:1212
-msgid "all config in metaconfig must have the same optiondescription"
-msgstr ""
-
-#: tiramisu/config.py:1225
+#: tiramisu/config.py:1178 tiramisu/config.py:1247
msgid "config name must be uniq in groupconfig for {0}"
msgstr ""
-#: tiramisu/config.py:1250
+#: tiramisu/config.py:1192
msgid "cannot find the config {}"
msgstr ""
+#: tiramisu/config.py:1211
+msgid "MetaConfig with optiondescription must have string has child, not {}"
+msgstr ""
+
+#: tiramisu/config.py:1230
+msgid "child must be a Config or MetaConfig"
+msgstr ""
+
+#: tiramisu/config.py:1234
+msgid "all config in metaconfig must have the same optiondescription"
+msgstr ""
+
+#: tiramisu/config.py:1272
+msgid "metaconfig must have the same optiondescription"
+msgstr ""
+
#: tiramisu/error.py:24
msgid "and"
msgstr ""
@@ -223,11 +231,11 @@ msgstr ""
msgid " {} "
msgstr ""
-#: tiramisu/error.py:103 tiramisu/setting.py:563
+#: tiramisu/error.py:103 tiramisu/setting.py:579
msgid "property"
msgstr ""
-#: tiramisu/error.py:105 tiramisu/setting.py:565
+#: tiramisu/error.py:105 tiramisu/setting.py:581
msgid "properties"
msgstr ""
@@ -239,38 +247,42 @@ msgstr ""
msgid "cannot access to {0} \"{1}\" because has {2} {3}"
msgstr ""
-#: tiramisu/error.py:185
+#: tiramisu/error.py:189
msgid "invalid value"
msgstr ""
-#: tiramisu/error.py:190
+#: tiramisu/error.py:194
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\""
msgstr ""
-#: tiramisu/error.py:194 tiramisu/error.py:198
+#: tiramisu/error.py:198 tiramisu/error.py:202
msgid "\"{0}\" is an invalid {1} for \"{2}\""
msgstr ""
-#: tiramisu/function.py:31
+#: tiramisu/function.py:34
msgid "args in params must be a tuple"
msgstr ""
-#: tiramisu/function.py:34 tiramisu/function.py:39
+#: tiramisu/function.py:37 tiramisu/function.py:42
msgid "arg in params must be a Param"
msgstr ""
-#: tiramisu/function.py:36
+#: tiramisu/function.py:39
msgid "kwargs in params must be a dict"
msgstr ""
-#: tiramisu/function.py:52
+#: tiramisu/function.py:58
msgid "paramoption needs an option not {}"
msgstr ""
-#: tiramisu/function.py:58
+#: tiramisu/function.py:64
msgid "param must have a boolean not a {} for notraisepropertyerror"
msgstr ""
+#: tiramisu/function.py:271
+msgid "unexpected {} condition_operator in calc_value"
+msgstr ""
+
#: tiramisu/option/baseoption.py:75 tiramisu/option/symlinkoption.py:33
msgid "\"{0}\" is an invalid name for an option"
msgstr ""
@@ -299,124 +311,124 @@ msgstr ""
msgid "missing those arguments \"{}\" in function \"{}\" for \"{}\""
msgstr ""
-#: tiramisu/option/baseoption.py:257
+#: tiramisu/option/baseoption.py:258
msgid "params defined for a callback function but no callback defined yet for option \"{0}\""
msgstr ""
-#: tiramisu/option/baseoption.py:349 tiramisu/storage/dictionary/value.py:275
+#: tiramisu/option/baseoption.py:350 tiramisu/storage/dictionary/value.py:275
#: tiramisu/storage/sqlite3/value.py:201
msgid "information's item not found: {0}"
msgstr ""
-#: tiramisu/option/baseoption.py:362
+#: tiramisu/option/baseoption.py:363
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr ""
-#: tiramisu/option/baseoption.py:393
+#: tiramisu/option/baseoption.py:394
msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr ""
-#: tiramisu/option/baseoption.py:403
+#: tiramisu/option/baseoption.py:404
msgid "\"{}\" not part of any Config"
msgstr ""
-#: tiramisu/option/baseoption.py:450
+#: tiramisu/option/baseoption.py:453
msgid "malformed requirements must be an option in option {0}"
msgstr ""
-#: tiramisu/option/baseoption.py:453
+#: tiramisu/option/baseoption.py:456
msgid "malformed requirements multi option must not set as requires of non multi option {0}"
msgstr ""
-#: tiramisu/option/baseoption.py:486
+#: tiramisu/option/baseoption.py:495
msgid "malformed requirements expected must have option and value for option {0}"
msgstr ""
-#: tiramisu/option/baseoption.py:493 tiramisu/option/baseoption.py:509
+#: tiramisu/option/baseoption.py:502 tiramisu/option/baseoption.py:518
msgid "malformed requirements expected value must be valid for option {0}: {1}"
msgstr ""
-#: tiramisu/option/baseoption.py:523
+#: tiramisu/option/baseoption.py:532
msgid "malformed requirements for option: {0} action cannot be force_store_value"
msgstr ""
-#: tiramisu/option/baseoption.py:531
+#: tiramisu/option/baseoption.py:540
msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr ""
-#: tiramisu/option/baseoption.py:538
+#: tiramisu/option/baseoption.py:547
msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr ""
-#: tiramisu/option/baseoption.py:545
+#: tiramisu/option/baseoption.py:554
msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr ""
-#: tiramisu/option/baseoption.py:552
+#: tiramisu/option/baseoption.py:561
msgid "malformed requirements for option: \"{0}\" operator must be \"or\" or \"and\""
msgstr ""
-#: tiramisu/option/baseoption.py:564
+#: tiramisu/option/baseoption.py:574
msgid "malformed requirements type for option: {0}, must be a dict"
msgstr ""
-#: tiramisu/option/baseoption.py:570
+#: tiramisu/option/baseoption.py:580
msgid "malformed requirements for option: {0} unknown keys {1}, must only {2}"
msgstr ""
-#: tiramisu/option/baseoption.py:579
+#: tiramisu/option/baseoption.py:592
msgid "malformed requirements for option: {0} require must have option, expected and action keys"
msgstr ""
-#: tiramisu/option/booloption.py:30
+#: tiramisu/option/booloption.py:31
msgid "boolean"
msgstr ""
-#: tiramisu/option/broadcastoption.py:31
+#: tiramisu/option/broadcastoption.py:32
msgid "broadcast address"
msgstr ""
-#: tiramisu/option/broadcastoption.py:38 tiramisu/option/dateoption.py:37
-#: tiramisu/option/domainnameoption.py:113 tiramisu/option/ipoption.py:77
-#: tiramisu/option/netmaskoption.py:41 tiramisu/option/networkoption.py:67
-#: tiramisu/option/passwordoption.py:38 tiramisu/option/portoption.py:106
-#: tiramisu/option/urloption.py:40
+#: tiramisu/option/broadcastoption.py:39 tiramisu/option/dateoption.py:38
+#: tiramisu/option/domainnameoption.py:118 tiramisu/option/ipoption.py:83
+#: tiramisu/option/netmaskoption.py:42 tiramisu/option/networkoption.py:68
+#: tiramisu/option/passwordoption.py:39 tiramisu/option/portoption.py:107
+#: tiramisu/option/urloption.py:41
msgid "invalid string"
msgstr ""
-#: tiramisu/option/broadcastoption.py:56
+#: tiramisu/option/broadcastoption.py:57
msgid "invalid len for vals"
msgstr ""
-#: tiramisu/option/broadcastoption.py:61
+#: tiramisu/option/broadcastoption.py:62
msgid "broadcast \"{4}\" invalid with network {0}/{1} (\"{2}\"/\"{3}\")"
msgstr ""
-#: tiramisu/option/choiceoption.py:36
+#: tiramisu/option/choiceoption.py:37
msgid "choice"
msgstr ""
-#: tiramisu/option/choiceoption.py:65
+#: tiramisu/option/choiceoption.py:66
msgid "values is not a function, so values_params must be None"
msgstr ""
-#: tiramisu/option/choiceoption.py:67
+#: tiramisu/option/choiceoption.py:68
msgid "values must be a tuple or a function for {0}"
msgstr ""
-#: tiramisu/option/choiceoption.py:100
+#: tiramisu/option/choiceoption.py:101
msgid "calculated values for {0} is not a list"
msgstr ""
-#: tiramisu/option/choiceoption.py:113
+#: tiramisu/option/choiceoption.py:114
msgid "only \"{0}\" is allowed"
msgstr ""
-#: tiramisu/option/choiceoption.py:116
+#: tiramisu/option/choiceoption.py:117
msgid "only \"{0}\" are allowed"
msgstr ""
-#: tiramisu/option/dateoption.py:30
+#: tiramisu/option/dateoption.py:31
msgid "date"
msgstr ""
@@ -424,43 +436,43 @@ msgstr ""
msgid "domain name"
msgstr ""
-#: tiramisu/option/domainnameoption.py:58
+#: tiramisu/option/domainnameoption.py:59
msgid "unknown type_ {0} for hostname"
msgstr ""
-#: tiramisu/option/domainnameoption.py:61
+#: tiramisu/option/domainnameoption.py:62
msgid "allow_ip must be a boolean"
msgstr ""
-#: tiramisu/option/domainnameoption.py:63
+#: tiramisu/option/domainnameoption.py:64
msgid "allow_without_dot must be a boolean"
msgstr ""
-#: tiramisu/option/domainnameoption.py:107
+#: tiramisu/option/domainnameoption.py:112
msgid "invalid length (min 1)"
msgstr ""
-#: tiramisu/option/domainnameoption.py:109
+#: tiramisu/option/domainnameoption.py:114
msgid "invalid length (max {0})"
msgstr ""
-#: tiramisu/option/domainnameoption.py:121
+#: tiramisu/option/domainnameoption.py:125
msgid "must not be an IP"
msgstr ""
-#: tiramisu/option/domainnameoption.py:125
+#: tiramisu/option/domainnameoption.py:131
msgid "must have dot"
msgstr ""
-#: tiramisu/option/domainnameoption.py:127
+#: tiramisu/option/domainnameoption.py:133
msgid "invalid length (max 255)"
msgstr ""
-#: tiramisu/option/domainnameoption.py:135
+#: tiramisu/option/domainnameoption.py:141
msgid "some characters are uppercase"
msgstr ""
-#: tiramisu/option/domainnameoption.py:138
+#: tiramisu/option/domainnameoption.py:144
msgid "some characters may cause problems"
msgstr ""
@@ -488,63 +500,63 @@ msgstr ""
msgid "DynOptionDescription callback return a list with multiple value \"{}\""
msgstr ""
-#: tiramisu/option/emailoption.py:31
+#: tiramisu/option/emailoption.py:32
msgid "email address"
msgstr ""
-#: tiramisu/option/filenameoption.py:30
+#: tiramisu/option/filenameoption.py:31
msgid "file name"
msgstr ""
-#: tiramisu/option/floatoption.py:30
+#: tiramisu/option/floatoption.py:31
msgid "float"
msgstr ""
-#: tiramisu/option/intoption.py:30
+#: tiramisu/option/intoption.py:31
msgid "integer"
msgstr ""
-#: tiramisu/option/intoption.py:54
+#: tiramisu/option/intoption.py:55
msgid "value must be greater than \"{0}\""
msgstr ""
-#: tiramisu/option/intoption.py:57
+#: tiramisu/option/intoption.py:58
msgid "value must be less than \"{0}\""
msgstr ""
-#: tiramisu/option/ipoption.py:35
+#: tiramisu/option/ipoption.py:36
msgid "IP"
msgstr ""
-#: tiramisu/option/ipoption.py:83 tiramisu/option/networkoption.py:73
+#: tiramisu/option/ipoption.py:89 tiramisu/option/networkoption.py:74
msgid "must use CIDR notation"
msgstr ""
-#: tiramisu/option/ipoption.py:105
+#: tiramisu/option/ipoption.py:111
msgid "shouldn't be reserved IP"
msgstr ""
-#: tiramisu/option/ipoption.py:107
+#: tiramisu/option/ipoption.py:113
msgid "mustn't be reserved IP"
msgstr ""
-#: tiramisu/option/ipoption.py:111
+#: tiramisu/option/ipoption.py:117
msgid "should be private IP"
msgstr ""
-#: tiramisu/option/ipoption.py:113
+#: tiramisu/option/ipoption.py:119
msgid "must be private IP"
msgstr ""
-#: tiramisu/option/ipoption.py:141
+#: tiramisu/option/ipoption.py:147
msgid "\"{0}\" is not in network \"{1}\" (\"{2}\")"
msgstr ""
-#: tiramisu/option/ipoption.py:157
+#: tiramisu/option/ipoption.py:163
msgid "ip_network needs an IP, a network and a netmask"
msgstr ""
-#: tiramisu/option/ipoption.py:163
+#: tiramisu/option/ipoption.py:169
msgid "\"{4}\" is not in network \"{0}\"/\"{1}\" (\"{2}\"/\"{3}\")"
msgstr ""
@@ -580,51 +592,51 @@ msgstr ""
msgid "malformed requirements option \"{0}\" must not be in follower for \"{1}\""
msgstr ""
-#: tiramisu/option/netmaskoption.py:34
+#: tiramisu/option/netmaskoption.py:35
msgid "netmask address"
msgstr ""
-#: tiramisu/option/netmaskoption.py:59
+#: tiramisu/option/netmaskoption.py:60
msgid "network_netmask needs a network and a netmask"
msgstr ""
-#: tiramisu/option/netmaskoption.py:68
+#: tiramisu/option/netmaskoption.py:69
msgid "with netmask \"{0}\" (\"{1}\")"
msgstr ""
-#: tiramisu/option/netmaskoption.py:71
+#: tiramisu/option/netmaskoption.py:72
msgid "with network \"{0}\" (\"{1}\")"
msgstr ""
-#: tiramisu/option/netmaskoption.py:82
+#: tiramisu/option/netmaskoption.py:83
msgid "ip_netmask needs an IP and a netmask"
msgstr ""
-#: tiramisu/option/netmaskoption.py:90
+#: tiramisu/option/netmaskoption.py:91
msgid "this is a network with netmask \"{0}\" (\"{1}\")"
msgstr ""
-#: tiramisu/option/netmaskoption.py:94
+#: tiramisu/option/netmaskoption.py:95
msgid "this is a broadcast with netmask \"{0}\" (\"{1}\")"
msgstr ""
-#: tiramisu/option/netmaskoption.py:99
+#: tiramisu/option/netmaskoption.py:100
msgid "IP \"{0}\" (\"{1}\") is the network"
msgstr ""
-#: tiramisu/option/netmaskoption.py:103
+#: tiramisu/option/netmaskoption.py:104
msgid "IP \"{0}\" (\"{1}\") is the broadcast"
msgstr ""
-#: tiramisu/option/networkoption.py:31
+#: tiramisu/option/networkoption.py:32
msgid "network address"
msgstr ""
-#: tiramisu/option/networkoption.py:90
+#: tiramisu/option/networkoption.py:91
msgid "shouldn't be reserved network"
msgstr ""
-#: tiramisu/option/networkoption.py:92
+#: tiramisu/option/networkoption.py:93
msgid "mustn't be reserved network"
msgstr ""
@@ -652,79 +664,79 @@ msgstr ""
msgid "invalid default_multi value \"{0}\" for option \"{1}\", must be a list for a submulti"
msgstr ""
-#: tiramisu/option/option.py:255
+#: tiramisu/option/option.py:259
msgid "invalid value \"{}\", this value is already in \"{}\""
msgstr ""
-#: tiramisu/option/option.py:285
+#: tiramisu/option/option.py:289
msgid "which must not be a list"
msgstr ""
-#: tiramisu/option/option.py:319 tiramisu/option/option.py:328
+#: tiramisu/option/option.py:323 tiramisu/option/option.py:332
msgid "which must be a list"
msgstr ""
-#: tiramisu/option/option.py:333
+#: tiramisu/option/option.py:337
msgid "which \"{}\" must be a list of list"
msgstr ""
-#: tiramisu/option/option.py:375
+#: tiramisu/option/option.py:379
msgid "default value not allowed if option \"{0}\" is calculated"
msgstr ""
-#: tiramisu/option/option.py:423
+#: tiramisu/option/option.py:427
msgid "'{0}' ({1}) cannot add consistency, option is read-only"
msgstr ""
-#: tiramisu/option/option.py:431
+#: tiramisu/option/option.py:435
msgid "consistency {0} not available for this option"
msgstr ""
-#: tiramisu/option/option.py:438
+#: tiramisu/option/option.py:442
msgid "unknown parameter {0} in consistency"
msgstr ""
-#: tiramisu/option/option.py:550 tiramisu/option/option.py:555
+#: tiramisu/option/option.py:554 tiramisu/option/option.py:559
msgid "cannot add consistency with submulti option"
msgstr ""
-#: tiramisu/option/option.py:556
+#: tiramisu/option/option.py:560
msgid "consistency must be set with an option, not {}"
msgstr ""
-#: tiramisu/option/option.py:559 tiramisu/option/option.py:567
+#: tiramisu/option/option.py:563 tiramisu/option/option.py:571
msgid "almost one option in consistency is in a dynoptiondescription but not all"
msgstr ""
-#: tiramisu/option/option.py:563
+#: tiramisu/option/option.py:567
msgid "option in consistency must be in same dynoptiondescription"
msgstr ""
-#: tiramisu/option/option.py:570
+#: tiramisu/option/option.py:574
msgid "cannot add consistency with itself"
msgstr ""
-#: tiramisu/option/option.py:572
+#: tiramisu/option/option.py:576
msgid "every options in consistency must be multi or none"
msgstr ""
-#: tiramisu/option/option.py:612
+#: tiramisu/option/option.py:616
msgid "unexpected length of \"{}\" in constency \"{}\", should be \"{}\""
msgstr ""
-#: tiramisu/option/option.py:712
+#: tiramisu/option/option.py:716
msgid "should be different from the value of \"{}\""
msgstr ""
-#: tiramisu/option/option.py:714
+#: tiramisu/option/option.py:718
msgid "must be different from the value of \"{}\""
msgstr ""
-#: tiramisu/option/option.py:717
+#: tiramisu/option/option.py:721
msgid "value for {} should be different"
msgstr ""
-#: tiramisu/option/option.py:719
+#: tiramisu/option/option.py:723
msgid "value for {} must be different"
msgstr ""
@@ -741,7 +753,7 @@ msgstr ""
msgid "the dynoption \"{0}\" cannot have \"force_store_value\" property"
msgstr ""
-#: tiramisu/option/optiondescription.py:97 tiramisu/setting.py:643
+#: tiramisu/option/optiondescription.py:97 tiramisu/setting.py:665
msgid "a leader ({0}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\""
msgstr ""
@@ -797,35 +809,35 @@ msgstr ""
msgid "group_type: {0} not allowed"
msgstr ""
-#: tiramisu/option/passwordoption.py:31
+#: tiramisu/option/passwordoption.py:32
msgid "password"
msgstr ""
-#: tiramisu/option/portoption.py:43
+#: tiramisu/option/portoption.py:44
msgid "port"
msgstr ""
-#: tiramisu/option/portoption.py:80
+#: tiramisu/option/portoption.py:81
msgid "inconsistency in allowed range"
msgstr ""
-#: tiramisu/option/portoption.py:85
+#: tiramisu/option/portoption.py:86
msgid "max value is empty"
msgstr ""
-#: tiramisu/option/portoption.py:110
+#: tiramisu/option/portoption.py:111
msgid "range must have two values only"
msgstr ""
-#: tiramisu/option/portoption.py:112
+#: tiramisu/option/portoption.py:113
msgid "first port in range must be smaller than the second one"
msgstr ""
-#: tiramisu/option/portoption.py:122
+#: tiramisu/option/portoption.py:123
msgid "must be an integer between {0} and {1}"
msgstr ""
-#: tiramisu/option/stroption.py:32
+#: tiramisu/option/stroption.py:33
msgid "string"
msgstr ""
@@ -837,23 +849,23 @@ msgstr ""
msgid "unknown option \"{0}\" in syndynoptiondescription \"{1}\""
msgstr ""
-#: tiramisu/option/urloption.py:33
+#: tiramisu/option/urloption.py:34
msgid "URL"
msgstr ""
-#: tiramisu/option/urloption.py:43
+#: tiramisu/option/urloption.py:44
msgid "must start with http:// or https://"
msgstr ""
-#: tiramisu/option/urloption.py:61
+#: tiramisu/option/urloption.py:62
msgid "port must be an between 0 and 65536"
msgstr ""
-#: tiramisu/option/urloption.py:70
+#: tiramisu/option/urloption.py:71
msgid "must ends with a valid resource name"
msgstr ""
-#: tiramisu/option/usernameoption.py:31
+#: tiramisu/option/usernameoption.py:32
msgid "username"
msgstr ""
@@ -865,47 +877,55 @@ msgstr ""
msgid "can't unbind {0}"
msgstr ""
-#: tiramisu/setting.py:518
+#: tiramisu/setting.py:519
msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'"
msgstr ""
-#: tiramisu/setting.py:566
+#: tiramisu/setting.py:582
msgid "cannot access to option \"{0}\" because required option \"{1}\" has {2} {3}"
msgstr ""
-#: tiramisu/setting.py:590
+#: tiramisu/setting.py:608
+msgid "the calculated value is {0}"
+msgstr ""
+
+#: tiramisu/setting.py:610
+msgid "the calculated value is not {0}"
+msgstr ""
+
+#: tiramisu/setting.py:614
msgid "the value of \"{0}\" is {1}"
msgstr ""
-#: tiramisu/setting.py:592
+#: tiramisu/setting.py:616
msgid "the value of \"{0}\" is not {1}"
msgstr ""
-#: tiramisu/setting.py:633
+#: tiramisu/setting.py:655
msgid "cannot set property {} for option \"{}\" this property is calculated"
msgstr ""
-#: tiramisu/setting.py:638
+#: tiramisu/setting.py:660
msgid "can't assign property to the symlinkoption \"{}\""
msgstr ""
-#: tiramisu/setting.py:670
+#: tiramisu/setting.py:692
msgid "permissive must be a frozenset"
msgstr ""
-#: tiramisu/setting.py:674
+#: tiramisu/setting.py:696
msgid "can't assign permissive to the symlinkoption \"{}\""
msgstr ""
-#: tiramisu/setting.py:681
+#: tiramisu/setting.py:703
msgid "cannot add those permissives: {0}"
msgstr ""
-#: tiramisu/setting.py:698
+#: tiramisu/setting.py:720
msgid "can't reset properties to the symlinkoption \"{}\""
msgstr ""
-#: tiramisu/setting.py:713
+#: tiramisu/setting.py:735
msgid "can't reset permissives to the symlinkoption \"{}\""
msgstr ""
@@ -939,19 +959,19 @@ msgstr ""
msgid "cannot change setting when connexion is already opened"
msgstr ""
-#: tiramisu/value.py:424
+#: tiramisu/value.py:427
msgid "can't set owner for the symlinkoption \"{}\""
msgstr ""
-#: tiramisu/value.py:427 tiramisu/value.py:639
+#: tiramisu/value.py:430 tiramisu/value.py:642
msgid "set owner \"{0}\" is forbidden"
msgstr ""
-#: tiramisu/value.py:430
+#: tiramisu/value.py:433
msgid "no value for {0} cannot change owner to {1}"
msgstr ""
-#: tiramisu/value.py:508
+#: tiramisu/value.py:511
msgid "index \"{}\" is higher than the length \"{}\" for option \"{}\""
msgstr ""
diff --git a/tiramisu/storage/cache/__init__.py b/tiramisu/storage/cache/__init__.py
new file mode 100644
index 0000000..e69de29