Browse Source

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

m.orban 8 years ago
parent
commit
7066126683

+ 2
- 1
install/loader.py View File

@@ -32,10 +32,11 @@ if 'LODEL2_NO_SETTINGS_LOAD' not in os.environ:
32 32
     if not settings.started():
33 33
         settings('conf.d')
34 34
     from lodel.settings import Settings
35
-
35
+    
36 36
     #Starts hooks
37 37
     from lodel.plugin import LodelHook
38 38
     from lodel.plugin import core_hooks
39
+    from lodel.plugin import core_scripts
39 40
 
40 41
 def start():
41 42
     #Load plugins

+ 21
- 9
install/lodel_admin.py View File

@@ -4,14 +4,20 @@ import sys
4 4
 import os, os.path
5 5
 import argparse
6 6
 
7
-"""
8
-#Dirty hack to solve symlinks problems :
9
-#   When loader was imported the original one (LODEL_LIBDIR/install/loader)
10
-#   because lodel_admin.py is a symlink from this folder
11
-#Another solution can be delete loader from install folder
12
-sys.path[0] = os.getcwd()
13
-import loader
14
-"""
7
+##@brief Dirty hack to avoid problems with simlink to lodel2 lib folder
8
+#
9
+#In instance folder we got a loader.py (the one we want to import here when
10
+#writing "import loader". The problem is that lodel_admin.py is a simlink to
11
+#LODEL2LIB_FOLDER/install/lodel_admin.py . In this folder there is the 
12
+#generic loader.py template. And when writing "import loader" its 
13
+#LODEL2LIB_FOLDER/install/loader.py that gets imported.
14
+#
15
+#In order to solve this problem the _simlink_hack() function delete the
16
+#LODEL2LIB_FOLDER/install entry from sys.path
17
+#@note This problem will be solved once lodel lib dir will be in 
18
+#/usr/lib/python3/lodel
19
+def _simlink_hack():
20
+    sys.path[0] = os.getcwd()
15 21
 
16 22
 ## @brief Utility method to generate python code given an emfile and a
17 23
 # translator
@@ -141,4 +147,10 @@ def update_plugin_discover_cache(path_list = None):
141 147
     for pname, pinfos in res['plugins'].items():
142 148
         print("\t- %s %s -> %s" % (
143 149
             pname, pinfos['version'], pinfos['path']))
144
-    
150
+
151
+if __name__ == '__main__':
152
+    _simlink_hack()
153
+    import loader
154
+    loader.start()
155
+    from lodel.plugin.scripts import main_run
156
+    main_run()

+ 13
- 4
lodel/leapi/datahandlers/datas_base.py View File

@@ -13,13 +13,14 @@ class Boolean(DataField):
13 13
     ##@brief A boolean field
14 14
     def __init__(self, **kwargs):
15 15
         if 'check_data_value' not in kwargs:
16
-            kwargs['check_data_value'] = self.check_value
16
+            kwargs['check_data_value'] = self.check_data_value
17 17
         super().__init__(ftype='bool', **kwargs)
18 18
 
19 19
     def _check_data_value(self, value):
20 20
         error = None
21 21
         try:
22
-            value = bool(value)
22
+            if type(value) != bool:
23
+                raise TypeError()
23 24
         except(ValueError, TypeError):
24 25
             error = TypeError("The value '%s' is not, and will never, be a boolean" % value)
25 26
         return value, error
@@ -36,7 +37,11 @@ class Integer(DataField):
36 37
     def _check_data_value(self, value):
37 38
         error = None
38 39
         try:
39
-            value = int(value)
40
+            value = float(value)
41
+            if value % 1 == 0:
42
+                value = int(value)
43
+            else:
44
+                raise TypeError()
40 45
         except(ValueError, TypeError):
41 46
             error = TypeError("The value '%s' is not, and will never, be an integer" % value)
42 47
         return value, error
@@ -67,8 +72,12 @@ class Varchar(DataField):
67 72
         error = None
68 73
         try:
69 74
             value = str(value)
70
-        except(ValueError, TypeError):
75
+            if len(value) > self.max_length:
76
+                raise ValueError
77
+        except TypeError:
71 78
             error = TypeError("The value '%s' can't be a str" % value)
79
+        except ValueError:
80
+            error = ValueError("The value '%s' is longer than the maximum length of this field (%s)" % (value, self.max_length))
72 81
         return value, error
73 82
 
74 83
 ##@brief Data field designed to handle date & time 

+ 32
- 0
lodel/plugin/core_scripts.py View File

@@ -0,0 +1,32 @@
1
+import lodel.plugin.scripts as lodel_script
2
+
3
+##@brief Implements lodel_admin.py discover-plugin action
4
+#
5
+#In depth directory scan to find plugins.
6
+class DiscoverPlugin(lodel_script.LodelScript):
7
+    _action = 'discover-plugin'
8
+    _description = 'Walk through given folders looking for plugins'
9
+    
10
+    @classmethod
11
+    def argparser_config(cls, parser):
12
+        parser.add_argument('-d', '--directory',
13
+            help="Directory to walk through looking for lodel2 plugins",
14
+            nargs='+')
15
+        parser.add_argument('-l', '--list-only', default=False,
16
+            action = 'store_true',
17
+            help="Use this option to print a list of discovered plugins \
18
+without modifying existing cache")
19
+
20
+    @classmethod
21
+    def run(cls, args):
22
+        from lodel.plugin.plugins import Plugin
23
+        if args.directory is None or len(args.directory) == 0:
24
+            cls.help_exit("Specify a least one directory")
25
+        no_cache = args.list_only
26
+        res = Plugin.discover(args.directory, no_cache)
27
+        print("Found plugins in : %s" % ', '.join(args.directory))
28
+        for pname, pinfos in res['plugins'].items():
29
+            print("\t- %s(%s) in %s" % (
30
+                pname, pinfos['version'], pinfos['path']))
31
+            
32
+

+ 3
- 0
lodel/plugin/exceptions.py View File

@@ -1,2 +1,5 @@
1 1
 class PluginError(Exception):
2 2
     pass
3
+
4
+class LodelScriptError(Exception):
5
+    pass

+ 8
- 3
lodel/plugin/plugins.py View File

@@ -513,9 +513,13 @@ name differ from the one found in plugin's init file"
513 513
     
514 514
     ##@brief Reccursively walk throught paths to find plugin, then stores
515 515
     #found plugin in a file...
516
+    #@param paths list : list of directory paths
517
+    #@param no_cache bool : if true only return a list of found plugins 
518
+    #without modifying the cache file
516 519
     #@return a dict {'path_list': [...], 'plugins': { see @ref _discover }}
520
+    #@todo add max_depth and symlink following options
517 521
     @classmethod
518
-    def discover(cls, paths = None):
522
+    def discover(cls, paths = None, no_cache = False):
519 523
         logger.info("Running plugin discover")
520 524
         if paths is None:
521 525
             paths = DEFAULT_PLUGINS_PATH_LIST
@@ -536,8 +540,9 @@ name differ from the one found in plugin's init file"
536 540
                 pass
537 541
         result = {'path_list': paths, 'plugins': result}
538 542
         #Writing to cache
539
-        with open(DISCOVER_CACHE_FILENAME, 'w+') as pdcache:
540
-            pdcache.write(json.dumps(result))
543
+        if not no_cache:
544
+            with open(DISCOVER_CACHE_FILENAME, 'w+') as pdcache:
545
+                pdcache.write(json.dumps(result))
541 546
         return result
542 547
     
543 548
     ##@brief Return discover result

+ 162
- 0
lodel/plugin/scripts.py View File

@@ -0,0 +1,162 @@
1
+import argparse
2
+import sys
3
+
4
+from lodel import logger
5
+from lodel.exceptions import *
6
+
7
+##@brief Stores registered scripts
8
+__registered_scripts = dict()
9
+
10
+##@brief LodelScript metaclass that allows to "catch" child class
11
+#declaration
12
+#
13
+#Automatic script registration on child class declaration
14
+class MetaLodelScript(type):
15
+    
16
+    def __init__(self, name, bases, attrs):
17
+        #Here we can store all child classes of LodelScript
18
+        super().__init__(name, bases, attrs)
19
+        if len(bases) == 1 and bases[0] == object:
20
+            print("Dropped : ", name, bases)
21
+            return
22
+
23
+        self.__register_script(name)
24
+        #_action initialization
25
+        if self._action is None:
26
+            logger.warning("%s._action is None. Trying to use class name as \
27
+action identifier" % name)
28
+            self._action = name
29
+        self._action = self._action.lower()
30
+        if self._description is None:
31
+            self._description = self._default_description()
32
+        self._parser = argparse.ArgumentParser(
33
+            prog = self._prog_name(),
34
+            description = self._description)
35
+        self.argparser_config(self._parser)
36
+            
37
+    
38
+    ##@brief Handles script registration
39
+    #@note Script list is maitained in 
40
+    #lodel.plugin.admin_script.__registered_scripts
41
+    def __register_script(self, name):
42
+        if self._action is None:
43
+            logger.warning("%s._action is None. Trying to use class name as \
44
+action identifier" % name)
45
+            self._action = name
46
+        self._action = self._action.lower()
47
+        script_registration(self._action, self)
48
+
49
+    def __str__(self):
50
+        return '%s : %s' % (self._action, self._description)
51
+
52
+class LodelScript(object, metaclass=MetaLodelScript):
53
+    
54
+    ##@brief A string to identify the action
55
+    _action = None
56
+    ##@brief Script descripiton (argparse argument)
57
+    _description = None
58
+    ##@brief argparse.ArgumentParser instance
59
+    _parser = None
60
+    
61
+    ##@brief No instanciation
62
+    def __init__(self):
63
+        raise NotImplementedError("Static class")
64
+    
65
+    ##@brief Virtual method. Designed to initialize arguement parser.
66
+    #@param argparser ArgumentParser : Child class argument parser instance
67
+    #@return MUST return the argument parser (NOT SURE ABOUT THAT !! Maybe it \
68
+    #works by reference)
69
+    @classmethod
70
+    def argparser_config(cls, parser):
71
+        raise LodelScriptError("LodelScript.argparser_config() is a pure \
72
+virtual method! MUST be implemented by ALL child classes")
73
+    
74
+    ##@brief Virtual method. Run the script
75
+    #@return None or an integer that will be the script return code
76
+    @classmethod
77
+    def run(cls, args):
78
+        raise LodelScriptError("LodelScript.run() is a pure virtual method. \
79
+MUST be implemented by ALL child classes")
80
+    
81
+    ##@brief Called by main_run() to execute a script.
82
+    #
83
+    #Handles argument parsing and then call LodelScript.run()
84
+    @classmethod
85
+    def _run(cls):
86
+        args = cls._parser.parse_args()
87
+        return cls.run(args)
88
+
89
+    ##@brief Append action name to the prog name
90
+    #@note See argparse.ArgumentParser() prog argument
91
+    @classmethod
92
+    def _prog_name(cls):
93
+        return '%s %s' % (sys.argv[0], cls._action)
94
+    
95
+    ##@brief Return the default description for an action
96
+    @classmethod
97
+    def _default_description(cls):
98
+        return "Lodel2 script : %s" % cls._action
99
+    
100
+    @classmethod
101
+    def help_exit(cls,msg = None, return_code = 1, exit_after = True):
102
+        if not (msg is None):
103
+            print(msg, file=sys.stderr)
104
+        cls._parser.print_help()
105
+        if exit_after:
106
+            exit(1)
107
+
108
+def script_registration(action_name, cls):
109
+    __registered_scripts[action_name] = cls
110
+    logger.info("New script registered : %s" % action_name)
111
+
112
+##@brief Return a list containing all available actions
113
+def _available_actions():
114
+    return [ act for act in __registered_scripts ]
115
+
116
+##@brief Returns default runner argument parser
117
+def _default_parser():
118
+
119
+    action_list = _available_actions()
120
+    if len(action_list) > 0:
121
+        action_list = ', '.join(sorted(action_list))
122
+    else:
123
+        action_list = 'NO SCRIPT FOUND !'
124
+
125
+    parser = argparse.ArgumentParser(description = "Lodel2 script runner")
126
+    parser.add_argument('-L', '--list-actions', action='store_true',
127
+        default=False, help="List available actions")
128
+    parser.add_argument('action', metavar="ACTION", type=str,
129
+        help="One of the following actions : %s" % action_list, nargs='?')
130
+    parser.add_argument('option', metavar="OPTIONS", type=str, nargs='*',
131
+        help="Action options. Use %s ACTION -h to have help on a specific \
132
+action" % sys.argv[0])
133
+    return parser
134
+
135
+##@brief Main function of lodel_admin.py script
136
+#
137
+#This function take care to run the good plugins and clean sys.argv from
138
+#action name before running script
139
+#
140
+#@return DO NOT RETURN BUT exit() ONCE SCRIPT EXECUTED !!
141
+def main_run():
142
+    default_parser = _default_parser()
143
+    if len(sys.argv) == 1:
144
+        default_parser.print_help()
145
+        exit(1)
146
+    args = default_parser.parse_args()
147
+    if args.list_actions:
148
+        print("Available actions :")
149
+        for sname in sorted(__registered_scripts.keys()):
150
+            print("\t- %s" % __registered_scripts[sname])
151
+        exit(0)
152
+    #preparing sys.argv (deleting action)
153
+    action = sys.argv[1].lower()
154
+    del(sys.argv[1])
155
+    if action not in __registered_scripts:
156
+        print("Unknow action '%s'\n" % action, file=sys.stderr)
157
+        default_parser.print_help()
158
+        exit(1)
159
+    script = __registered_scripts[action]
160
+    ret = script._run()
161
+    ret = 0 if ret is None else ret
162
+    exit(ret)

+ 28
- 0
tests/datahandlers/test_boolean.py View File

@@ -0,0 +1,28 @@
1
+import unittest
2
+
3
+from lodel.leapi.datahandlers.datas import Boolean, Varchar, Integer
4
+
5
+
6
+class BooleanTestCase(unittest.TestCase):
7
+
8
+    def test_boolean_check_data_value(self):
9
+        test_boolean = Boolean()
10
+
11
+        # correct values
12
+        for test_value in [True, False]:
13
+            value, error = test_boolean._check_data_value(test_value)
14
+            self.assertIsNone(error)
15
+
16
+        # incorrect values
17
+        for test_value in ['ok', 'True', 'False']:
18
+            value, error = test_boolean._check_data_value(test_value)
19
+            self.assertIsNotNone(error)
20
+
21
+    def test_can_override(self):
22
+        test_boolean = Boolean()
23
+
24
+        test_varchar = Varchar()
25
+        test_int = Integer()
26
+
27
+        self.assertFalse(test_boolean.can_override(test_varchar))
28
+        self.assertFalse(test_boolean.can_override(test_int))

+ 24
- 0
tests/datahandlers/test_integer.py View File

@@ -0,0 +1,24 @@
1
+import unittest
2
+
3
+from lodel.leapi.datahandlers.datas import Integer, Boolean
4
+
5
+
6
+class IntegerTestCase(unittest.TestCase):
7
+
8
+    def test_integer_check_data_value(self):
9
+        test_int = Integer()
10
+
11
+        # Incorrect values
12
+        for test_bad_value in ['ok','ceci est un test', '15.2', 15.2]:
13
+            _, error = test_int._check_data_value(test_bad_value)
14
+            self.assertIsNotNone(error)
15
+
16
+        # Correct values
17
+        for test_correct_value in [10, '15', '15.0']:
18
+            _, error = test_int._check_data_value(test_correct_value)
19
+            self.assertIsNone(error)
20
+
21
+    def test_can_override(self):
22
+        test_int = Integer()
23
+        test_boolean = Boolean()
24
+        self.assertFalse(test_int.can_override(test_boolean))

+ 14
- 5
tests/datahandlers/test_regex.py View File

@@ -1,19 +1,21 @@
1 1
 import unittest
2 2
 
3
-from lodel.leapi.datahandlers.datas import Regex, Varchar
3
+from lodel.leapi.datahandlers.datas import Regex, Varchar, Integer
4 4
 
5 5
 
6 6
 class RegexTestCase(unittest.TestCase):
7 7
 
8 8
     def test_check_correct_data_value(self):
9 9
         test_value = '126.205.255.12'
10
-        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$', max_length=100)
10
+        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
11
+                           max_length=100)
11 12
         value, error = test_regex._check_data_value(test_value)
12 13
         self.assertIsNone(error)
13 14
         self.assertEqual(value, test_value)
14 15
 
15 16
     def test_check_bad_data_value(self):
16
-        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$', max_length=15)
17
+        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
18
+                           max_length=15)
17 19
         for test_value in ['800.9.10.5', 'test_string_value', '999.999.999.999']:
18 20
             value, error = test_regex._check_data_value(test_value)
19 21
             self.assertEqual(value, '')
@@ -32,9 +34,16 @@ class RegexTestCase(unittest.TestCase):
32 34
                 self.assertEqual(value, test_value)
33 35
 
34 36
     def test_can_override(self):
35
-        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$', max_length=15)
36
-        for test_varchar in [Varchar(max_length=64), Varchar(max_length=15), Varchar(max_length=9)]:
37
+        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
38
+                           max_length=15)
39
+        for test_varchar in [Varchar(), Varchar(max_length=15), Varchar(max_length=9)]:
37 40
             if test_regex.max_length == test_varchar.max_length:
38 41
                 self.assertTrue(test_regex.can_override(test_varchar))
39 42
             else:
40 43
                 self.assertFalse(test_regex.can_override(test_varchar))
44
+
45
+    def test_cant_override(self):
46
+        test_regex = Regex('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
47
+                           max_length=15)
48
+        test_int = Integer()
49
+        self.assertFalse(test_regex.can_override(test_int))

+ 27
- 0
tests/datahandlers/test_varchar.py View File

@@ -0,0 +1,27 @@
1
+import unittest
2
+
3
+from lodel.leapi.datahandlers.datas import Varchar, Integer
4
+
5
+
6
+class VarcharTestCase(unittest.TestCase):
7
+
8
+    def test_check_data_value(self):
9
+        test_varchar = Varchar(max_length=10)
10
+
11
+        _, error = test_varchar._check_data_value("c" * 10)
12
+        self.assertIsNone(error)
13
+
14
+        _, error = test_varchar._check_data_value("c" * 9)
15
+        self.assertIsNone(error)
16
+
17
+        _, error = test_varchar._check_data_value("c" * 11)
18
+        self.assertIsNotNone(error)
19
+        self.assertIsInstance(error, ValueError)
20
+
21
+    def test_can_override(self):
22
+        test_varchar1 = Varchar()
23
+        test_integer = Integer()
24
+        test_varchar2 = Varchar()
25
+
26
+        self.assertFalse(test_varchar1.can_override(test_integer))
27
+        self.assertTrue(test_varchar1.can_override(test_varchar2))

Loading…
Cancel
Save