From 3ed55fdc2973c0dc7a8c42df32a73f2a2523ab43 Mon Sep 17 00:00:00 2001 From: Yann Date: Wed, 20 Apr 2016 11:07:52 +0200 Subject: [PATCH] Some modifications + tests update for settings loader --- globconf.d/global.ini | 7 +++ lodel/editorial_model/components.py | 4 +- lodel/leapi/datahandlers/base_classes.py | 45 +++++++++++++++++++ lodel/settings/settings.py | 2 + lodel/settings/validator.py | 14 ++++++ .../complex.conf.d/file1.ini | 8 ++++ .../complex.conf.d/file2.ini | 9 ++++ .../complex.conf.d/file3.ini | 6 +++ tests/settings/test_settings.py | 1 + tests/settings/test_settings_loader.py | 39 ++++++++++++++++ 10 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 tests/settings/settings_examples/complex.conf.d/file1.ini create mode 100644 tests/settings/settings_examples/complex.conf.d/file2.ini create mode 100644 tests/settings/settings_examples/complex.conf.d/file3.ini diff --git a/globconf.d/global.ini b/globconf.d/global.ini index 82d2756..d0cbda1 100644 --- a/globconf.d/global.ini +++ b/globconf.d/global.ini @@ -1,3 +1,10 @@ [lodel2] lib_path = /home/yannweb/dev/lodel2/lodel2-git plugins_path = /home/yannweb/dev/lodel2/lodel2-git/plugins + +[lodel2.editorialmodel] +emfile = examples/em_test.pickle +emtranslator = picklefile +dyncode = lodel/leapi/dyncode.py +editormode = True +groups = diff --git a/lodel/editorial_model/components.py b/lodel/editorial_model/components.py index 79dba69..0c8519d 100644 --- a/lodel/editorial_model/components.py +++ b/lodel/editorial_model/components.py @@ -72,6 +72,7 @@ class EmClass(EmComponent): self.__fields = dict() ##@brief Property that represent a dict of all fields (the EmField defined in this class and all its parents) + # @todo use Settings.editorialmodel.groups to determine wich fields should be returned @property def __all_fields(self): res = dict() @@ -97,6 +98,7 @@ class EmClass(EmComponent): # @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance # @param no_parents bool : If True returns only fields defined is this class and not the one defined in parents classes # @return A list on EmFields instances (if uid is None) else return an EmField instance + # @todo use Settings.editorialmodel.groups to determine wich fields should be returned def fields(self, uid = None, no_parents = False): fields = self.__fields if no_parents else self.__all_fields try: @@ -253,7 +255,7 @@ class EmGroup(object): res[new_app.uid] = new_app return res - ##@brief Returns EmGroup components + ##@brief Returns EmGroup components # @returns a copy of the set of components def components(self): return (self.__components).copy() diff --git a/lodel/leapi/datahandlers/base_classes.py b/lodel/leapi/datahandlers/base_classes.py index 4db61e7..f3d2e00 100644 --- a/lodel/leapi/datahandlers/base_classes.py +++ b/lodel/leapi/datahandlers/base_classes.py @@ -256,3 +256,48 @@ class MultipleRef(Reference): if self.max_item < len(value): return None, FieldValidationError("To many items") +## @brief Class designed to handle datas access will fieldtypes are constructing datas +# +# This class is designed to allow automatic scheduling of construct_data calls. +# +# In theory it's able to detect circular dependencies +# @todo test circular deps detection +# @todo test circulat deps false positiv +class DatasConstructor(object): + + ## @brief Init a DatasConstructor + # @param lec LeCrud : @ref LeObject child class + # @param datas dict : dict with field name as key and field values as value + # @param fields_handler dict : dict with field name as key and data handler instance as value + def __init__(self, leobject, datas, fields_handler): + ## Stores concerned class + self._leobject = leobject + ## Stores datas and constructed datas + self._datas = copy.copy(datas) + ## Stores fieldtypes + self._fields_handler = fields_handler + ## Stores list of fieldname for constructed datas + self._constructed = [] + ## Stores construct calls list + self._construct_calls = [] + + ## @brief Implements the dict.keys() method on instance + def keys(self): + return self._datas.keys() + + ## @brief Allows to access the instance like a dict + def __getitem__(self, fname): + if fname not in self._constructed: + if fname in self._construct_calls: + raise RuntimeError('Probably circular dependencies in fieldtypes') + cur_value = self._datas[fname] if fname in self._datas else None + self._datas[fname] = self._fields_handler[fname].construct_data(self._leobject, fname, self, cur_value) + self._constructed.append(fname) + return self._datas[fname] + + ## @brief Allows to set instance values like a dict + # @warning Should not append in theory + def __setitem__(self, fname, value): + self._datas[fname] = value + warnings.warn("Setting value of an DatasConstructor instance") + diff --git a/lodel/settings/settings.py b/lodel/settings/settings.py index 8efd8ee..e15f984 100644 --- a/lodel/settings/settings.py +++ b/lodel/settings/settings.py @@ -59,6 +59,8 @@ PYTHON_SYS_LIB_PATH = '/usr/local/lib/python{major}.{minor}/'.format( # # @todo handles default sections for variable sections (sections ending with # '.*') +# @todo delete the first stage, the lib path HAVE TO BE HARDCODED. In fact +#when we will run lodel in production the lodel2 lib will be in the python path class Settings(object): ##@brief global conf specsification (default_value + validator) diff --git a/lodel/settings/validator.py b/lodel/settings/validator.py index 6863bf6..31513a1 100644 --- a/lodel/settings/validator.py +++ b/lodel/settings/validator.py @@ -210,6 +210,8 @@ LODEL2_CONF_SPECS = { SettingValidator('bool')), 'plugins': ( "", SettingValidator('list')), + 'sitename': ( 'noname', + SettingValidator('strip')), }, 'lodel2.logging.*' : { 'level': ( 'ERROR', @@ -222,5 +224,17 @@ LODEL2_CONF_SPECS = { SettingValidator('int', none_is_valid = True)), 'maxbytes': ( None, SettingValidator('int', none_is_valid = True)), + }, + 'lodel2.editorialmodel': { + 'emfile': ( 'em.pickle', + SettingValidator('strip')), + 'emtranslator': ( 'picklefile', + SettingValidator('strip')), + 'dyncode': ( 'leapi_dyncode.py', + SettingValidator('strip')), + 'groups': ( '', + SettingValidator('list')), + 'editormode': ( False, + SettingValidator('bool')), } } diff --git a/tests/settings/settings_examples/complex.conf.d/file1.ini b/tests/settings/settings_examples/complex.conf.d/file1.ini new file mode 100644 index 0000000..54c2ac6 --- /dev/null +++ b/tests/settings/settings_examples/complex.conf.d/file1.ini @@ -0,0 +1,8 @@ +[lodel2.editorialmodel] +lib_path=/tmp +foo_bar=42 +foo.bar=1337 +[lodel2.bar.foo] +barfoo=42 +[lodel2.bar.bar] +toto=tata diff --git a/tests/settings/settings_examples/complex.conf.d/file2.ini b/tests/settings/settings_examples/complex.conf.d/file2.ini new file mode 100644 index 0000000..11fe7e1 --- /dev/null +++ b/tests/settings/settings_examples/complex.conf.d/file2.ini @@ -0,0 +1,9 @@ +[lodel2.editorialmodel] +example=foobar + +[lodel2.conf1] +conf_key=woot +conf_value=42 + +[lodel2.conf2] +conf_foo=bar diff --git a/tests/settings/settings_examples/complex.conf.d/file3.ini b/tests/settings/settings_examples/complex.conf.d/file3.ini new file mode 100644 index 0000000..c97a152 --- /dev/null +++ b/tests/settings/settings_examples/complex.conf.d/file3.ini @@ -0,0 +1,6 @@ +[lodel2.foo.bar] +barfoo=42 +[lodel2.foo.foo] +foobar=barfoo +[lodel2.bar.foo] +foobar=barfoo diff --git a/tests/settings/test_settings.py b/tests/settings/test_settings.py index 100b6e2..ada87cb 100644 --- a/tests/settings/test_settings.py +++ b/tests/settings/test_settings.py @@ -8,6 +8,7 @@ from lodel.settings.settings import Settings class SettingsTestCase(unittest.TestCase): + @unittest.skip('todo : write it') def test_init(self): settings = Settings('tests/settings/settings_tests.ini', 'tests/settings/settings_tests_conf.d') pass diff --git a/tests/settings/test_settings_loader.py b/tests/settings/test_settings_loader.py index 4050921..ef42c58 100644 --- a/tests/settings/test_settings_loader.py +++ b/tests/settings/test_settings_loader.py @@ -59,6 +59,45 @@ class SettingsLoaderTestCase(unittest.TestCase): value = loader.getoption('lodel2.foo.bar', 'foobar', dummy_validator) self.assertEqual(value, "hello world") + def test_getoption_complex(self): + """ Testing behavior of getoption with less simple files & confs """ + + expected = { + 'lodel2.editorialmodel': { + 'lib_path': '/tmp', + 'foo_bar': '42', + 'foo.bar': '1337', + 'example': 'foobar', + }, + 'lodel2.conf1': { + 'conf_key': 'woot', + 'conf_value': '42', + }, + 'lodel2.conf2': { + 'conf_foo': 'bar', + }, + 'lodel2.foo.foo': { + 'foobar': 'barfoo', + }, + 'lodel2.bar.foo': { + 'foobar': 'barfoo', + 'barfoo': '42', + }, + 'lodel2.bar.bar': { + 'toto': 'tata', + } + } + + loader = SettingsLoader('tests/settings/settings_examples/complex.conf.d') + for section in expected: + for key, expected_value in expected[section].items(): + value = loader.getoption( section, + key, + dummy_validator) + self.assertEqual(value, expected_value) + + + def test_variable_sections(self): """ Testing variable section recognition """ loader = SettingsLoader('tests/settings/settings_examples/var_sections.conf.d')