Bläddra i källkod

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

prieto 8 år sedan
förälder
incheckning
b3eb8134b6

+ 4
- 1
Makefile Visa fil

@@ -3,9 +3,12 @@ dyncode_filename='lodel/leapi/dyncode.py'
3 3
 all: tests doc dyncode
4 4
 
5 5
 # generate doxygen documentation
6
-doc: cleandoc
6
+doc: cleandoc doc_graphviz
7 7
 	doxygen
8 8
 
9
+doc_graphviz:
10
+	cd doc/img/graphviz; make
11
+
9 12
 # Test em update ( examples/em_test.pickle )
10 13
 em_test:
11 14
 	python3 em_test.py

+ 50
- 6
em_test.py Visa fil

@@ -142,7 +142,7 @@ text.new_field(    'title',
142 142
                     display_name = {'eng': 'Title', 'fre': 'Titre'},
143 143
                     group = editorial_group,
144 144
                     data_handler = 'varchar',
145
-)
145
+                    nullable = True,)
146 146
 text.new_field(    'subtitle',
147 147
                     display_name = {
148 148
                         'eng': 'Subtitle',
@@ -150,21 +150,65 @@ text.new_field(    'subtitle',
150 150
                     },
151 151
                     group = editorial_group,
152 152
                     data_handler = 'varchar',
153
-)
153
+                    nullable = True)
154 154
 
155 155
 # Classe collection
156 156
 collection = em.new_class(  'collection',
157 157
                             display_name = 'Collection',
158 158
                             group = editorial_group,
159
-                            abstract = True,
160
-                            parents = entitie,
161
-)
159
+                            abstract = False,
160
+                            parents = entitie)
162 161
 collection.new_field(   'title',
163 162
                         display_name = 'Title',
164 163
                         group = editorial_group,
165
-                        abstract = True,
166 164
                         data_handler = 'varchar'
167 165
 )
166
+collection.new_field(   'publications',
167
+                        display_name = 'Publications',
168
+                        group = editorial_group,
169
+                        data_handler = 'list',
170
+                        back_reference = ('publication', 'collection'))
171
+
172
+# Classe publication
173
+pulication = em.new_class(  'publication',
174
+                            display_name = 'Publication',
175
+                            group = editorial_group,
176
+                            abstract = False,
177
+                            parents = entitie,)
178
+publication.new_field(  'collection',
179
+                        display_name = 'Collection',
180
+                        group = editorial_group,
181
+                        data_handler = 'link',
182
+                        back_reference = ('collection', 'publications'))
183
+
184
+#########################
185
+#   Texte definition    #
186
+#########################
187
+
188
+section = em.new_class(    'section',
189
+                            display_name = 'Section',
190
+                            group = editorial_group,
191
+                            abstract = False,
192
+                            parents = text)
193
+
194
+subsection = em.new_class(  'subsection',
195
+                            display_name = 'Subsection',
196
+                            group = editorial_group,
197
+                            abstract = False,
198
+                            parents = section)
199
+
200
+section.new_field(  'childs',
201
+                    display_name = 'Next section',
202
+                    group = editorial_group,
203
+                    data_hander = 'hierarch',
204
+                    allowed_class = [subsection],
205
+                    back_reference = ('subsection', 'parent'))
206
+
207
+subsection.new_field(   'parent',
208
+                        display_name = 'Parent',
209
+                        group = editorial_group,
210
+                        data_handler = 'link',
211
+                        allowed_class = [section])
168 212
 
169 213
 #####################
170 214
 # Persons & authors #

+ 1
- 1
lodel/datasource/mongodb/datasource.py Visa fil

@@ -24,7 +24,7 @@ class MongoDbDataSource(object):
24 24
         self.connection = MongoClient(connection_string)
25 25
         self.database = self.connection[connection_args['dbname']]
26 26
 
27
-    ## @brief Inserts a list of records in a given collection
27
+    ##@brief Inserts a list of records in a given collection
28 28
     #
29 29
     # @param collection_name str : name of the MongoDB collection in which we will insert the records
30 30
     # @param datas list : list of dictionaries corresponding to the records

+ 36
- 36
lodel/editorial_model/components.py Visa fil

@@ -9,12 +9,12 @@ from lodel.utils.mlstring import MlString
9 9
 
10 10
 from lodel.editorial_model.exceptions import *
11 11
 
12
-## @brief Abstract class to represent editorial model components
12
+##@brief Abstract class to represent editorial model components
13 13
 # @see EmClass EmField
14 14
 # @todo forbid '.' in uid
15 15
 class EmComponent(object):
16 16
     
17
-    ## @brief Instanciate an EmComponent
17
+    ##@brief Instanciate an EmComponent
18 18
     # @param uid str : uniq identifier
19 19
     # @param display_name MlString|str|dict : component display_name
20 20
     # @param help_text MlString|str|dict : help_text
@@ -43,10 +43,10 @@ class EmComponent(object):
43 43
         return int.from_bytes(m.digest(), byteorder='big')
44 44
 
45 45
 
46
-## @brief Handles editorial model objects classes
46
+##@brief Handles editorial model objects classes
47 47
 class EmClass(EmComponent):
48 48
     
49
-    ## @brief Instanciate a new EmClass
49
+    ##@brief Instanciate a new EmClass
50 50
     # @param uid str : uniq identifier
51 51
     # @param display_name MlString|str|dict : component display_name
52 52
     # @param abstract bool : set the class as asbtract if True
@@ -68,10 +68,10 @@ class EmClass(EmComponent):
68 68
         else:
69 69
             parents = list()
70 70
         self.parents = parents
71
-        ## @brief Stores EmFields instances indexed by field uid
71
+        ##@brief Stores EmFields instances indexed by field uid
72 72
         self.__fields = dict() 
73 73
     
74
-    ## @brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
74
+    ##@brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
75 75
     @property
76 76
     def __all_fields(self):
77 77
         res = dict()
@@ -80,7 +80,7 @@ class EmClass(EmComponent):
80 80
         res.update(self.__fields)
81 81
         return res
82 82
 
83
-    ## @brief Return the list of all dependencies
83
+    ##@brief Return the list of all dependencies
84 84
     #
85 85
     # Reccursive parents listing
86 86
     @property
@@ -93,7 +93,7 @@ class EmClass(EmComponent):
93 93
             res |= parent.parents_recc
94 94
         return res
95 95
 
96
-    ## @brief EmField getter
96
+    ##@brief EmField getter
97 97
     # @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance
98 98
     # @param no_parents bool : If True returns only fields defined is this class and not the one defined in parents classes
99 99
     # @return A list on EmFields instances (if uid is None) else return an EmField instance
@@ -104,7 +104,7 @@ class EmClass(EmComponent):
104 104
         except KeyError:
105 105
             raise EditorialModelError("No such EmField '%s'" % uid)
106 106
 
107
-    ## @brief Add a field to the EmClass
107
+    ##@brief Add a field to the EmClass
108 108
     # @param emfield EmField : an EmField instance
109 109
     # @warning do not add an EmField allready in another class !
110 110
     # @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents)
@@ -121,7 +121,7 @@ class EmClass(EmComponent):
121 121
         emfield._emclass = self
122 122
         return emfield
123 123
     
124
-    ## @brief Create a new EmField and add it to the EmClass
124
+    ##@brief Create a new EmField and add it to the EmClass
125 125
     # @param data_handler str : A DataHandler name
126 126
     # @param uid str : the EmField uniq id
127 127
     # @param **field_kwargs :  EmField constructor parameters ( see @ref EmField.__init__() ) 
@@ -152,10 +152,10 @@ class EmClass(EmComponent):
152 152
         return "<class %s EmClass uid=%s>" % (abstract, repr(self.uid) )
153 153
 
154 154
 
155
-## @brief Handles editorial model classes fields
155
+##@brief Handles editorial model classes fields
156 156
 class EmField(EmComponent):
157 157
 
158
-    ## @brief Instanciate a new EmField
158
+    ##@brief Instanciate a new EmField
159 159
     # @param uid str : uniq identifier
160 160
     # @param display_name MlString|str|dict : field display_name
161 161
     # @param data_handler str : A DataHandler name
@@ -165,22 +165,22 @@ class EmField(EmComponent):
165 165
     def __init__(self, uid, data_handler, display_name = None, help_text = None, group = None, **handler_kwargs):
166 166
         from lodel.leapi.datahandlers.base_classes import DataHandler
167 167
         super().__init__(uid, display_name, help_text, group)
168
-        ## @brief The data handler name
168
+        ##@brief The data handler name
169 169
         self.data_handler_name = data_handler
170
-        ## @brief The data handler class
170
+        ##@brief The data handler class
171 171
         self.data_handler_cls = DataHandler.from_name(data_handler)
172
-        ## @brief The data handler instance associated with this EmField
172
+        ##@brief The data handler instance associated with this EmField
173 173
         self.data_handler_instance = self.data_handler_cls(**handler_kwargs)
174
-        ## @brief Stores data handler instanciation options
174
+        ##@brief Stores data handler instanciation options
175 175
         self.data_handler_options = handler_kwargs
176
-        ## @brief Stores the emclass that contains this field (set by EmClass.add_field() method)
176
+        ##@brief Stores the emclass that contains this field (set by EmClass.add_field() method)
177 177
         self._emclass = None
178 178
 
179
-    ## @brief Returns data_handler_name attribute
179
+    ##@brief Returns data_handler_name attribute
180 180
     def get_data_handler_name(self):
181 181
         return copy.copy(self.data_handler_name)
182 182
         
183
-    ## @brief Returns data_handler_cls attribute
183
+    ##@brief Returns data_handler_cls attribute
184 184
     def get_data_handler_cls(self):
185 185
         return copy.copy(selfdata_handler_cls)
186 186
     
@@ -195,10 +195,10 @@ class EmField(EmComponent):
195 195
                                 'utf-8')
196 196
         ).digest(), byteorder='big')
197 197
 
198
-## @brief Handles functionnal group of EmComponents
198
+##@brief Handles functionnal group of EmComponents
199 199
 class EmGroup(object):
200 200
         
201
-    ## @brief Create a new EmGroup
201
+    ##@brief Create a new EmGroup
202 202
     # @note you should NEVER call the constructor yourself. Use Model.add_group instead
203 203
     # @param uid str : Uniq identifier
204 204
     # @param depends list : A list of EmGroup dependencies
@@ -206,11 +206,11 @@ class EmGroup(object):
206 206
     # @param help_text MlString|str : 
207 207
     def __init__(self, uid, depends = None, display_name = None, help_text = None):
208 208
         self.uid = uid
209
-        ## @brief Stores the list of groups that depends on this EmGroup indexed by uid
209
+        ##@brief Stores the list of groups that depends on this EmGroup indexed by uid
210 210
         self.required_by = dict()
211
-        ## @brief Stores the list of dependencies (EmGroup) indexed by uid
211
+        ##@brief Stores the list of dependencies (EmGroup) indexed by uid
212 212
         self.require = dict()
213
-        ## @brief Stores the list of EmComponent instances contained in this group
213
+        ##@brief Stores the list of EmComponent instances contained in this group
214 214
         self.__components = set()
215 215
 
216 216
         self.display_name = None if display_name is None else MlString(display_name)
@@ -221,7 +221,7 @@ class EmGroup(object):
221 221
                     raise ValueError("EmGroup expected in depends argument but %s found" % grp)
222 222
                 self.add_dependencie(grp)
223 223
     
224
-    ## @brief Returns EmGroup dependencie
224
+    ##@brief Returns EmGroup dependencie
225 225
     # @param recursive bool : if True return all dependencies and their dependencies
226 226
     # @return a dict of EmGroup identified by uid
227 227
     def dependencies(self, recursive = False):
@@ -237,7 +237,7 @@ class EmGroup(object):
237 237
                     res[new_dep.uid] = new_dep
238 238
         return res
239 239
     
240
-    ## @brief Returns EmGroup applicants
240
+    ##@brief Returns EmGroup applicants
241 241
     # @param recursive bool : if True return all dependencies and their dependencies
242 242
     # @returns a dict of EmGroup identified by uid
243 243
     def applicants(self, recursive = False):
@@ -253,12 +253,12 @@ class EmGroup(object):
253 253
                     res[new_app.uid] = new_app
254 254
         return res
255 255
     
256
-	## @brief Returns EmGroup components
256
+	##@brief Returns EmGroup components
257 257
     # @returns a copy of the set of components
258 258
     def components(self):
259 259
         return (self.__components).copy()
260 260
 
261
-    ## @brief Returns EmGroup display_name
261
+    ##@brief Returns EmGroup display_name
262 262
     #  @param lang str | None : If None return default lang translation
263 263
     #  @returns None if display_name is None, a str for display_name else
264 264
     def get_display_name(self, lang=None):
@@ -266,7 +266,7 @@ class EmGroup(object):
266 266
         if name is None : return None
267 267
         return name.get(lang);
268 268
 
269
-    ## @brief Returns EmGroup help_text
269
+    ##@brief Returns EmGroup help_text
270 270
     #  @param lang str | None : If None return default lang translation
271 271
     #  @returns None if display_name is None, a str for display_name else
272 272
     def get_help_text(self, lang=None):
@@ -274,7 +274,7 @@ class EmGroup(object):
274 274
         if help is None : return None
275 275
         return help.get(lang);
276 276
     
277
-    ## @brief Add components in a group
277
+    ##@brief Add components in a group
278 278
     # @param components list : EmComponent instances list
279 279
     def add_components(self, components):
280 280
         for component in components:
@@ -285,7 +285,7 @@ class EmGroup(object):
285 285
                 raise EditorialModelError("Expecting components to be a list of EmComponent, but %s found in the list" % type(component))
286 286
         self.__components |= set(components)
287 287
 
288
-    ## @brief Add a dependencie
288
+    ##@brief Add a dependencie
289 289
     # @param em_group EmGroup|iterable : an EmGroup instance or list of instance
290 290
     def add_dependencie(self, grp):
291 291
         try:
@@ -301,7 +301,7 @@ class EmGroup(object):
301 301
         self.require[grp.uid] = grp
302 302
         grp.required_by[self.uid] = self
303 303
         
304
-    ## @brief Add a applicant
304
+    ##@brief Add a applicant
305 305
     # @param em_group EmGroup|iterable : an EmGroup instance or list of instance
306 306
     def add_applicant(self, grp):
307 307
         try:
@@ -317,17 +317,17 @@ class EmGroup(object):
317 317
         self.required_by[grp.uid] = grp
318 318
         grp.require[self.uid] = self
319 319
     
320
-    ## @brief Search for circular dependencie
320
+    ##@brief Search for circular dependencie
321 321
     # @return True if circular dep found else False
322 322
     def __circular_dependencie(self, new_dep):
323 323
         return self.uid in new_dep.dependencies(True)
324 324
     
325
-    ## @brief Search for circular applicant
325
+    ##@brief Search for circular applicant
326 326
     # @return True if circular app found else False
327 327
     def __circular_applicant(self, new_app):
328 328
         return self.uid in new_app.applicants(True)
329 329
 
330
-    ## @brief Fancy string representation of an EmGroup
330
+    ##@brief Fancy string representation of an EmGroup
331 331
     # @return a string
332 332
     def __str__(self):
333 333
         if self.display_name is None:
@@ -353,7 +353,7 @@ class EmGroup(object):
353 353
                                 byteorder = 'big'
354 354
         )
355 355
     
356
-    ## @brief Complete string representation of an EmGroup
356
+    ##@brief Complete string representation of an EmGroup
357 357
     # @return a string
358 358
     def __repr__(self):
359 359
         return "<class EmGroup '%s' depends : [%s]>" % (self.uid, ', '.join([duid for duid in self.dependencies(False)]) )

+ 15
- 15
lodel/editorial_model/model.py Visa fil

@@ -8,21 +8,21 @@ from lodel.utils.mlstring import MlString
8 8
 from lodel.editorial_model.exceptions import *
9 9
 from lodel.editorial_model.components import EmClass, EmField, EmGroup
10 10
 
11
-## @brief Describe an editorial model
11
+##@brief Describe an editorial model
12 12
 class EditorialModel(object):
13 13
     
14
-    ## @brief Create a new editorial model
14
+    ##@brief Create a new editorial model
15 15
     # @param name MlString|str|dict : the editorial model name
16 16
     # @param description MlString|str|dict : the editorial model description
17 17
     def __init__(self, name, description = None):
18 18
         self.name = MlString(name)
19 19
         self.description = MlString(description)
20
-        ## @brief Stores all groups indexed by id
20
+        ##@brief Stores all groups indexed by id
21 21
         self.__groups = dict()
22
-        ## @brief Stores all classes indexed by id
22
+        ##@brief Stores all classes indexed by id
23 23
         self.__classes = dict()
24 24
     
25
-    ## @brief EmClass accessor
25
+    ##@brief EmClass accessor
26 26
     # @param uid None | str : give this argument to get a specific EmClass
27 27
     # @return if uid is given returns an EmClass else returns an EmClass iterator
28 28
     def classes(self, uid = None):
@@ -31,7 +31,7 @@ class EditorialModel(object):
31 31
         except KeyError:
32 32
             raise EditorialModelException("EmClass not found : '%s'" % uid)
33 33
 
34
-    ## @brief EmGroup getter
34
+    ##@brief EmGroup getter
35 35
     # @param uid None | str : give this argument to get a specific EmGroup
36 36
     # @return if uid is given returns an EmGroup else returns an EmGroup iterator
37 37
     def groups(self, uid = None):
@@ -40,7 +40,7 @@ class EditorialModel(object):
40 40
         except KeyError:
41 41
             raise EditorialModelException("EmGroup not found : '%s'" % uid)
42 42
     
43
-    ## @brief EmField getter
43
+    ##@brief EmField getter
44 44
     # @param uid str : An EmField uid represented by "CLASSUID.FIELDUID"
45 45
     # @return Fals or an EmField instance
46 46
     #
@@ -61,7 +61,7 @@ class EditorialModel(object):
61 61
             pass
62 62
         return False
63 63
 
64
-    ## @brief Add a class to the editorial model
64
+    ##@brief Add a class to the editorial model
65 65
     # @param emclass EmClass : the EmClass instance to add
66 66
     # @return emclass
67 67
     def add_class(self, emclass):
@@ -72,7 +72,7 @@ class EditorialModel(object):
72 72
         self.__classes[emclass.uid] = emclass
73 73
         return emclass
74 74
 
75
-    ## @brief Add a group to the editorial model
75
+    ##@brief Add a group to the editorial model
76 76
     # @param emgroup EmGroup : the EmGroup instance to add
77 77
     # @return emgroup
78 78
     def add_group(self, emgroup):
@@ -83,13 +83,13 @@ class EditorialModel(object):
83 83
         self.__groups[emgroup.uid] = emgroup
84 84
         return emgroup
85 85
 
86
-    ## @brief Add a new EmClass to the editorial model
86
+    ##@brief Add a new EmClass to the editorial model
87 87
     # @param uid str : EmClass uid
88 88
     # @param **kwargs : EmClass constructor options ( see @ref lodel.editorial_model.component.EmClass.__init__() )
89 89
     def new_class(self, uid, **kwargs):
90 90
         return self.add_class(EmClass(uid, **kwargs))
91 91
     
92
-    ## @brief Add a new EmGroup to the editorial model
92
+    ##@brief Add a new EmGroup to the editorial model
93 93
     # @param uid str : EmGroup uid
94 94
     # @param *kwargs : EmGroup constructor keywords arguments (see @ref lodel.editorial_model.component.EmGroup.__init__() )
95 95
     def new_group(self, uid, **kwargs):
@@ -103,7 +103,7 @@ class EditorialModel(object):
103 103
             translator = self.translator_from_name(translator)
104 104
         return translator.save(self, **translator_kwargs)
105 105
     
106
-    ## @brief Load a model
106
+    ##@brief Load a model
107 107
     # @param translator module : The translator module to use
108 108
     # @param **translator_args
109 109
     @classmethod
@@ -112,7 +112,7 @@ class EditorialModel(object):
112 112
             translator = cls.translator_from_name(translator)
113 113
         return translator.load(**translator_kwargs)
114 114
 
115
-    ## @brief Return a translator module given a translator name
115
+    ##@brief Return a translator module given a translator name
116 116
     # @param translator_name str : The translator name
117 117
     # @return the translator python module
118 118
     # @throw NameError if the translator does not exists
@@ -126,12 +126,12 @@ class EditorialModel(object):
126 126
         return mod
127 127
         
128 128
     
129
-    ## @brief Private getter for __groups or __classes
129
+    ##@brief Private getter for __groups or __classes
130 130
     # @see classes() groups()
131 131
     def __elt_getter(self, elts, uid):
132 132
         return list(elts.values()) if uid is None else elts[uid]
133 133
     
134
-    ## @brief Lodel hash
134
+    ##@brief Lodel hash
135 135
     def d_hash(self):
136 136
         payload = "%s%s" % (
137 137
                             self.name,

+ 2
- 2
lodel/editorial_model/translator/picklefile.py Visa fil

@@ -3,7 +3,7 @@
3 3
 import pickle
4 4
 from pickle import Pickler
5 5
 
6
-## @brief Save a model in a file
6
+##@brief Save a model in a file
7 7
 # @param model EditorialModel : the model to save
8 8
 # @param filename str|None : if None return the model as pickle bytes
9 9
 # @return None if filename is a string, else returns bytes representation of model
@@ -12,7 +12,7 @@ def save(model, filename = None):
12 12
         pickle.dump(model, ffd)
13 13
     return filename
14 14
 
15
-## @brief Load a model from a file
15
+##@brief Load a model from a file
16 16
 # @param filename str : the filename to use to load the model
17 17
 def load(filename):
18 18
     with open(filename, 'rb') as ffd:

+ 21
- 21
lodel/leapi/datahandlers/base_classes.py Visa fil

@@ -13,22 +13,22 @@ from lodel import logger
13 13
 class FieldValidationError(Exception):
14 14
     pass
15 15
 
16
-## @brief Base class for all data handlers
16
+##@brief Base class for all data handlers
17 17
 class DataHandler(object):
18 18
     
19 19
     __HANDLERS_MODULES = ('datas_base', 'datas', 'references')
20
-    ## @brief Stores the DataHandler childs classes indexed by name
20
+    ##@brief Stores the DataHandler childs classes indexed by name
21 21
     __base_handlers = None
22
-    ## @brief Stores custom datahandlers classes indexed by name
22
+    ##@brief Stores custom datahandlers classes indexed by name
23 23
     # @todo do it ! (like plugins, register handlers... blablabla)
24 24
     __custom_handlers = dict()
25 25
 
26 26
     help_text = 'Generic Field Data Handler'
27 27
 
28
-    ## @brief List fields that will be exposed to the construct_data_method
28
+    ##@brief List fields that will be exposed to the construct_data_method
29 29
     _construct_datas_deps = []
30 30
 
31
-    ## @brief constructor
31
+    ##@brief constructor
32 32
     # @param internal False | str : define whether or not a field is internal
33 33
     # @param immutable bool : indicates if the fieldtype has to be defined in child classes of LeObject or if it is
34 34
     #                         designed globally and immutable
@@ -65,12 +65,12 @@ class DataHandler(object):
65 65
     def is_primary_key(self):
66 66
         return self.primary_key
67 67
 
68
-    ## @brief checks if a fieldtype is internal
68
+    ##@brief checks if a fieldtype is internal
69 69
     # @return bool
70 70
     def is_internal(self):
71 71
         return self.internal is not False
72 72
 
73
-    ## @brief calls the data_field defined _check_data_value() method
73
+    ##@brief calls the data_field defined _check_data_value() method
74 74
     # @return tuple (value, error|None)
75 75
     def check_data_value(self, value):
76 76
         if value is None:
@@ -80,7 +80,7 @@ class DataHandler(object):
80 80
             return None, None
81 81
         return self._check_data_value(value)
82 82
 
83
-    ## @brief checks if this class can override the given data handler
83
+    ##@brief checks if this class can override the given data handler
84 84
     # @param data_handler DataHandler
85 85
     # @return bool
86 86
     def can_override(self, data_handler):
@@ -88,7 +88,7 @@ class DataHandler(object):
88 88
             return False
89 89
         return True
90 90
 
91
-    ## @brief Build field value
91
+    ##@brief Build field value
92 92
     # @param emcomponent EmComponent : An EmComponent child class instance
93 93
     # @param fname str : The field name
94 94
     # @param datas dict : dict storing fields values (from the component)
@@ -110,7 +110,7 @@ class DataHandler(object):
110 110
 
111 111
         return RuntimeError("Unable to construct data for field %s", fname)
112 112
 
113
-    ## @brief Check datas consistency
113
+    ##@brief Check datas consistency
114 114
     # @param emcomponent EmComponent : An EmComponent child class instance
115 115
     # @param fname : the field name
116 116
     # @param datas dict : dict storing fields values
@@ -119,7 +119,7 @@ class DataHandler(object):
119 119
     def check_data_consistency(self, emcomponent, fname, datas):
120 120
         return True
121 121
 
122
-    ## @brief This method is use by plugins to register new data handlers
122
+    ##@brief This method is use by plugins to register new data handlers
123 123
     @classmethod
124 124
     def register_new_handler(cls, name, data_handler):
125 125
         if not inspect.isclass(data_handler):
@@ -140,7 +140,7 @@ class DataHandler(object):
140 140
                         cls.__base_handlers[name.lower()] = obj
141 141
         return copy.copy(cls.__base_handlers)
142 142
 
143
-    ## @brief given a field type name, returns the associated python class
143
+    ##@brief given a field type name, returns the associated python class
144 144
     # @param fieldtype_name str : A field type name (not case sensitive)
145 145
     # @return DataField child class
146 146
     # @todo implements custom handlers fetch
@@ -152,7 +152,7 @@ class DataHandler(object):
152 152
             raise NameError("No data handlers named '%s'" % (name,))
153 153
         return cls.__base_handlers[name]
154 154
  
155
-    ## @brief Return the module name to import in order to use the datahandler
155
+    ##@brief Return the module name to import in order to use the datahandler
156 156
     # @param data_handler_name str : Data handler name
157 157
     # @return a str
158 158
     @classmethod
@@ -164,24 +164,24 @@ class DataHandler(object):
164 164
                                                     class_name = handler_class.__name__
165 165
         )
166 166
             
167
-    ## @brief __hash__ implementation for fieldtypes
167
+    ##@brief __hash__ implementation for fieldtypes
168 168
     def __hash__(self):
169 169
         hash_dats = [self.__class__.__module__]
170 170
         for kdic in sorted([k for k in self.__dict__.keys() if not k.startswith('_')]):
171 171
             hash_dats.append((kdic, getattr(self, kdic)))
172 172
         return hash(tuple(hash_dats))
173 173
 
174
-## @brief Base class for datas data handler (by opposition with references)
174
+##@brief Base class for datas data handler (by opposition with references)
175 175
 class DataField(DataHandler):
176 176
     pass
177 177
 
178
-## @brief Abstract class for all references
178
+##@brief Abstract class for all references
179 179
 #
180 180
 # References are fields that stores a reference to another
181 181
 # editorial object
182 182
 class Reference(DataHandler):
183 183
 
184
-    ## @brief Instanciation
184
+    ##@brief Instanciation
185 185
     # @param allowed_classes list | None : list of allowed em classes if None no restriction
186 186
     # @param back_reference tuple | None : tuple containing (LeObject child class, fieldname)
187 187
     # @param internal bool : if False, the field is not internal
@@ -200,12 +200,12 @@ class Reference(DataHandler):
200 200
     def back_reference(self):
201 201
         return copy.copy(self.__back_reference)
202 202
 
203
-    ## @brief Set the back reference for this field.
203
+    ##@brief Set the back reference for this field.
204 204
     def _set_back_reference(self, back_reference):
205 205
         self.__back_reference = back_reference
206 206
         
207 207
 
208
-    ## @brief Check value
208
+    ##@brief Check value
209 209
     # @param value *
210 210
     # @return tuple(value, exception)
211 211
     # @todo implement the check when we have LeObject to check value
@@ -222,7 +222,7 @@ class Reference(DataHandler):
222 222
         return value
223 223
 
224 224
 
225
-## @brief This class represent a data_handler for single reference to another object
225
+##@brief This class represent a data_handler for single reference to another object
226 226
 #
227 227
 # The fields using this data handlers are like "foreign key" on another object
228 228
 class SingleRef(Reference):
@@ -238,7 +238,7 @@ class SingleRef(Reference):
238 238
         return val, expt
239 239
 
240 240
 
241
-## @brief This class represent a data_handler for multiple references to another object
241
+##@brief This class represent a data_handler for multiple references to another object
242 242
 #
243 243
 # The fields using this data handlers are like SingleRef but can store multiple references in one field
244 244
 # @note SQL implementation could be tricky

+ 6
- 6
lodel/leapi/datahandlers/datas.py Visa fil

@@ -2,13 +2,13 @@
2 2
 
3 3
 from lodel.leapi.datahandlers.datas_base import *
4 4
 
5
-## @brief Data field designed to handle formated strings
5
+##@brief Data field designed to handle formated strings
6 6
 class FormatString(Varchar):
7 7
 
8 8
     help = 'Automatic string field, designed to use the str % operator to build its content'
9 9
     base_type = 'char'
10 10
 
11
-    ## @brief Build its content with a field list and a format string
11
+    ##@brief Build its content with a field list and a format string
12 12
     # @param format_string str
13 13
     # @param max_length int : the maximum length of the handled value
14 14
     # @param field_list list : List of field to use
@@ -26,13 +26,13 @@ class FormatString(Varchar):
26 26
             return False
27 27
         return True
28 28
 
29
-## @brief Varchar validated by a regex
29
+##@brief Varchar validated by a regex
30 30
 class Regex(Varchar):
31 31
 
32 32
     help = 'String field validated with a regex. Takes two options : max_length and regex'
33 33
     base_type = 'char'
34 34
 
35
-    ## @brief A string field validated by a regex
35
+    ##@brief A string field validated by a regex
36 36
     # @param regex str : a regex string (passed as argument to re.compile())
37 37
     # @param max_length int : the max length for this field (default : 10)
38 38
     # @param **kwargs
@@ -57,13 +57,13 @@ class Regex(Varchar):
57 57
             return False
58 58
         return True
59 59
 
60
-## @brief Handles uniq ID
60
+##@brief Handles uniq ID
61 61
 class UniqID(Integer):
62 62
 
63 63
     help = 'Fieldtype designed to handle editorial model UID'
64 64
     base_type = 'int'
65 65
 
66
-    ## @brief A uid field
66
+    ##@brief A uid field
67 67
     # @param **kwargs
68 68
     def __init__(self, **kwargs):
69 69
         kwargs['internal'] = 'automatic'

+ 11
- 11
lodel/leapi/datahandlers/datas_base.py Visa fil

@@ -3,13 +3,13 @@ import warnings
3 3
 
4 4
 from lodel.leapi.datahandlers.base_classes import DataField
5 5
 
6
-## @brief Data field designed to handle boolean values
6
+##@brief Data field designed to handle boolean values
7 7
 class Boolean(DataField):
8 8
 
9 9
     help = 'A basic boolean field'
10 10
     base_type = 'bool'
11 11
 
12
-    ## @brief A boolean field
12
+    ##@brief A boolean field
13 13
     def __init__(self, **kwargs):
14 14
         if 'check_data_value' not in kwargs:
15 15
             kwargs['check_data_value'] = self.check_value
@@ -23,7 +23,7 @@ class Boolean(DataField):
23 23
             error = TypeError("The value '%s' is not, and will never, be a boolean" % value)
24 24
         return value, error
25 25
 
26
-## @brief Data field designed to handle integer values
26
+##@brief Data field designed to handle integer values
27 27
 class Integer(DataField):
28 28
 
29 29
     help = 'Basic integer field'
@@ -40,19 +40,19 @@ class Integer(DataField):
40 40
             error = TypeError("The value '%s' is not, and will never, be an integer" % value)
41 41
         return value, error
42 42
 
43
-## @brief Data field designed to handle string
43
+##@brief Data field designed to handle string
44 44
 class Varchar(DataField):
45 45
 
46 46
     help = 'Basic string (varchar) field. Default size is 64 characters'
47 47
     base_type = 'char'
48 48
 
49
-    ## @brief A string field
49
+    ##@brief A string field
50 50
     # @brief max_length int: The maximum length of this field
51 51
     def __init__(self, max_length=64, **kwargs):
52 52
         self.max_length = int(max_length)
53 53
         super().__init__(**kwargs)
54 54
 
55
-    ## @brief checks if this class can override the given data handler
55
+    ##@brief checks if this class can override the given data handler
56 56
     # @param data_handler DataHandler
57 57
     # @return bool
58 58
     def can_override(self, data_handler):
@@ -62,13 +62,13 @@ class Varchar(DataField):
62 62
             return False
63 63
         return True
64 64
 
65
-## @brief Data field designed to handle date & time 
65
+##@brief Data field designed to handle date & time 
66 66
 class DateTime(DataField):
67 67
 
68 68
     help = 'A datetime field. Take two boolean options now_on_update and now_on_create'
69 69
     base_type = 'datetime'
70 70
 
71
-    ## @brief A datetime field
71
+    ##@brief A datetime field
72 72
     # @param now_on_update bool : If true, the date is set to NOW on update
73 73
     # @param now_on_create bool : If true, the date is set to NEW on creation
74 74
     # @param **kwargs
@@ -77,7 +77,7 @@ class DateTime(DataField):
77 77
         self.now_on_create = now_on_create
78 78
         super().__init__(**kwargs)
79 79
 
80
-## @brief Data field designed to handle long string
80
+##@brief Data field designed to handle long string
81 81
 class Text(DataField):
82 82
     help = 'A text field (big string)'
83 83
     base_type = 'text'
@@ -85,12 +85,12 @@ class Text(DataField):
85 85
     def __init__(self, **kwargs):
86 86
         super(self.__class__, self).__init__(ftype='text', **kwargs)
87 87
 
88
-## @brief Data field designed to handle Files
88
+##@brief Data field designed to handle Files
89 89
 class File(DataField):
90 90
 
91 91
     base_type = 'file'
92 92
 
93
-    ## @brief a file field
93
+    ##@brief a file field
94 94
     # @param upload_path str : None by default
95 95
     # @param **kwargs
96 96
     def __init__(self, upload_path=None, **kwargs):

+ 11
- 11
lodel/leapi/datahandlers/references.py Visa fil

@@ -3,17 +3,17 @@ from lodel.leapi.datahandlers.base_classes import Reference, MultipleRef, Single
3 3
 
4 4
 class Link(SingleRef): pass
5 5
 
6
-## @brief Child class of MultipleRef where references are represented in the form of a python list
6
+##@brief Child class of MultipleRef where references are represented in the form of a python list
7 7
 class List(MultipleRef):
8 8
 
9
-    ## @brief instanciates a list reference
9
+    ##@brief instanciates a list reference
10 10
     # @param allowed_classes list | None : list of allowed em classes if None no restriction
11 11
     # @param internal bool
12 12
     # @param kwargs
13 13
     def __init__(self, max_length = None, **kwargs):
14 14
         super().__init__(**kwargs)
15 15
 
16
-    ## @brief Check value
16
+    ##@brief Check value
17 17
     # @param value *
18 18
     # @return tuple(value, exception)
19 19
     def _check_data_value(self, value):
@@ -24,17 +24,17 @@ class List(MultipleRef):
24 24
         return val, expt
25 25
 
26 26
 
27
-## @brief Child class of MultipleRef where references are represented in the form of a python set
27
+##@brief Child class of MultipleRef where references are represented in the form of a python set
28 28
 class Set(MultipleRef):
29 29
 
30
-    ## @brief instanciates a set reference
30
+    ##@brief instanciates a set reference
31 31
     # @param allowed_classes list | None : list of allowed em classes if None no restriction
32 32
     # @param internal bool : if False, the field is not internal
33 33
     # @param kwargs : Other named arguments
34 34
     def __init__(self, **kwargs):
35 35
         super().__init__(**kwargs)
36 36
 
37
-    ## @brief Check value
37
+    ##@brief Check value
38 38
     # @param value *
39 39
     # @return tuple(value, exception)
40 40
     def _check_data_value(self, value):
@@ -45,17 +45,17 @@ class Set(MultipleRef):
45 45
         return val, expt
46 46
 
47 47
 
48
-## @brief Child class of MultipleRef where references are represented in the form of a python dict
48
+##@brief Child class of MultipleRef where references are represented in the form of a python dict
49 49
 class Map(MultipleRef):
50 50
 
51
-    ## @brief instanciates a dict reference
51
+    ##@brief instanciates a dict reference
52 52
     # @param allowed_classes list | None : list of allowed em classes if None no restriction
53 53
     # @param internal bool : if False, the field is not internal
54 54
     # @param kwargs : Other named arguments
55 55
     def __init__(self, **kwargs):
56 56
         super().__init__(**kwargs)
57 57
 
58
-    ## @brief Check value
58
+    ##@brief Check value
59 59
     # @param value *
60 60
     # @return tuple(value, exception)
61 61
     def _check_data_value(self, value):
@@ -66,10 +66,10 @@ class Map(MultipleRef):
66 66
                 None if isinstance(expt, Exception) else value,
67 67
                 expt)
68 68
 
69
-## @brief This Reference class is designed to handler hierarchy with some constraint
69
+##@brief This Reference class is designed to handler hierarchy with some constraint
70 70
 class Hierarch(MultipleRef):
71 71
     
72
-    ## @brief Instanciate a data handler handling hierarchical relation with constraints
72
+    ##@brief Instanciate a data handler handling hierarchical relation with constraints
73 73
     # @param back_reference tuple : Here it is mandatory to have a back ref (like a parent field)
74 74
     # @param max_depth int | None :  limit of depth
75 75
     # @param max_childs int | Nine : maximum number of childs by nodes

+ 6
- 6
lodel/leapi/lefactory.py Visa fil

@@ -5,7 +5,7 @@ from lodel.editorial_model.components import *
5 5
 from lodel.leapi.leobject import LeObject
6 6
 from lodel.leapi.datahandlers.base_classes import DataHandler
7 7
 
8
-## @brief Generate python module code from a given model
8
+##@brief Generate python module code from a given model
9 9
 # @param model lodel.editorial_model.model.EditorialModel
10 10
 def dyncode_from_em(model):
11 11
     
@@ -31,7 +31,7 @@ from lodel.leapi.datahandlers.base_classes import DataField
31 31
     )
32 32
     return res_code
33 33
 
34
-## @brief return A list of EmClass sorted by dependencies
34
+##@brief return A list of EmClass sorted by dependencies
35 35
 #
36 36
 # The first elts in the list depends on nothing, etc.
37 37
 # @return a list of EmClass instances
@@ -41,11 +41,11 @@ def emclass_sorted_by_deps(emclass_list):
41 41
     ret = sorted(emclass_list, key = functools.cmp_to_key(emclass_deps_cmp))
42 42
     return ret
43 43
 
44
-## @brief Returns a list of EmClass that will be represented as LeObject child classes
44
+##@brief Returns a list of EmClass that will be represented as LeObject child classes
45 45
 def get_classes(model):
46 46
     return [ cls for cls in emclass_sorted_by_deps(model.classes()) if not cls.pure_abstract ]
47 47
 
48
-## @brief Given an EmField returns the data_handler constructor suitable for dynamic code
48
+##@brief Given an EmField returns the data_handler constructor suitable for dynamic code
49 49
 def data_handler_constructor(emfield):
50 50
     #dh_module_name = DataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
51 51
     get_handler_class_instr = 'DataField.from_name(%s)' % repr(emfield.data_handler_name)
@@ -63,7 +63,7 @@ def data_handler_constructor(emfield):
63 63
                                                         handler_instr = get_handler_class_instr,
64 64
                                                         options = ', '.join(options))
65 65
             
66
-## @brief Return a python repr of option values
66
+##@brief Return a python repr of option values
67 67
 def forge_optval(optval):
68 68
     if isinstance(optval, dict):
69 69
         return '{' + (', '.join( [ '%s: %s' % (repr(name), forge_optval(val)) for name, val in optval.items()])) + '}'
@@ -81,7 +81,7 @@ def forge_optval(optval):
81 81
     else:
82 82
         return repr(optval)
83 83
 
84
-## @brief Generate dyncode from an EmClass
84
+##@brief Generate dyncode from an EmClass
85 85
 # @param model EditorialModel : 
86 86
 # @param emclass EmClass : EmClass instance
87 87
 # @return a tuple with emclass python code, a set containing modules name to import, and a list of python instruction to bootstrap dynamic code, in this order

+ 23
- 23
lodel/leapi/leobject.py Visa fil

@@ -3,7 +3,7 @@
3 3
 import importlib
4 4
 
5 5
 class LeApiErrors(Exception):
6
-    ## @brief Instanciate a new exceptions handling multiple exceptions
6
+    ##@brief Instanciate a new exceptions handling multiple exceptions
7 7
     # @param msg str : Exception message
8 8
     # @param exceptions dict : A list of data check Exception with concerned field (or stuff) as key
9 9
     def __init__(self, msg = "Unknow error", exceptions = None):
@@ -25,37 +25,37 @@ class LeApiErrors(Exception):
25 25
         return msg
26 26
 
27 27
 
28
-## @brief When an error concern a query
28
+##@brief When an error concern a query
29 29
 class LeApiQueryError(LeApiErrors):
30 30
     pass
31 31
 
32 32
 
33
-## @brief When an error concerns a datas
33
+##@brief When an error concerns a datas
34 34
 class LeApiDataCheckError(LeApiErrors):
35 35
     pass
36 36
 
37 37
 
38
-## @brief Wrapper class for LeObject getter & setter
38
+##@brief Wrapper class for LeObject getter & setter
39 39
 #
40 40
 # This class intend to provide easy & friendly access to LeObject fields values 
41 41
 # without name collision problems
42 42
 # @note Wrapped methods are : LeObject.data() & LeObject.set_data()
43 43
 class LeObjectValues(object):
44 44
     
45
-    ## @brief Construct a new LeObjectValues
45
+    ##@brief Construct a new LeObjectValues
46 46
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
47 47
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
48 48
     def __init__(self, fieldnames_callback, set_callback, get_callback):
49 49
         self.__setter = set_callback
50 50
         self.__getter = get_callback
51 51
     
52
-    ## @brief Provide read access to datas values
52
+    ##@brief Provide read access to datas values
53 53
     # @note Read access should be provided for all fields
54 54
     # @param fname str : Field name
55 55
     def __getattribute__(self, fname):
56 56
         return self.__getter(fname)
57 57
     
58
-    ## @brief Provide write access to datas values
58
+    ##@brief Provide write access to datas values
59 59
     # @note Write acces shouldn't be provided for internal or immutable fields
60 60
     # @param fname str : Field name
61 61
     # @param fval * : the field value
@@ -65,23 +65,23 @@ class LeObjectValues(object):
65 65
 
66 66
 class LeObject(object):
67 67
  
68
-    ## @brief boolean that tells if an object is abtract or not
68
+    ##@brief boolean that tells if an object is abtract or not
69 69
     _abstract = None
70
-    ## @brief A dict that stores DataHandler instances indexed by field name
70
+    ##@brief A dict that stores DataHandler instances indexed by field name
71 71
     _fields = None
72
-    ## @brief A tuple of fieldname (or a uniq fieldname) representing uid
72
+    ##@brief A tuple of fieldname (or a uniq fieldname) representing uid
73 73
     _uid = None 
74 74
 
75
-    ## @brief Construct an object representing an Editorial component
75
+    ##@brief Construct an object representing an Editorial component
76 76
     # @note Can be considered as EmClass instance
77 77
     def __init__(self, **kwargs):
78 78
         if self._abstract:
79 79
             raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
80
-        ## @brief A dict that stores fieldvalues indexed by fieldname
80
+        ##@brief A dict that stores fieldvalues indexed by fieldname
81 81
         self.__datas = { fname:None for fname in self._fields }
82
-        ## @brief Store a list of initianilized fields when instanciation not complete else store True
82
+        ##@brief Store a list of initianilized fields when instanciation not complete else store True
83 83
         self.__initialized = list()
84
-        ## @brief Datas accessor. Instance of @ref LeObjectValues
84
+        ##@brief Datas accessor. Instance of @ref LeObjectValues
85 85
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
86 86
 
87 87
         # Checks that uid is given
@@ -114,12 +114,12 @@ class LeObject(object):
114 114
     #   Fields datas handling methods   #
115 115
     #-----------------------------------#
116 116
 
117
-    ## @brief @property True if LeObject is initialized else False
117
+    ##@brief @property True if LeObject is initialized else False
118 118
     @property
119 119
     def initialized(self):
120 120
         return not isinstance(self.__initialized, list)
121 121
 
122
-    ## @brief Return a list of fieldnames
122
+    ##@brief Return a list of fieldnames
123 123
     # @param include_ro bool : if True include read only field names
124 124
     # @return a list of str
125 125
     @classmethod
@@ -133,7 +133,7 @@ class LeObject(object):
133 133
     def name2objname(cls, name):
134 134
         return name.title()
135 135
     
136
-    ## @brief Return the datahandler asssociated with a LeObject field
136
+    ##@brief Return the datahandler asssociated with a LeObject field
137 137
     # @param fieldname str : The fieldname
138 138
     # @return A data handler instance
139 139
     @classmethod
@@ -142,7 +142,7 @@ class LeObject(object):
142 142
             raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
143 143
         return cls._fields[fieldname]
144 144
  
145
-    ## @brief Return a LeObject child class from a name
145
+    ##@brief Return a LeObject child class from a name
146 146
     # @warning This method has to be called from dynamically generated LeObjects
147 147
     # @param leobject_name str : LeObject name
148 148
     # @return A LeObject child class
@@ -161,7 +161,7 @@ class LeObject(object):
161 161
     def is_abstract(cls):
162 162
         return cls._abstract
163 163
 
164
-    ## @brief Read only access to all datas
164
+    ##@brief Read only access to all datas
165 165
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
166 166
     # @param name str : field name
167 167
     # @return the Value
@@ -174,7 +174,7 @@ class LeObject(object):
174 174
             raise RuntimeError("The field %s is not initialized yet (and have no value)" % name)
175 175
         return self.__datas[name]
176 176
     
177
-    ## @brief Datas setter
177
+    ##@brief Datas setter
178 178
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
179 179
     # @param fname str : field name
180 180
     # @param fval * : field value
@@ -211,7 +211,7 @@ class LeObject(object):
211 211
             else:
212 212
                 self.__datas[fname] = val
213 213
     
214
-    ## @brief Update the __initialized attribute according to LeObject internal state
214
+    ##@brief Update the __initialized attribute according to LeObject internal state
215 215
     #
216 216
     # Check the list of initialized fields and set __initialized to True if all fields initialized
217 217
     def __set_initialized(self):
@@ -220,7 +220,7 @@ class LeObject(object):
220 220
             if set(expected_fields) == set(self.__initialized):
221 221
                 self.__initialized = True
222 222
 
223
-    ## @brief Designed to be called when datas are modified
223
+    ##@brief Designed to be called when datas are modified
224 224
     #
225 225
     # Make different checks on the LeObject given it's state (fully initialized or not)
226 226
     # @return None if checks succeded else return an exception list
@@ -267,7 +267,7 @@ class LeObject(object):
267 267
     #   Other methods    #
268 268
     #--------------------#
269 269
     
270
-    ## @brief Temporary method to set private fields attribute at dynamic code generation
270
+    ##@brief Temporary method to set private fields attribute at dynamic code generation
271 271
     #
272 272
     # This method is used in the generated dynamic code to set the _fields attribute
273 273
     # at the end of the dyncode parse

+ 23
- 23
lodel/leapi/query.py Visa fil

@@ -9,13 +9,13 @@ class LeQueryError(Exception):
9 9
 
10 10
 class LeQuery(object):
11 11
 
12
-    ## @brief The datasource object used for this query
12
+    ##@brief The datasource object used for this query
13 13
     datasource = None
14 14
 
15
-    ## @brief The available operators used in query definitions
15
+    ##@brief The available operators used in query definitions
16 16
     query_operators = ['=', '<=', '>=', '!=', '<', '>', ' in ', ' not in ', ' like ', ' not like ']
17 17
 
18
-    ## @brief Constructor
18
+    ##@brief Constructor
19 19
     # @param target_class EmClass : class of the object to query about
20 20
     def __init__(self, target_class):
21 21
         if not issubclass(target_class, LeObject):
@@ -23,17 +23,17 @@ class LeQuery(object):
23 23
         self.target_class = target_class
24 24
 
25 25
 
26
-## @brief Class representing an Insert query
26
+##@brief Class representing an Insert query
27 27
 class LeInsertQuery(LeQuery):
28 28
 
29
-    ## @brief Constructor
29
+    ##@brief Constructor
30 30
     # @param target_class EmClass: class corresponding to the inserted object
31 31
     # @param datas dict : datas to insert
32 32
     def __init__(self, target_class, datas):
33 33
         super().__init__(target_class)
34 34
         self.datas = datas
35 35
 
36
-    ## @brief executes the insert query
36
+    ##@brief executes the insert query
37 37
     # @return bool
38 38
     # @TODO reactivate the LodelHooks call when this class is implemented
39 39
     def execute(self):
@@ -42,7 +42,7 @@ class LeInsertQuery(LeQuery):
42 42
         # ret = LodelHook.call_hook('leapi_insert_post', self.target_class, ret)
43 43
         return ret
44 44
 
45
-    ## @brief calls the datasource to perform the insert command
45
+    ##@brief calls the datasource to perform the insert command
46 46
     # @param datas dict : formatted datas corresponding to the insert
47 47
     # @return str : the uid of the inserted object
48 48
     def __insert(self, **datas):
@@ -51,15 +51,15 @@ class LeInsertQuery(LeQuery):
51 51
         return res
52 52
 
53 53
 
54
-## @brief Class representing an Abstract Filtered Query
54
+##@brief Class representing an Abstract Filtered Query
55 55
 class LeFilteredQuery(LeQuery):
56 56
 
57
-    ## @brief Constructor
57
+    ##@brief Constructor
58 58
     # @param target_class EmClass : Object of the query
59 59
     def __init__(self, target_class):
60 60
         super().__init__(target_class)
61 61
 
62
-    ## @brief Validates the query filters
62
+    ##@brief Validates the query filters
63 63
     # @param query_filters list
64 64
     # @return bool
65 65
     # @raise LeQueryError if one of the filter is not valid
@@ -70,7 +70,7 @@ class LeFilteredQuery(LeQuery):
70 70
                 raise LeQueryError("The operator %s is not valid." % query_filter[1])
71 71
         return True
72 72
 
73
-    ## @brief Checks if a field is relational
73
+    ##@brief Checks if a field is relational
74 74
     # @param field str : Name of the field
75 75
     # @return bool
76 76
     @classmethod
@@ -78,10 +78,10 @@ class LeFilteredQuery(LeQuery):
78 78
         return field.startswith('superior.') or field.startswith('subordinate.')
79 79
 
80 80
 
81
-## @brief Class representing a Get Query
81
+##@brief Class representing a Get Query
82 82
 class LeGetQuery(LeFilteredQuery):
83 83
 
84
-    ## @brief Constructor
84
+    ##@brief Constructor
85 85
     # @param target_class EmClass : main class
86 86
     # @param query_filters
87 87
     # @param field_list list
@@ -101,7 +101,7 @@ class LeGetQuery(LeFilteredQuery):
101 101
         self.offset = offset
102 102
         self.instanciate = instanciate
103 103
 
104
-    ## @brief executes the query
104
+    ##@brief executes the query
105 105
     # @return list
106 106
     # @TODO activate LodelHook calls
107 107
     def execute(self):
@@ -137,7 +137,7 @@ class LeGetQuery(LeFilteredQuery):
137 137
         results = self._datasource.select()  # TODO add the correct arguments for the datasource's method call
138 138
         return results
139 139
 
140
-    ## @brief prepares the field list
140
+    ##@brief prepares the field list
141 141
     # @return list
142 142
     # @raise LeApiDataCheckError
143 143
     def __prepare_field_list(self):
@@ -159,12 +159,12 @@ class LeGetQuery(LeFilteredQuery):
159 159
 
160 160
         return ret_field_list
161 161
 
162
-    ## @brief prepares a relational field
162
+    ##@brief prepares a relational field
163 163
     def __prepare_relational_field(self, field):
164 164
         # TODO Implement the method
165 165
         return field
166 166
 
167
-    ## @brief splits the filter string into a tuple (FIELD, OPERATOR, VALUE)
167
+    ##@brief splits the filter string into a tuple (FIELD, OPERATOR, VALUE)
168 168
     # @param filter str
169 169
     # @return tuple
170 170
     # @raise ValueError
@@ -190,7 +190,7 @@ class LeGetQuery(LeFilteredQuery):
190 190
         op_re_piece += ')'
191 191
         self.query_re = re.compile('^\s*(?P<field>(((superior)|(subordinate))\.)?[a-z_][a-z0-9\-_]*)\s*'+op_re_piece+'\s*(?P<value>[^<>=!].*)\s*$', flags=re.IGNORECASE)
192 192
 
193
-    ## @brief checks if a field is in the target class of the query
193
+    ##@brief checks if a field is in the target class of the query
194 194
     # @param field str
195 195
     # @return str
196 196
     # @raise ValueError
@@ -199,7 +199,7 @@ class LeGetQuery(LeFilteredQuery):
199 199
             return ValueError("No such field '%s' in %s" % (field, self.target_class))
200 200
         return field
201 201
 
202
-    ## @brief Prepares the filters (relational and others)
202
+    ##@brief Prepares the filters (relational and others)
203 203
     # @return tuple
204 204
     def __prepare_filters(self):
205 205
         filters = list()
@@ -234,7 +234,7 @@ class LeGetQuery(LeFilteredQuery):
234 234
         datas['target_class'] = self.target_class
235 235
         return datas
236 236
 
237
-    ## @brief prepares the "order" parameters
237
+    ##@brief prepares the "order" parameters
238 238
     # @return list
239 239
     def __prepare_order(self):
240 240
         errors = dict()
@@ -269,7 +269,7 @@ class LeUpdateQuery(LeFilteredQuery):
269 269
         # ret = LodelHook.call_hook('leapi_update_post', self.target_object, ret)
270 270
         return ret
271 271
 
272
-    ## @brief calls the datasource's update method and the corresponding hooks
272
+    ##@brief calls the datasource's update method and the corresponding hooks
273 273
     # @return bool
274 274
     # @TODO change the behavior in case of error in the update process
275 275
     def __update(self):
@@ -280,7 +280,7 @@ class LeUpdateQuery(LeFilteredQuery):
280 280
         else:
281 281
             return False
282 282
 
283
-    ## @brief prepares the query_filters to be used as argument for the datasource's update method
283
+    ##@brief prepares the query_filters to be used as argument for the datasource's update method
284 284
     def __prepare(self):
285 285
         datas = dict()
286 286
         if LeFilteredQuery.validate_query_filters(self.query_filters):
@@ -304,7 +304,7 @@ class LeDeleteQuery(LeFilteredQuery):
304 304
         # ret = LodelHook.call('leapi_delete_post', self.target_object, ret)
305 305
         return ret
306 306
 
307
-    ## @brief calls the datasource's delete method
307
+    ##@brief calls the datasource's delete method
308 308
     # @return bool
309 309
     # @TODO change the behavior in case of error in the update process
310 310
     def __delete(self):

+ 4
- 4
lodel/logger.py Visa fil

@@ -23,7 +23,7 @@ def __init_from_settings():
23 23
     for name, logging_opt in Settings.logging.items():
24 24
         add_handler(name, logging_opt)
25 25
 
26
-## @brief Add an handler, identified by a name, to a given logger 
26
+##@brief Add an handler, identified by a name, to a given logger 
27 27
 #
28 28
 # logging_opt is a dict with logger option. Allowed keys are : 
29 29
 # - filename : take a filepath as value and cause the use of a logging.handlers.RotatingFileHandler
@@ -67,14 +67,14 @@ def add_handler(name, logging_opt):
67 67
     logger.addHandler(handler)
68 68
     
69 69
 
70
-## @brief Remove an handler generated from configuration (runtime logger configuration)
70
+##@brief Remove an handler generated from configuration (runtime logger configuration)
71 71
 # @param name str : handler name
72 72
 def remove_handler(name):
73 73
     if name in handlers:
74 74
         logger.removeHandler(handlers[name])
75 75
     # else: can we do anything ?
76 76
 
77
-## @brief Utility function that disable unconditionnaly handlers that implies console output
77
+##@brief Utility function that disable unconditionnaly handlers that implies console output
78 78
 # @note In fact, this function disables handlers generated from settings wich are instances of logging.StreamHandler
79 79
 def remove_console_handlers():
80 80
     for name, handler in handlers.items():
@@ -84,7 +84,7 @@ def remove_console_handlers():
84 84
 
85 85
 # Utility functions
86 86
 
87
-## @brief Generic logging function
87
+##@brief Generic logging function
88 88
 # @param lvl int : Log severity
89 89
 # @param msg str : log message
90 90
 # @param *args : additional positionnal arguments

+ 32
- 3
lodel/plugin/__init__.py Visa fil

@@ -4,10 +4,39 @@
4 4
 #
5 5
 # @section howto_writeplugin_basicstruct Plugin basic structure
6 6
 # A plugins is a python package that have to contains 3 files :
7
-# - <code>__init__.py</code>
8
-# - <code>main.py</code> ( defined in @ref lodel.plugin.plugins.MAIN_NAME )
9
-# - <code>confspec.py</code> ( defined in @ref lodel.plugin.plugins.CONFSPEC_NAME )
7
+#- <code>__init__.py</code>
8
+#- <code>main.py</code> ( defined in @ref lodel.plugin.plugins.MAIN_FILENAME )
9
+#- <code>confspec.py</code> ( defined in
10
+#@ref lodel.plugin.plugins.CONFSPEC_FILENAME )
10 11
 #
12
+# There is an example plugin in @ref plugins/dummy
13
+#
14
+# @subsection howto_writreplugin_confspec Plugin configuration specification
15
+# First of all a good practice is to preffix all plugin specific configuration
16
+# key with <code>lodel2.plugin.PLUGIN_NAME</code>.
17
+#
18
+# A configuration specification is a dict containing dict containing
19
+# tupe(DEFAULT_VALUE, VALIDATOR). The first level dict keys are sections, and
20
+# the dictionnary contained in it contains conf keys. More information on 
21
+# validators : @ref lodel.settings.validator
22
+# 
23
+# @subsubsection howto_writreplugin_confspec_example Example :
24
+#
25
+#A confspec that matches this peace of configuration file
26
+#<pre>
27
+#[lodel2.plugin.fooplugin]
28
+#hello = ...
29
+#foo = ...
30
+#bar = ...
31
+#</pre>
32
+#would be
33
+#<pre>
34
+#{
35
+#   'lodel2.plugin.fooplugin': {
36
+#                                   'foo': ...,
37
+#                                   'bar': ...,
38
+#                                   'hello': ..., } }
39
+#</pre>
11 40
12 41
 
13 42
 from .hooks import LodelHook

+ 10
- 10
lodel/plugin/hooks.py Visa fil

@@ -4,16 +4,16 @@ import os
4 4
 import copy
5 5
 from importlib.machinery import SourceFileLoader
6 6
 
7
-## @brief Class designed to handle a hook's callback with a priority
7
+##@brief Class designed to handle a hook's callback with a priority
8 8
 class DecoratedWrapper(object):
9
-    ## @brief Constructor
9
+    ##@brief Constructor
10 10
     # @param hook function : the function to wrap
11 11
     # @param priority int : the callbacl priority
12 12
     def __init__(self, hook, priority):
13 13
         self._priority = priority
14 14
         self._hook = hook
15 15
     
16
-    ## @brief Call the callback
16
+    ##@brief Call the callback
17 17
     # @param hook_name str : The name of the called hook
18 18
     # @param caller * : The caller (depends on the hook)
19 19
     # @param payload * : Datas that depends on the hook
@@ -21,7 +21,7 @@ class DecoratedWrapper(object):
21 21
     def __call__(self, hook_name, caller, payload):
22 22
         return self._hook(hook_name, caller, payload)
23 23
 
24
-## @brief Decorator designed to register hook's callbacks
24
+##@brief Decorator designed to register hook's callbacks
25 25
 #
26 26
 # @note Decorated functions are expected to take 3 arguments :
27 27
 #  - hook_name : the called hook name
@@ -29,17 +29,17 @@ class DecoratedWrapper(object):
29 29
 #  - payload : datas depending on the hook
30 30
 class LodelHook(object):
31 31
     
32
-    ## @brief Stores all hooks (DecoratedWrapper instances)
32
+    ##@brief Stores all hooks (DecoratedWrapper instances)
33 33
     _hooks = dict()
34 34
     
35
-    ## @brief Decorator constructor
35
+    ##@brief Decorator constructor
36 36
     # @param hook_name str : the name of the hook to register to
37 37
     # @param priority int : the hook priority
38 38
     def __init__(self, hook_name, priority = None):
39 39
         self._hook_name = hook_name
40 40
         self._priority = 0xFFFF if priority is None else priority
41 41
     
42
-    ## @brief called just after __init__
42
+    ##@brief called just after __init__
43 43
     # @param hook function : the decorated function
44 44
     # @return the hook argument
45 45
     def __call__(self, hook):
@@ -50,7 +50,7 @@ class LodelHook(object):
50 50
         self._hooks[self._hook_name] = sorted(self._hooks[self._hook_name], key = lambda h: h._priority)
51 51
         return hook
52 52
 
53
-    ## @brief Call hooks
53
+    ##@brief Call hooks
54 54
     # @param hook_name str : the hook's name
55 55
     # @param caller * : the hook caller (depends on the hook)
56 56
     # @param payload * : datas for the hook
@@ -63,7 +63,7 @@ class LodelHook(object):
63 63
                 payload = hook(hook_name, caller, payload)
64 64
         return payload
65 65
     
66
-    ## @brief Fetch registered hooks
66
+    ##@brief Fetch registered hooks
67 67
     # @param names list | None : optionnal filter on name
68 68
     # @param cls
69 69
     # @return a list of functions
@@ -76,7 +76,7 @@ class LodelHook(object):
76 76
             res = copy.copy(cls._hooks)
77 77
         return { name: [(hook._hook, hook._priority) for hook in hooks] for name, hooks in res.items() }
78 78
     
79
-    ## @brief Unregister all hooks
79
+    ##@brief Unregister all hooks
80 80
     # @param cls
81 81
     # @warning REALLY NOT a good idea !
82 82
     # @note implemented for testing purpose

+ 7
- 7
lodel/plugin/plugins.py Visa fil

@@ -12,7 +12,7 @@ from importlib.machinery import SourceFileLoader, SourcelessFileLoader
12 12
 # - main.py containing hooks registration etc
13 13
 # - confspec.py containing a configuration specification dictionary named CONFSPEC
14 14
 
15
-## @brief The package in wich we will load plugins modules
15
+##@brief The package in wich we will load plugins modules
16 16
 VIRTUAL_PACKAGE_NAME = 'lodel.plugins_pkg'
17 17
 CONFSPEC_FILENAME = 'confspec.py'
18 18
 MAIN_FILENAME = 'main.py'
@@ -23,15 +23,15 @@ class PluginError(Exception):
23 23
 
24 24
 class Plugins(object):
25 25
     
26
-    ## @brief Stores plugin directories paths
26
+    ##@brief Stores plugin directories paths
27 27
     _plugin_directories = None
28
-    ## @brief Optimisation cache storage for plugin paths
28
+    ##@brief Optimisation cache storage for plugin paths
29 29
     _plugin_paths = dict()
30 30
 
31 31
     def __init__(self): # may be useless
32 32
         self.started()
33 33
     
34
-    ## @brief Given a plugin name returns the plugin path
34
+    ##@brief Given a plugin name returns the plugin path
35 35
     # @param plugin_name str : The plugin name
36 36
     # @return the plugin directory path
37 37
     @classmethod
@@ -50,7 +50,7 @@ class Plugins(object):
50 50
                 return plugin_path
51 51
         raise NameError("No plugin named '%s'" % plugin_name)
52 52
 
53
-    ## @brief Fetch a confspec given a plugin_name
53
+    ##@brief Fetch a confspec given a plugin_name
54 54
     # @param plugin_name str : The plugin name
55 55
     # @return a dict of conf spec
56 56
     # @throw PluginError if plugin_name is not valid
@@ -70,7 +70,7 @@ class Plugins(object):
70 70
             raise PluginError("Failed to load plugin '%s'. It seems that the plugin name is not valid" % plugin_name)
71 71
         return getattr(confspec_module, CONFSPEC_VARNAME)
72 72
  
73
-    ## @brief Load a module to register plugin's hooks
73
+    ##@brief Load a module to register plugin's hooks
74 74
     # @param plugin_name str : The plugin name
75 75
     @classmethod
76 76
     def load_plugin(cls, plugin_name):
@@ -86,7 +86,7 @@ class Plugins(object):
86 86
         except ImportError:
87 87
             raise PluginError("Failed to load plugin '%s'. It seems that the plugin name is not valid" % plugin_name)
88 88
         
89
-    ## @brief Bootstrap the Plugins class
89
+    ##@brief Bootstrap the Plugins class
90 90
     @classmethod
91 91
     def bootstrap(cls, plugins_directories):
92 92
         cls._plugin_directories = plugins_directories

+ 38
- 0
lodel/settings/__init__.py Visa fil

@@ -1,6 +1,44 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
+## @package lodel.settings Lodel2 settings package
4
+#
5
+# @par Configuration files
6
+# The configurations files are in ini format (thank's python ConfigParser...).
7
+# To know how the settings are validated and specified see
8
+# @ref lodel.settings.validator and @ref howto_writeplugin_basicstruct
9
+# The configuration is divided in two parts :
10
+#- a global configuration file that contains
11
+# - the path to the lodel2 lib directory
12
+# - the paths of directories containing plugins
13
+#- a conf.d directories containing multiple configuration files
14
+#  
15
+# @par Bootstrap/load/use in lodel instance
16
+# To use Settings in production you have to write a loader that will bootstrap
17
+# the Settings class allowing @ref lodel.settings.__init__.py to expose a copy
18
+# of the lodel.settings.Settings representation of the
19
+# @ref lodel.settings.settings.Settings.__confs . Here is an example of 
20
+# loader file :
21
+# <pre>
22
+# #-*- coding: utf-8 -*-
23
+# from lodel.settings.settings import Settings
24
+# Settings.bootstrap(
25
+#                       conf_file = 'somepath/settings_local.ini',
26
+#                       conf_dir = 'somepath/conf.d')
27
+# </pre>
28
+# Once this file is imported it allows to all lodel2 modules to use settings
29
+# like this :
30
+# <pre>
31
+# from lodel.settings import Settings
32
+# if Settings.debug:
33
+#   print("DEBUG MODE !")
34
+# </pre>
35
+#
36
+
3 37
 from lodel.settings.settings import Settings as SettingsHandler
38
+
39
+##@brief Bootstraped instance
4 40
 settings = SettingsHandler.bootstrap()
5 41
 if settings is not None:
42
+    ##@brief Exposed variable that represents configurations values in a
43
+    # namedtuple tree
6 44
     Settings = settings.confs

+ 30
- 37
lodel/settings/settings.py Visa fil

@@ -12,34 +12,21 @@ from lodel.settings.utils import SettingsError, SettingsErrors
12 12
 from lodel.settings.validator import SettingValidator, LODEL2_CONF_SPECS
13 13
 from lodel.settings.settings_loader import SettingsLoader
14 14
 
15
-## @package lodel.settings Lodel2 settings package
16
-#
17
-# Contains all module that help handling settings
18
-
19 15
 ## @package lodel.settings.settings Lodel2 settings module
20 16
 #
21
-# Handles configuration load/parse/check.
22
-#
23
-# @subsection Configuration load process
24
-#
25
-# The configuration load process is not trivial. In fact loaded plugins are able to add their own options.
26
-# But the list of plugins to load and the plugins options are in the same file, the instance configuration file.
27
-#
28
-# @subsection Configuration specification
29
-#
30
-# Configuration specification is divided in 2 parts :
31
-# - default values
32
-# - value validation/cast (see @ref Lodel.settings.validator.ConfValidator )
33
-# 
17
+# Contains the class that handles the namedtuple tree of settings
34 18
 
35
-## @brief A default python system lib path
19
+##@brief A default python system lib path
36 20
 PYTHON_SYS_LIB_PATH = '/usr/local/lib/python{major}.{minor}/'.format(
37 21
 
38
-                                                                        major = sys.version_info.major,
39
-                                                                        minor = sys.version_info.minor)
40
-## @brief Handles configuration load etc.
22
+                                                major = sys.version_info.major,
23
+                                                minor = sys.version_info.minor)
24
+##@brief Handles configuration load etc.
41 25
 #
42
-# @par Basic usage
26
+# To see howto bootstrap Settings and use it in lodel instance see 
27
+# @ref lodel.settings
28
+# 
29
+# @par Basic instance usage
43 30
 # For example if a file defines confs like :
44 31
 # <pre>
45 32
 # [super_section]
@@ -49,13 +36,16 @@ PYTHON_SYS_LIB_PATH = '/usr/local/lib/python{major}.{minor}/'.format(
49 36
 # <pre> settings_instance.confs.super_section.super_conf </pre>
50 37
 #
51 38
 # @par Init sequence
52
-# The initialization sequence is a bit tricky. In fact, plugins adds allowed configuration 
53
-# sections/values, but the list of plugins to load in in... the settings.
39
+# The initialization sequence is a bit tricky. In fact, plugins adds allowed
40
+# configuration sections/values, but the list of plugins to load in in... the 
41
+# settings.
54 42
 # Here is the conceptual presentation of Settings class initialization stages :
55 43
 #   -# Preloading (sets values like lodel2 library path or the plugins path)
56
-#   -# Ask a @ref lodel.settings.setting_loader.SettingsLoader to load all configurations files
44
+#   -# Ask a @ref lodel.settings.setting_loader.SettingsLoader to load all 
45
+#configurations files
57 46
 #   -# Fetch the list of plugins in the loaded settings
58
-#   -# Merge plugins settings specification with the global lodel settings specs ( see @ref lodel.plugin )
47
+#   -# Merge plugins settings specification with the global lodel settings 
48
+#specs ( see @ref lodel.plugin )
59 49
 #   -# Fetch all settings from the merged settings specs
60 50
 #
61 51
 # @par Init sequence in practical
@@ -65,10 +55,11 @@ PYTHON_SYS_LIB_PATH = '/usr/local/lib/python{major}.{minor}/'.format(
65 55
 #   -# @ref Settings.__populate_from_specs() (step 5)
66 56
 #   -# And finally @ref Settings.__confs_to_namedtuple()
67 57
 #
68
-# @todo handles default sections for variable sections (sections ending with '.*')
58
+# @todo handles default sections for variable sections (sections ending with 
59
+# '.*')
69 60
 class Settings(object):
70 61
     
71
-    ## @brief global conf specsification (default_value + validator)
62
+    ##@brief global conf specsification (default_value + validator)
72 63
     _conf_preload = {
73 64
             'lib_path': (   PYTHON_SYS_LIB_PATH+'/lodel2/',
74 65
                             SettingValidator('directory')),
@@ -77,7 +68,9 @@ class Settings(object):
77 68
     }
78 69
     instance = None
79 70
     
80
-    ## @brief Should be called only by the boostrap classmethod
71
+    ##@brief Should be called only by the boostrap classmethod
72
+    # @param conf_file str : Path to the global lodel2 configuration file
73
+    # @param conf_dir str : Path to the conf directory
81 74
     def __init__(self, conf_file = '/etc/lodel2/lodel2.conf', conf_dir = 'conf.d'):
82 75
         self.__confs = dict()
83 76
         self.__conf_dir = conf_dir
@@ -86,7 +79,7 @@ class Settings(object):
86 79
         #   and self.__confs['lodel2']['lib_path'] set
87 80
         self.__bootstrap()
88 81
     
89
-    ## @brief Stores as class attribute a Settings instance
82
+    ##@brief Stores as class attribute a Settings instance
90 83
     @classmethod
91 84
     def bootstrap(cls, conf_file = None, conf_dir = None):
92 85
         if cls.instance is None:
@@ -96,13 +89,13 @@ class Settings(object):
96 89
                 cls.instance = cls(conf_file, conf_dir)
97 90
         return cls.instance
98 91
 
99
-    ## @brief Configuration keys accessor
92
+    ##@brief Configuration keys accessor
100 93
     # @return All confs organised into named tuples
101 94
     @property
102 95
     def confs(self):
103 96
         return copy.copy(self.__confs)
104 97
 
105
-    ## @brief This method handlers Settings instance bootstraping
98
+    ##@brief This method handlers Settings instance bootstraping
106 99
     def __bootstrap(self):
107 100
         lodel2_specs = LODEL2_CONF_SPECS
108 101
         plugins_opt_specs = lodel2_specs['lodel2']['plugins']
@@ -126,7 +119,7 @@ class Settings(object):
126 119
         specs = self.__merge_specs(specs)
127 120
         self.__populate_from_specs(specs, loader)
128 121
     
129
-    ## @brief Produce a configuration specification dict by merging all specifications
122
+    ##@brief Produce a configuration specification dict by merging all specifications
130 123
     #
131 124
     # Merges global lodel2 conf spec from @ref lodel.settings.validator.LODEL2_CONF_SPECS
132 125
     # and configuration specifications from loaded plugins
@@ -144,7 +137,7 @@ class Settings(object):
144 137
                     res[section][kname] = copy.copy(spec[section][kname])
145 138
         return res
146 139
     
147
-    ## @brief Populate the Settings instance with options values fecthed with the loader from merged specs
140
+    ##@brief Populate the Settings instance with options values fecthed with the loader from merged specs
148 141
     #
149 142
     # Populate the __confs attribute
150 143
     # @param specs dict : Settings specification dictionnary as returned by __merge_specs
@@ -170,7 +163,7 @@ class Settings(object):
170 163
         self.__confs_to_namedtuple()
171 164
         pass
172 165
     
173
-    ## @brief Transform the __confs attribute into imbricated namedtuple
166
+    ##@brief Transform the __confs attribute into imbricated namedtuple
174 167
     #
175 168
     # For example an option named "foo" in a section named "hello.world" will
176 169
     # be acessible with self.__confs.hello.world.foo
@@ -225,14 +218,14 @@ class Settings(object):
225 218
                 path.append( (curname, cur) )
226 219
                 nodename += '.'+curname.title()
227 220
     
228
-    ## @brief Forge a named tuple given a conftree node
221
+    ##@brief Forge a named tuple given a conftree node
229 222
     # @param conftree dict : A conftree node
230 223
     # @return a named tuple with fieldnames corresponding to conftree keys
231 224
     def __tree2namedtuple(self, conftree, name):
232 225
         ResNamedTuple = namedtuple(name, conftree.keys())
233 226
         return ResNamedTuple(**conftree)
234 227
 
235
-    ## @brief Load base global configurations keys
228
+    ##@brief Load base global configurations keys
236 229
     #
237 230
     # Base configurations keys are :
238 231
     # - lodel2 lib path

+ 7
- 7
lodel/settings/settings_loader.py Visa fil

@@ -7,16 +7,16 @@ import copy
7 7
 from lodel.settings.utils import *
8 8
 
9 9
    
10
-## @brief Merges and loads configuration files
10
+##@brief Merges and loads configuration files
11 11
 class SettingsLoader(object):
12
-    ## @brief Constructor
12
+    ##@brief Constructor
13 13
     # @param conf_path str : conf.d path
14 14
     def __init__(self,conf_path):
15 15
         self.__conf_path=conf_path
16 16
         self.__conf_sv=set()
17 17
         self.__conf=self.__merge()
18 18
     
19
-    ## @brief Lists and merges files in settings_loader.conf_path
19
+    ##@brief Lists and merges files in settings_loader.conf_path
20 20
     #
21 21
     # 
22 22
     # @return dict()
@@ -50,7 +50,7 @@ class SettingsLoader(object):
50 50
         
51 51
         
52 52
     
53
-    ## @brief Returns option if exists default_value else and validates
53
+    ##@brief Returns option if exists default_value else and validates
54 54
     # @param section str : name of the section
55 55
     # @param keyname str
56 56
     # @param validator callable : takes one argument value and raises validation fail
@@ -71,7 +71,7 @@ class SettingsLoader(object):
71 71
              return default_value
72 72
                               
73 73
     
74
-    ## @brief Returns the section to be configured
74
+    ##@brief Returns the section to be configured
75 75
     # @param section_prefix str
76 76
     # @param default_section str
77 77
     # @return the section as dict()
@@ -93,8 +93,8 @@ class SettingsLoader(object):
93 93
             
94 94
         return sections;
95 95
     
96
-    ## @brief Returns the sections which have not been configured
96
+    ##@brief Returns the sections which have not been configured
97 97
     # @return list of missing options
98 98
     def getremains(self):
99 99
         return list(self.__conf_sv)
100
-        
100
+        

+ 8
- 4
lodel/settings/utils.py Visa fil

@@ -1,9 +1,13 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
-## @brief Error class for settings errors
3
+## @package lodel.settings.utils Lodel 2 settings utility
4
+#
5
+# For the moment defines exception classes
6
+
7
+##@brief Error class for settings errors
4 8
 class SettingsError(Exception):
5 9
     
6
-    ## @brief Instanciate a new SettingsError
10
+    ##@brief Instanciate a new SettingsError
7 11
     # @param msg str : Error message
8 12
     # @param key_id str : The key concerned by the error
9 13
     def __init__(self, msg = "Unknown error", key_id = None, filename = None):
@@ -23,10 +27,10 @@ class SettingsError(Exception):
23 27
         res += ": %s" % (self.__msg)
24 28
         return res
25 29
 
26
-## @brief Designed to handles mutliple SettingsError
30
+##@brief Designed to handles mutliple SettingsError
27 31
 class SettingsErrors(Exception):
28 32
     
29
-    ## @brief Instanciate an SettingsErrors
33
+    ##@brief Instanciate an SettingsErrors
30 34
     # @param exceptions list : list of SettingsError instance
31 35
     def __init__(self, exceptions):
32 36
         for expt in exceptions: 

+ 18
- 15
lodel/settings/validator.py Visa fil

@@ -9,11 +9,13 @@ import copy
9 9
 ## @package lodel.settings.validator Lodel2 settings validators/cast module
10 10
 #
11 11
 # Validator are registered in the SettingValidator class.
12
+# @note to get a list of registered default validators just run
13
+# <pre>$ python scripts/settings_validator.py</pre>
12 14
 
13 15
 class SettingsValidationError(Exception):
14 16
     pass
15 17
 
16
-## @brief Handles settings validators
18
+##@brief Handles settings validators
17 19
 #
18 20
 # Class instance are callable objects that takes a value argument (the value to validate). It raises
19 21
 # a SettingsValidationError if validation fails, else it returns a properly
@@ -23,13 +25,13 @@ class SettingValidator(object):
23 25
     _validators = dict()
24 26
     _description = dict()
25 27
     
26
-    ## @brief Instanciate a validator
28
+    ##@brief Instanciate a validator
27 29
     def __init__(self, name, none_is_valid = False):
28 30
         if name is not None and name not in self._validators:
29 31
             raise NameError("No validator named '%s'" % name)
30 32
         self.__name = name
31 33
 
32
-    ## @brief Call the validator
34
+    ##@brief Call the validator
33 35
     # @param value *
34 36
     # @return properly casted value
35 37
     # @throw SettingsValidationError
@@ -41,7 +43,7 @@ class SettingValidator(object):
41 43
         except Exception as e:
42 44
             raise SettingsValidationError(e)
43 45
     
44
-    ## @brief Register a new validator
46
+    ##@brief Register a new validator
45 47
     # @param name str : validator name
46 48
     # @param callback callable : the function that will validate a value
47 49
     @classmethod
@@ -54,12 +56,12 @@ class SettingValidator(object):
54 56
         cls._validators[name] = callback
55 57
         cls._description[name] = description
56 58
     
57
-    ## @brief Get the validator list associated with description
59
+    ##@brief Get the validator list associated with description
58 60
     @classmethod
59 61
     def validators_list(cls):
60 62
         return copy.copy(cls._description)
61 63
 
62
-    ## @brief Create and register a list validator
64
+    ##@brief Create and register a list validator
63 65
     # @param elt_validator callable : The validator that will be used for validate each elt value
64 66
     # @param validator_name str
65 67
     # @param description None | str
@@ -80,7 +82,7 @@ class SettingValidator(object):
80 82
                                 description)
81 83
         return cls(validator_name)
82 84
                 
83
-    ## @brief Create and register a regular expression validator
85
+    ##@brief Create and register a regular expression validator
84 86
     # @param pattern str : regex pattern
85 87
     # @param validator_name str : The validator name
86 88
     # @param description str : Validator description
@@ -100,20 +102,21 @@ class SettingValidator(object):
100 102
 
101 103
     
102 104
     ## @return a list of registered validators
105
+    @classmethod
103 106
     def validators_list_str(cls):
104 107
         result = ''
105
-        for name in cls._validators:
106
-            result += "\t%s" % name
107
-            if name in self._description and self._description[name] is not None:
108
-                result += "\t: %s" % self._description[name]
108
+        for name in sorted(cls._validators.keys()):
109
+            result += "\t%016s" % name
110
+            if name in cls._description and cls._description[name] is not None:
111
+                result += ": %s" % cls._description[name]
109 112
             result += "\n"
110 113
         return result
111 114
 
112
-## @brief Integer value validator callback
115
+##@brief Integer value validator callback
113 116
 def int_val(value):
114 117
     return int(value)
115 118
 
116
-## @brief Output file validator callback
119
+##@brief Output file validator callback
117 120
 # @return A file object (if filename is '-' return sys.stderr)
118 121
 def file_err_output(value):
119 122
     if not isinstance(value, str):
@@ -122,7 +125,7 @@ def file_err_output(value):
122 125
         return sys.stderr
123 126
     return value
124 127
 
125
-## @brief Boolean value validator callback
128
+##@brief Boolean value validator callback
126 129
 def boolean_val(value):
127 130
     if not (value is True) and not (value is False):
128 131
         raise SettingsValidationError("A boolean was expected but got '%s' " % value)
@@ -194,7 +197,7 @@ SettingValidator.create_re_validator(
194 197
 #   Lodel 2 configuration specification
195 198
 #
196 199
 
197
-## @brief Global specifications for lodel2 settings
200
+##@brief Global specifications for lodel2 settings
198 201
 LODEL2_CONF_SPECS = {
199 202
     'lodel2': {
200 203
         'debug': (  True,

+ 7
- 7
lodel/utils/mlstring.py Visa fil

@@ -5,7 +5,7 @@ import hashlib
5 5
 import json
6 6
 
7 7
 
8
-## @brief Stores multilangage string
8
+##@brief Stores multilangage string
9 9
 class MlString(object):
10 10
     
11 11
     __default_lang = 'eng'
@@ -17,7 +17,7 @@ class MlString(object):
17 17
         'esp',
18 18
     ]
19 19
 
20
-    ## @brief Create a new MlString instance
20
+    ##@brief Create a new MlString instance
21 21
     # @param arg str | dict : Can be a json string, a string or a dict
22 22
     def __init__(self, arg):
23 23
         self.values = dict()
@@ -33,7 +33,7 @@ class MlString(object):
33 33
         else:
34 34
             raise ValueError('<class str>, <class dict> or <class MlString> expected, but %s found' % type(arg))
35 35
     
36
-    ## @brief Return a translation given a lang
36
+    ##@brief Return a translation given a lang
37 37
     # @param lang str | None : If None return default lang translation
38 38
     def get(self, lang = None):
39 39
         lang = self.__default_lang if lang is None else lang
@@ -44,7 +44,7 @@ class MlString(object):
44 44
         else:
45 45
             return str(self)
46 46
 
47
-    ## @brief Set a translation
47
+    ##@brief Set a translation
48 48
     # @param lang str : the lang
49 49
     # @param val str | None:  the translation if None delete the translation
50 50
     def set(self, lang, val):
@@ -57,7 +57,7 @@ class MlString(object):
57 57
         else:
58 58
             self.values[lang] = val
59 59
 
60
-    ## @brief Checks that given lang is valid
60
+    ##@brief Checks that given lang is valid
61 61
     # @param lang str : the lang
62 62
     @classmethod
63 63
     def lang_is_valid(cls, lang):
@@ -65,7 +65,7 @@ class MlString(object):
65 65
             raise ValueError('Invalid value for lang. Str expected but %s found' % type(lang))
66 66
         return lang in cls.langs
67 67
 
68
-    ## @brief Get or set the default lang
68
+    ##@brief Get or set the default lang
69 69
     @classmethod
70 70
     def default_lang(cls, lang = None):
71 71
         if lang is None:
@@ -74,7 +74,7 @@ class MlString(object):
74 74
             raise ValueError('lang "%s" is not valid"' % lang)
75 75
         cls.__default_lang = lang
76 76
     
77
-    ## @brief Return a mlstring loaded from a json string
77
+    ##@brief Return a mlstring loaded from a json string
78 78
     # @param json_str str : Json string
79 79
     @classmethod
80 80
     def from_json(cls, json_str):

+ 1
- 1
plugins/dummy/main.py Visa fil

@@ -2,7 +2,7 @@
2 2
 
3 3
 from lodel.plugin import LodelHook
4 4
 
5
-## @brief Hook's callback example
5
+##@brief Hook's callback example
6 6
 @LodelHook('leapi_get_pre')
7 7
 @LodelHook('leapi_get_post')
8 8
 @LodelHook('leapi_update_pre')

+ 6
- 0
scripts/settings_validator.py Visa fil

@@ -0,0 +1,6 @@
1
+#-*- coding: utf-8 -*-
2
+import sys
3
+import os, os.path
4
+sys.path.append(os.path.dirname(os.getcwd()+'/..'))
5
+from lodel.settings.validator import SettingValidator
6
+print(SettingValidator.validators_list_str())

+ 5
- 0
tests/settings/settings_examples/bad_merge.conf.d/file1.ini Visa fil

@@ -0,0 +1,5 @@
1
+[lodel2.merge.fails.duplicate]
2
+duplicated = foobar
3
+foo = 1337
4
+bar = 42
5
+foobar = hello world !

+ 2
- 0
tests/settings/settings_examples/bad_merge.conf.d/file2.ini Visa fil

@@ -0,0 +1,2 @@
1
+[lodel2.merge.fails.duplicate]
2
+duplicated = wrong

+ 4
- 0
tests/settings/settings_examples/merge.conf.d/file1.ini Visa fil

@@ -0,0 +1,4 @@
1
+[lodel2]
2
+a = a
3
+b = b
4
+c = c

+ 10
- 0
tests/settings/settings_examples/merge.conf.d/file2.ini Visa fil

@@ -0,0 +1,10 @@
1
+[lodel2]
2
+d = d
3
+e = e
4
+f = f
5
+
6
+[lodel2.othersection]
7
+a = a
8
+b = b
9
+c = c
10
+d = d

+ 3
- 0
tests/settings/settings_examples/merge.conf.d/file3.ini Visa fil

@@ -0,0 +1,3 @@
1
+[lodel2.othersection]
2
+e = e
3
+f = f

+ 11
- 0
tests/settings/settings_examples/remains.conf.d/file1.ini Visa fil

@@ -0,0 +1,11 @@
1
+[lodel2]
2
+a = a
3
+b = b
4
+c = c
5
+
6
+[lodel2.othersection]
7
+a = a
8
+b = b
9
+c = c
10
+d = d
11
+e = e

+ 3
- 0
tests/settings/settings_examples/remains.conf.d/file2.ini Visa fil

@@ -0,0 +1,3 @@
1
+[lodel2]
2
+d = d
3
+e = e

+ 30
- 25
tests/settings/test_settings_loader.py Visa fil

@@ -2,7 +2,7 @@
2 2
 
3 3
 import unittest
4 4
 
5
-#import tests.loader_utils
5
+from lodel.settings.utils import *
6 6
 from lodel.settings.settings_loader import SettingsLoader
7 7
 
8 8
 #A dummy validator that only returns the value
@@ -15,36 +15,36 @@ class SettingsLoaderTestCase(unittest.TestCase):
15 15
     def test_merge_getsection(self):
16 16
         """Tests merge and getSection functions """
17 17
         settings = SettingsLoader('tests/settings/conf.d')
18
-        a = settings.getsection('A')
19
-        self.assertEqual(a,dict({"a":"a1","b":"b1,b2,b3","c":"toto","fhui":"njl"}))
20
-        b = settings.getsection('B')
21
-        self.assertEqual(b,dict({"ab":"art","bb":"bj,kl,mn","cb":"tatat"}))
22
-        c = settings.getsection('C')
23
-        self.assertEqual(c,dict({"ca":"a2","cb":"b4,b2,b3","cc":"titi"}))
24
-        d = settings.getsection('D')
25
-        
26
-        for v in a:
27
-            assert ('A','{"a":"a1","b":"b1,b2,b3","c":"toto","fhui":"njl"}')
28
-        def maFonction(a):
29
-            return a
30
-        e=settings.getoption('A','a',maFonction)
18
+
19
+        e=settings.getoption('A','a',dummy_validator)
31 20
         self.assertEqual(e,'a1')
32
-        f=settings.getoption('B','bb',maFonction)
21
+        f=settings.getoption('B','bb',dummy_validator)
33 22
         self.assertEqual(f,"bj,kl,mn")
34 23
         g=settings.getremains()
35 24
         self.assertIsNotNone(g)
36
-        e=settings.getoption('A','b',maFonction)
37
-        e=settings.getoption('A','c',maFonction)
38
-        e=settings.getoption('A','fhui',maFonction)
39
-        f=settings.getoption('B','ab',maFonction)
40
-        f=settings.getoption('B','cb',maFonction)
41
-        f=settings.getoption('C','cb',maFonction)
42
-        f=settings.getoption('C','ca',maFonction)
43
-        f=settings.getoption('C','cc',maFonction)
25
+        e=settings.getoption('A','b',dummy_validator)
26
+        e=settings.getoption('A','c',dummy_validator)
27
+        e=settings.getoption('A','fhui',dummy_validator)
28
+        f=settings.getoption('B','ab',dummy_validator)
29
+        f=settings.getoption('B','cb',dummy_validator)
30
+        f=settings.getoption('C','cb',dummy_validator)
31
+        f=settings.getoption('C','ca',dummy_validator)
32
+        f=settings.getoption('C','cc',dummy_validator)
44 33
        
45 34
         g=settings.getremains()
46 35
         self.assertEqual(g,[])
47
-        
36
+    
37
+    def test_merge(self):
38
+        """ Test merge of multiple configuration files """
39
+        loader = SettingsLoader('tests/settings/settings_examples/merge.conf.d')
40
+        for value in ('a','b','c','d','e','f'):
41
+            self.assertEqual(loader.getoption('lodel2', value, dummy_validator), value)
42
+            self.assertEqual(loader.getoption('lodel2.othersection', value, dummy_validator), value)
43
+
44
+    def test_merge_conflict(self):
45
+        """ Test merge fails because of duplicated keys """
46
+        with self.assertRaises(SettingsError):
47
+            loader = SettingsLoader('tests/settings/settings_examples/bad_merge.conf.d')
48 48
      
49 49
     def test_getoption_simple(self):
50 50
         """ Testing behavior of getoption """
@@ -73,4 +73,9 @@ class SettingsLoaderTestCase(unittest.TestCase):
73 73
         loader = SettingsLoader('tests/settings/settings_examples/var_sections.conf.d')
74 74
         with self.assertRaises(NameError):
75 75
             sections = loader.getsection('lodel2.notexisting')
76
-
76
+    
77
+    @unittest.skip("Waiting implementation")
78
+    def test_remains(self):
79
+        """ Testing the remains method of SettingsLoader """
80
+        loader = SettingsLoader('tests/settings/settings_examples/remains.conf.d')
81
+        pass #TO BE DONE LATER

Loading…
Avbryt
Spara