Explorar el Código

Merge branch 'newlodel' of git.labocleo.org:lodel2 into newlodel

prieto hace 7 años
padre
commit
8b00660dfd

+ 1
- 1
lodel/leapi/datahandlers/datas.py Ver fichero

@@ -26,7 +26,7 @@ build its content'
26 26
             datas[fname] for fname in self._field_list)
27 27
         if len(ret) > self.max_length:
28 28
             warnings.warn("Format field overflow. Truncating value")
29
-            ret = ret[:self.max_length-1]
29
+            ret = ret[:self.max_length]
30 30
         return ret
31 31
     
32 32
 ##@brief Varchar validated by a regex

+ 4
- 96
lodel/leapi/leobject.py Ver fichero

@@ -4,13 +4,14 @@ import importlib
4 4
 import warnings
5 5
 import copy
6 6
 
7
-from lodel.plugin import Plugin
8 7
 from lodel import logger
9 8
 from lodel.settings import Settings
10 9
 from lodel.settings.utils import SettingsError
11 10
 from .query import LeInsertQuery, LeUpdateQuery, LeDeleteQuery, LeGetQuery
12 11
 from .exceptions import *
12
+from lodel.plugin.exceptions import *
13 13
 from lodel.plugin.hooks import LodelHook
14
+from lodel.plugin import Plugin, DatasourcePlugin
14 15
 from lodel.leapi.datahandlers.base_classes import DatasConstructor
15 16
 
16 17
 ##@brief Stores the name of the field present in each LeObject that indicates
@@ -244,7 +245,7 @@ class LeObject(object):
244 245
         else:
245 246
             ro_ds, rw_ds = cls._datasource_name
246 247
         #Read only datasource initialisation
247
-        cls._ro_datasource = cls._init_datasource(ro_ds, True)
248
+        cls._ro_datasource = DatasourcePlugin.init_datasource(ro_ds, True)
248 249
         if cls._ro_datasource is None:
249 250
             log_msg = "No read only datasource set for LeObject %s"
250 251
             log_msg %= cls.__name__
@@ -254,7 +255,7 @@ class LeObject(object):
254 255
             log_msg %= (ro_ds, cls.__name__)
255 256
             logger.debug(log_msg)
256 257
         #Read write datasource initialisation
257
-        cls._rw_datasource = cls._init_datasource(rw_ds, False)
258
+        cls._rw_datasource = DatasourcePlugin.init_datasource(rw_ds, False)
258 259
         if cls._ro_datasource is None:
259 260
             log_msg = "No read/write datasource set for LeObject %s"
260 261
             log_msg %= cls.__name__
@@ -264,99 +265,6 @@ class LeObject(object):
264 265
             log_msg %= (ro_ds, cls.__name__)
265 266
             logger.debug(log_msg)
266 267
         
267
-
268
-    ##@brief Replace the _datasource attribute value by a datasource instance
269
-    #
270
-    #This method is used once at dyncode load to replace the datasource string
271
-    #by a datasource instance to avoid doing this operation for each query
272
-    #@param ds_name str : The name of the datasource to instanciate
273
-    #@param ro bool : if true initialise the _ro_datasource attribute else
274
-    #initialise _rw_datasource attribute
275
-    #@throw SettingsError if an error occurs
276
-    @classmethod
277
-    def _init_datasource(cls, ds_name, ro):
278
-        expt_msg = "In LeAPI class '%s' " % cls.__name__
279
-        if ds_name not in Settings.datasources._fields:
280
-            #Checking that datasource exists
281
-            expt_msg += "Unknown or unconfigured datasource %s for class %s"
282
-            expt_msg %= (ds_name, cls.__name__)
283
-            raise SettingsError(expt_msg)
284
-        try:
285
-            #fetching plugin name
286
-            ds_plugin_name, ds_identifier = cls._get_ds_plugin_name(ds_name, ro)
287
-        except NameError:
288
-            expt_msg += "Datasource %s is missconfigured, missing identifier."
289
-            expt_msg %= ds_name
290
-            raise SettingsError(expt_msg)
291
-        except RuntimeError:
292
-            expt_msg += "Error in datasource %s configuration. Trying to use \
293
-a read only as a read&write datasource"
294
-            expt_msg %= ds_name
295
-            raise SettingsError(expt_msg)
296
-        except ValueError as e:
297
-            expt_msg += str(e)
298
-            raise SettingsError(expt_msg)
299
-        
300
-        try:
301
-            ds_conf = cls._get_ds_connection_conf(ds_identifier, ds_plugin_name)
302
-        except NameError as e:
303
-            expt_msg += str(e)
304
-            raise SettingsError(expt_msg)
305
-        #Checks that the datasource plugin exists
306
-        ds_plugin_module = Plugin.get(ds_plugin_name).loader_module()
307
-        try:
308
-            datasource_class = getattr(ds_plugin_module, "Datasource")
309
-        except AttributeError as e:
310
-            expt_msg += "The datasource plugin %s seems to be invalid. Error \
311
-raised when trying to import Datasource"
312
-            expt_msg %= ds_identifier
313
-            raise SettingsError(expt_msg)
314
-
315
-        return datasource_class(**ds_conf)
316
-
317
-    ##@brief Try to fetch a datasource configuration
318
-    #@param ds_identifier str : datasource name
319
-    #@param ds_plugin_name : datasource plugin name
320
-    #@return a dict containing datasource initialisation options
321
-    #@throw NameError if a datasource plugin or instance cannot be found
322
-    @staticmethod
323
-    def _get_ds_connection_conf(ds_identifier,ds_plugin_name):
324
-        if ds_plugin_name not in Settings.datasource._fields:
325
-            msg = "Unknown or unconfigured datasource plugin %s"
326
-            msg %= ds_plugin
327
-            raise NameError(msg)
328
-        ds_conf = getattr(Settings.datasource, ds_plugin_name)
329
-        if ds_identifier not in ds_conf._fields:
330
-            msg = "Unknown or unconfigured datasource instance %s"
331
-            msg %= ds_identifier
332
-            raise NameError(msg)
333
-        ds_conf = getattr(ds_conf, ds_identifier)
334
-        return {k: getattr(ds_conf,k) for k in ds_conf._fields }
335
-
336
-    ##@brief fetch datasource plugin name
337
-    #@param ds_name str : datasource name
338
-    #@param ro bool : if true consider the datasource as read only
339
-    #@return a tuple(DATASOURCE_PLUGIN_NAME, DATASOURCE_CONNECTION_NAME)
340
-    #@throw NameError if datasource identifier not found
341
-    #@throw RuntimeError if datasource is read_only but ro flag was false
342
-    @staticmethod
343
-    def _get_ds_plugin_name(ds_name, ro):
344
-        datasource_orig_name = ds_name
345
-        # fetching connection identifier given datasource name
346
-        ds_identifier = getattr(Settings.datasources, ds_name)
347
-        read_only = getattr(ds_identifier, 'read_only')
348
-        try:
349
-            ds_identifier = getattr(ds_identifier, 'identifier')
350
-        except NameError as e:
351
-            raise e
352
-        if read_only and not ro:
353
-            raise RuntimeError()
354
-        res = ds_identifier.split('.')
355
-        if len(res) != 2:
356
-            raise ValueError("expected value for identifier is like \
357
-DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
358
-        return res
359
-    
360 268
     ##@brief Return the uid of the current LeObject instance
361 269
     #@return the uid value
362 270
     #@warning Broke multiple uid capabilities

+ 1
- 0
lodel/plugin/__init__.py Ver fichero

@@ -41,3 +41,4 @@
41 41
 
42 42
 from .hooks import LodelHook
43 43
 from .plugins import Plugin, CustomMethod
44
+from .datasource_plugin import DatasourcePlugin

+ 133
- 0
lodel/plugin/datasource_plugin.py Ver fichero

@@ -0,0 +1,133 @@
1
+from .plugins import Plugin
2
+from .exceptions import *
3
+from lodel.settings.validator import SettingValidator
4
+
5
+##@brief Designed to handles datasources plugins
6
+#
7
+#A datasource provide data access to LeAPI typically a connector on a DB
8
+#or an API
9
+#@note For the moment implementation is done with a retro-compatibilities
10
+#priority and not with a convenience priority.
11
+#@todo Refactor and rewrite lodel2 datasource handling
12
+class DatasourcePlugin(Plugin):
13
+    
14
+    ##@brief Stores confspecs indicating where DatasourcePlugin list is stored
15
+    _plist_confspecs = {
16
+        'section': 'lodel2',
17
+        'key': 'datasources',
18
+        'default': None,
19
+        'validator': SettingValidator('list', none_is_valid = False) }
20
+    
21
+    def __init__(self, name):
22
+        super().__init__(name)
23
+        self.__datasource_cls = None
24
+
25
+    def datasource_cls(self):
26
+        if self.__datasource_cls is None:
27
+            self.__datasource_cls = self.loader_module().Datasource
28
+        return self.__datasource_cls
29
+
30
+    def migration_handler(self):
31
+        return self.loader_module().migration_handler_class()
32
+
33
+    @classmethod
34
+    def plist_confspec(cls):
35
+        return copy.copy(cls._plist_confspecs)
36
+
37
+    ##@brief Return an initialized Datasource instance
38
+    #@param ds_name str : The name of the datasource to instanciate
39
+    #@param ro bool
40
+    #@return A properly initialized Datasource instance
41
+    #@throw SettingsError if an error occurs in settings
42
+    #@throw DatasourcePluginError for various errors
43
+    @classmethod
44
+    def init_datasource(cls, ds_name, ro):
45
+        plugin_name, ds_identifier = cls.plugin_name(ds_name, ro)
46
+        ds_conf = cls._get_ds_connection_conf(ds_identifier, plugin_name)
47
+        ds_cls = cls.get_datasource(plugin_name)
48
+        return ds_cls(**ds_conf)
49
+
50
+    ##@brief Given a datasource name returns a DatasourcePlugin name
51
+    #@param ds_name str : datasource name
52
+    #@param ro bool : if true consider the datasource as readonly
53
+    #@return a DatasourcePlugin name
54
+    #@throw PluginError if datasource name not found
55
+    #@throw DatasourcePermError if datasource is read_only but ro flag arg is
56
+    #false
57
+    @staticmethod
58
+    def plugin_name(ds_name, ro):
59
+        from lodel.settings import Settings
60
+        # fetching connection identifier given datasource name
61
+        try:
62
+            ds_identifier = getattr(Settings.datasources, ds_name)
63
+        except (NameError, AttributeError):
64
+            raise DatasourcePluginError("Unknown or unconfigured datasource \
65
+'%s'" % ds_name)
66
+        # fetching read_only flag
67
+        try:
68
+            read_only = getattr(ds_identifier, 'read_only')
69
+        except (NameError, AttributeError):
70
+            raise SettingsError("Malformed datasource configuration for '%s' \
71
+: missing read_only key" % ds_name)
72
+        # fetching datasource identifier
73
+        try:
74
+            ds_identifier = getattr(ds_identifier, 'identifier')
75
+        except (NameError,AttributeError) as e:
76
+            raise SettingsError("Malformed datasource configuration for '%s' \
77
+: missing identifier key" % ds_name)
78
+        # settings and ro arg consistency check
79
+        if read_only and not ro:
80
+            raise DatasourcePluginError("ro argument was set to False but \
81
+True found in settings for datasource '%s'" % ds_name)
82
+        res = ds_identifier.split('.')
83
+        if len(res) != 2:
84
+            raise SettingsError("expected value for identifier is like \
85
+DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
86
+        return res
87
+
88
+    ##@brief Try to fetch a datasource configuration
89
+    #@param ds_identifier str : datasource name
90
+    #@param ds_plugin_name : datasource plugin name
91
+    #@return a dict containing datasource initialisation options
92
+    #@throw NameError if a datasource plugin or instance cannot be found
93
+    @staticmethod
94
+    def _get_ds_connection_conf(ds_identifier,ds_plugin_name):
95
+        from lodel.settings import Settings
96
+        if ds_plugin_name not in Settings.datasource._fields:
97
+            msg = "Unknown or unconfigured datasource plugin %s"
98
+            msg %= ds_plugin
99
+            raise DatasourcePluginError(msg)
100
+        ds_conf = getattr(Settings.datasource, ds_plugin_name)
101
+        if ds_identifier not in ds_conf._fields:
102
+            msg = "Unknown or unconfigured datasource instance %s"
103
+            msg %= ds_identifier
104
+            raise DatasourcePluginError(msg)
105
+        ds_conf = getattr(ds_conf, ds_identifier)
106
+        return {k: getattr(ds_conf,k) for k in ds_conf._fields }
107
+
108
+    ##@brief DatasourcePlugin instance accessor
109
+    #@param ds_name str : plugin name
110
+    #@return a DatasourcePlugin instance
111
+    #@throw PluginError if no plugin named ds_name found
112
+    #@throw PluginTypeError if ds_name ref to a plugin that is not a 
113
+    #DatasourcePlugin
114
+    @classmethod
115
+    def get(cls, ds_name):
116
+        pinstance = super().get(ds_name) #Will raise PluginError if bad name
117
+        if not isinstance(pinstance, DatasourcePlugin):
118
+           raise PluginTypeErrror("A name of a DatasourcePlugin was excepted \
119
+but %s is a %s" % (ds_name, pinstance.__class__.__name__))
120
+        return pinstance
121
+
122
+    ##@brief Return a datasource class given a datasource name
123
+    #@param ds_name str : datasource plugin name
124
+    #@throw PluginError if ds_name is not an existing plugin name
125
+    #@throw PluginTypeError if ds_name is not the name of a DatasourcePlugin
126
+    @classmethod
127
+    def get_datasource(cls, ds_plugin_name):
128
+        return cls.get(ds_plugin_name).datasource_cls()
129
+
130
+    @classmethod
131
+    def get_migration_handler(cls, ds_plugin_name):
132
+        return cls.get(ds_plugin_name).migration_handler_class()
133
+ 

+ 6
- 0
lodel/plugin/exceptions.py Ver fichero

@@ -1,5 +1,11 @@
1 1
 class PluginError(Exception):
2 2
     pass
3 3
 
4
+class PluginTypeErrror(PluginError):
5
+    pass
6
+
4 7
 class LodelScriptError(Exception):
5 8
     pass
9
+
10
+class DatasourcePluginError(PluginError):
11
+    pass

+ 116
- 17
lodel/plugin/plugins.py Ver fichero

@@ -8,8 +8,9 @@ import json
8 8
 from importlib.machinery import SourceFileLoader, SourcelessFileLoader
9 9
 
10 10
 import plugins
11
-from .exceptions import *
12 11
 from lodel import logger
12
+from lodel.settings.utils import SettingsError
13
+from .exceptions import *
13 14
 
14 15
 ## @package lodel.plugins Lodel2 plugins management
15 16
 #
@@ -26,7 +27,7 @@ VIRTUAL_TEMP_PACKAGE_NAME = 'lodel.plugin_tmp'
26 27
 ##@brief Plugin init filename
27 28
 INIT_FILENAME = '__init__.py' # Loaded with settings
28 29
 PLUGIN_NAME_VARNAME = '__plugin_name__'
29
-PLUGIN_TYPE_VARNAME = '__type__'
30
+PLUGIN_TYPE_VARNAME = '__plugin_type__'
30 31
 PLUGIN_VERSION_VARNAME = '__version__'
31 32
 CONFSPEC_FILENAME_VARNAME = '__confspec__'
32 33
 CONFSPEC_VARNAME = 'CONFSPEC'
@@ -41,8 +42,8 @@ DEFAULT_PLUGINS_PATH_LIST = ['./plugins']
41 42
 MANDATORY_VARNAMES = [PLUGIN_NAME_VARNAME, LOADER_FILENAME_VARNAME, 
42 43
     PLUGIN_VERSION_VARNAME]
43 44
 
44
-EXTENSIONS = 'default'
45
-PLUGINS_TYPES = [EXTENSIONS, 'datasource', 'session_handler', 'ui']
45
+DEFAULT_PLUGIN_TYPE = 'extension'
46
+PLUGINS_TYPES = [DEFAULT_PLUGIN_TYPE, 'datasource', 'session_handler', 'ui']
46 47
 
47 48
 
48 49
 ##@brief Describe and handle version numbers
@@ -147,6 +148,35 @@ to generic PluginVersion comparison function : '%s'" % cmp_fun_name)
147 148
         return {'major': self.major, 'minor': self.minor,
148 149
             'revision': self.revision}
149 150
 
151
+##@brief Stores plugin class registered
152
+__all_ptypes = list()
153
+
154
+##@brief Plugin metaclass that allows to "catch" child class
155
+#declaration
156
+#
157
+#Automatic script registration on child class declaration
158
+class MetaPlugType(type):
159
+    
160
+    def __init__(self, name, bases, attrs):
161
+        #Here we can store all child classes of Plugin
162
+        super().__init__(name, bases, attrs)
163
+        if len(bases) == 1 and bases[0] == object:
164
+            print("Dropped : ", name, bases)
165
+            return
166
+        self.__register_types()
167
+        #list_name= [cls.__name__ for cls in __all_ptypes] 
168
+        #if self.name in list_name:
169
+        #    return
170
+        #else:
171
+        #    plug_type_register(self)
172
+
173
+    def __register_types(self):
174
+        plug_type_register(self)
175
+
176
+def plug_type_register(cls):
177
+    __all_ptypes.append(cls)
178
+    logger.info("New child class registered : %s" % cls.__name__)
179
+
150 180
 
151 181
 ##@brief Handle plugins
152 182
 #
@@ -157,7 +187,7 @@ to generic PluginVersion comparison function : '%s'" % cmp_fun_name)
157 187
 # 1. Settings call start method to instanciate all plugins found in confs
158 188
 # 2. Settings fetch all confspecs
159 189
 # 3. the loader call load_all to register hooks etc
160
-class Plugin(object):
190
+class Plugin(object, metaclass=MetaPlugType):
161 191
     
162 192
     ##@brief Stores plugin directories paths
163 193
     _plugin_directories = None
@@ -171,6 +201,9 @@ class Plugin(object):
171 201
 
172 202
     ##@brief Attribute that stores plugins list from discover cache file
173 203
     _plugin_list = None
204
+    
205
+    ##@brief Store dict representation of discover cache content
206
+    _discover_cache = None
174 207
 
175 208
     ##@brief Plugin class constructor
176 209
     #
@@ -248,7 +281,7 @@ init file. Malformed plugin"
248 281
         try:
249 282
             self.__type = getattr(self.module, PLUGIN_TYPE_VARNAME)
250 283
         except AttributeError:
251
-            self.__type = EXTENSIONS
284
+            self.__type = DEFAULT_PLUGIN_TYPE
252 285
         self.__type = str(self.__type).lower()
253 286
         if self.__type not in PLUGINS_TYPES:
254 287
             raise PluginError("Unknown plugin type '%s'" % self.__type)
@@ -426,6 +459,50 @@ name differ from the one found in plugin's init file"
426 459
     def confspecs(self):
427 460
         return copy.copy(self.__confspecs)
428 461
 
462
+    ##@brief Retrieves plugin list confspecs
463
+    #
464
+    #This method ask for each Plugin child class the confspecs specifying where
465
+    #the wanted plugin list is stored. (For example DatasourcePlugin expect
466
+    #that a list of ds plugin to load stored in lodel2 section, datasources key
467
+    # etc...
468
+    @classmethod
469
+    def plugin_list_confspec(cls):
470
+        from lodel.settings.validator import confspec_append
471
+        res = dict()
472
+        for pcls in cls.plugin_types():
473
+            plcs = pcls.plist_confspec()
474
+            confspec_append(res, plcs)
475
+        return res
476
+
477
+    ##@brief Attempt to read plugin discover cache
478
+    #@note If no cache yet make a discover with default plugin directory
479
+    #@return a dict (see @ref _discover() )
480
+    @classmethod
481
+    def plugin_cache(cls):
482
+        if cls._discover_cache is None:
483
+            if not os.path.isfile(DISCOVER_CACHE_FILENAME):
484
+                cls.discover()
485
+            with open(DISCOVER_CACHE_FILENAME) as pdcache_fd:
486
+                res = json.load(pdcache_fd)
487
+            #Check consistency of loaded cache
488
+            if 'path_list' not in res:
489
+                raise LodelFatalError("Malformed plugin's discover cache file \
490
+: '%s'. Unable to find plugin's paths list." % DISCOVER_CACHE_FILENAME)
491
+            expected_keys = ['type', 'path', 'version']
492
+            for pname in res['plugins']:
493
+                for ekey in expected_keys:
494
+                    if ekey not in res['plugins'][pname]:
495
+                        #Bad cache !
496
+                        logger.warning("Malformed plugin's discover cache \
497
+file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
498
+                        cls._discover_cache = cls.discover(res['path_list'])
499
+                        break
500
+            else:
501
+                #The cache we just read was OK
502
+                cls._discover_cache = res
503
+                
504
+        return cls._discover_cache
505
+
429 506
     ##@brief Register a new plugin
430 507
     # 
431 508
     #@param plugin_name str : The plugin name
@@ -433,11 +510,23 @@ name differ from the one found in plugin's init file"
433 510
     #@throw PluginError
434 511
     @classmethod
435 512
     def register(cls, plugin_name):
513
+        from .datasource_plugin import DatasourcePlugin
436 514
         if plugin_name in cls._plugin_instances:
437 515
             msg = "Plugin allready registered with same name %s"
438 516
             msg %= plugin_name
439 517
             raise PluginError(msg)
440
-        plugin = cls(plugin_name)
518
+        #Here we check that previous discover found a plugin with that name
519
+        pdcache = cls.plugin_cache()
520
+        if plugin_name not in pdcache['plugins']:
521
+            raise PluginError("No plugin named %s found" % plugin_name)
522
+        pinfos = pdcache['plugins'][plugin_name]
523
+        ptype = pinfos['type']
524
+        if ptype == 'datasource':
525
+            pcls = DatasourcePlugin
526
+        else:
527
+            pcls = cls
528
+        print(plugin_name, ptype, pcls)
529
+        plugin = pcls(plugin_name)
441 530
         cls._plugin_instances[plugin_name] = plugin
442 531
         logger.debug("Plugin %s available." % plugin)
443 532
         return plugin
@@ -530,8 +619,7 @@ name differ from the one found in plugin's init file"
530 619
         result = dict()
531 620
         for pinfos in tmp_res:
532 621
             pname = pinfos['name']
533
-            if (
534
-                    pname in result 
622
+            if (    pname in result 
535 623
                     and pinfos['version'] > result[pname]['version'])\
536 624
                 or pname not in result:
537 625
                 result[pname] = pinfos
@@ -566,6 +654,11 @@ name differ from the one found in plugin's init file"
566 654
         cls._plugin_list = infos['plugins']
567 655
         return cls._plugin_list
568 656
 
657
+    ##@brief Return a list of child Class Plugin
658
+    @classmethod
659
+    def plugin_types(cls):
660
+        return cls.__all_ptypes
661
+
569 662
     ##@brief Attempt to open and load plugin discover cache
570 663
     #@return discover cache
571 664
     #@throw PluginError when open or load fails
@@ -617,16 +710,23 @@ name differ from the one found in plugin's init file"
617 710
                 log_msg %= (attr_name, INIT_FILENAME)
618 711
                 logger.debug(log_msg)
619 712
                 return False
713
+        #Fetching plugin's version
620 714
         try:
621 715
             pversion = getattr(initmod, PLUGIN_VERSION_VARNAME)
622
-        except PluginError as e:
716
+        except (NameError, AttributeError) as e:
623 717
             msg = "Invalid plugin version found in %s : %s"
624 718
             msg %= (path, e)
625 719
             raise PluginError(msg)
720
+        #Fetching plugin's type
721
+        try:
722
+            ptype = getattr(initmod, PLUGIN_TYPE_VARNAME)
723
+        except (NameError, AttributeError) as e:
724
+            ptype = DEFAULT_PLUGIN_TYPE
626 725
         pname = getattr(initmod, PLUGIN_NAME_VARNAME)
627 726
         return {'name': pname,
628 727
             'version': pversion,
629
-            'path': path}
728
+            'path': path,
729
+            'type': ptype}
630 730
     
631 731
     ##@brief Import init file from a plugin path
632 732
     #@param path str : Directory path
@@ -795,14 +895,13 @@ with %s" % (custom_method._method_name, custom_method))
795 895
 
796 896
 class SessionHandler(Plugin):
797 897
     __instance = None
798
-
799
-    def __new__(cls):
800
-        if cls.__instance == None:
801
-            cls.instance == object.__new__(cls)
802
-        return cls.__instance
803 898
         
804 899
     def __init__(self, plugin_name):
805
-        super(Plugin, self).__init__(plugin_name)
900
+        if self.__instance is None:
901
+            super(Plugin, self).__init__(plugin_name)
902
+            self.__instance = True
903
+        else:
904
+            raise RuntimeError("A SessionHandler Plugin is already plug")
806 905
 
807 906
 class InterfacePlugin(Plugin):
808 907
     def __init__(self, plugin_name):

+ 1
- 1
lodel/settings/settings.py Ver fichero

@@ -128,7 +128,7 @@ class Settings(object, metaclass=MetaSettings):
128 128
         confkey=confname.rpartition('.')
129 129
         loader.setoption(confkey[0], confkey[2], confvalue, validator)
130 130
 
131
-    ##@brief This method handlers Settings instance bootstraping
131
+    ##@brief This method handles Settings instance bootstraping
132 132
     def __bootstrap(self):
133 133
         logger.debug("Settings bootstraping")
134 134
         lodel2_specs = LODEL2_CONF_SPECS

+ 27
- 10
lodel/settings/validator.py Ver fichero

@@ -240,20 +240,20 @@ def emfield_val(value):
240 240
     return value
241 241
 
242 242
 def plugin_val(value):
243
-    if spl = value.split('.')
243
+    spl = value.split('.')
244 244
     if len(spl) != 2:
245 245
         msg = "Expected a value in the form PLUGIN.TYPE but got : %s"
246 246
         raise SettingsValidationError(msg % value)
247 247
     value = tuple(spl)
248
-        #Late validation hook
249
-        @LodelHook('lodel2_dyncode_bootstraped')
250
-        def type_check(hookname, caller, payload):
251
-            from lodel import plugin
252
-            typesname = { cls.__name__.lower():cls for cls in plugin.PLUGINS_TYPE}
253
-            if value[1].lower() not in typesname:
254
-                msg = "Following plugin type do not exist in plugin list %s : %s"
255
-                raise SettingsValidationError(msg % value)
256
-            return value
248
+    #Late validation hook
249
+    @LodelHook('lodel2_dyncode_bootstraped')
250
+    def type_check(hookname, caller, payload):
251
+        from lodel import plugin
252
+        typesname = { cls.__name__.lower():cls for cls in plugin.PLUGINS_TYPE}
253
+        if value[1].lower() not in typesname:
254
+            msg = "Following plugin type do not exist in plugin list %s : %s"
255
+            raise SettingsValidationError(msg % value)
256
+        return value
257 257
     plug_type_val = plugin_val(value)
258 258
     return plug_type_val
259 259
 
@@ -352,6 +352,23 @@ SettingValidator.create_re_validator(
352 352
 #   Lodel 2 configuration specification
353 353
 #
354 354
 
355
+##@brief Append a piece of confspec
356
+#@note orig is modified during the process
357
+#@param orig dict : the confspec to update
358
+#@param upd dict : the confspec to add
359
+#@return new confspec
360
+def confspec_append(orig, upd):
361
+    for section in orig:
362
+        if section in upd:
363
+            orig[section].update(upd[section])
364
+        else:
365
+            orig[section] = upd[section]
366
+    return orig
367
+
368
+def confspec_add(orig, section, key, default, validator):
369
+    if section not in orig:
370
+        section[orig] = dict()
371
+
355 372
 ##@brief Global specifications for lodel2 settings
356 373
 LODEL2_CONF_SPECS = {
357 374
     'lodel2': {

+ 1
- 0
plugins/dummy_datasource/__init__.py Ver fichero

@@ -1,6 +1,7 @@
1 1
 from lodel.settings.validator import SettingValidator
2 2
 from .datasource import DummyDatasource as Datasource
3 3
 
4
+__plugin_type__ = 'datasource'
4 5
 __plugin_name__ = "dummy_datasource"
5 6
 __version__ = '0.0.1'
6 7
 __loader__ = 'main.py'

+ 1
- 0
plugins/mongodb_datasource/__init__.py Ver fichero

@@ -1,4 +1,5 @@
1 1
 #-*- coding: utf-8 -*-
2
+__plugin_type__ = 'datasource'
2 3
 __plugin_name__ = 'mongodb_datasource'
3 4
 __version__ = '0.0.1'
4 5
 __plugin_type__ = 'datasource'

+ 22
- 0
tests/datahandlers/test_concat.py Ver fichero

@@ -0,0 +1,22 @@
1
+import unittest
2
+
3
+from lodel.leapi.datahandlers.datas import Concat
4
+from lodel.editorial_model.components import EmClass
5
+
6
+class ConcatTestCase(unittest.TestCase):
7
+
8
+    def test_construct_data(self):
9
+        test_class = EmClass('testing', display_name='testing class')
10
+        test_class.new_field('field1', 'varchar')
11
+        test_class.new_field('field2', 'varchar')
12
+
13
+        test_concat = Concat(['field1', 'field2'], '*')
14
+        concat_string_value = test_concat.construct_data(test_class, 'field', {'field1': 'o'*5, 'field2': 'k'*4}, '')
15
+        self.assertEqual('%s*%s' % ('o'*5, 'k'*4), concat_string_value)
16
+
17
+        test_concat.max_length=10
18
+        concat_string_value = test_concat.construct_data(test_class, 'field', {'field1': 'o'*5, 'field2': 'k'*10}, '')
19
+        test_value = '%s*%s' % ('o'*5, 'k'*10)
20
+        self.assertNotEqual(test_value, concat_string_value)
21
+        self.assertEqual(len(concat_string_value), test_concat.max_length)
22
+        self.assertTrue(concat_string_value in test_value)

+ 28
- 0
tests/datahandlers/test_formatstring.py Ver fichero

@@ -0,0 +1,28 @@
1
+import unittest
2
+
3
+from lodel.leapi.datahandlers.datas import FormatString
4
+from lodel.editorial_model.components import EmClass
5
+
6
+
7
+class FormatStringTestCase(unittest.TestCase):
8
+
9
+    def test_construct_data(self):
10
+        test_class = EmClass('testing',display_name='testing class')
11
+        test_class.new_field('field1', 'varchar')
12
+        test_class.new_field('field2', 'varchar')
13
+
14
+        test_formatstring = FormatString('%s_%s',['field1', 'field2'], max_length=10)
15
+        formatted_string_value = test_formatstring.construct_data(test_class, 'field', {'field1': 'o'*5, 'field2': 'k'*4}, '')
16
+        self.assertEqual('%s_%s' % ('o'*5, 'k'*4), formatted_string_value)
17
+
18
+    def test_construct_too_long_data(self):
19
+        test_class = EmClass('testing', display_name='testing class')
20
+        test_class.new_field('field1', 'varchar')
21
+        test_class.new_field('field2', 'varchar')
22
+        test_formatstring = FormatString('%s-%s', ['field2', 'field1'], max_length=10)
23
+        formatted_string_value = test_formatstring.construct_data(test_class, 'field', {'field1': 'o'*300, 'field2': 'k'*500},'')
24
+        test_value = '%s-%s' % ('k'*500, 'o'*300)
25
+        self.assertNotEqual(test_value, formatted_string_value)
26
+        self.assertTrue(formatted_string_value in test_value)
27
+        self.assertTrue(len(formatted_string_value) == test_formatstring.max_length)
28
+        self.assertEqual(formatted_string_value, test_value[:test_formatstring.max_length])

Loading…
Cancelar
Guardar