Browse Source

Merge branch 'master' of git@git.labocleo.org:lodel2

Quentin Bonaventure 7 years ago
parent
commit
cb95014a9b

+ 109
- 101
lodel/auth/client.py View File

@@ -11,243 +11,251 @@ LodelContext.expose_modules(globals(), {
11 11
     'lodel.logger': 'logger',
12 12
     'lodel.plugin': [('SessionHandlerPlugin', 'SessionHandler')],
13 13
     'lodel.auth.exceptions': ['ClientError', 'ClientAuthenticationFailure',
14
-        'ClientPermissionDenied', 'ClientAuthenticationError'],
15
-    'lodel.leapi.query': ['LeGetQuery'],})
14
+                              'ClientPermissionDenied', 'ClientAuthenticationError'],
15
+    'lodel.leapi.query': ['LeGetQuery'], })
16 16
 
17
-##@brief Client metaclass designed to implements container accessor on 
18
-#Client Class
17
+# @brief Client metaclass designed to implements container accessor on
18
+# Client Class
19 19
 #
20 20
 #@todo Maybe we can delete this metaclass....
21
+
22
+
21 23
 class ClientMetaclass(type):
22
-    
24
+
23 25
     def __init__(self, name, bases, attrs):
24 26
         return super(ClientMetaclass, self).__init__(name, bases, attrs)
25 27
 
26 28
     def __getitem__(self, key):
27
-        return self.datas()[key]
29
+        return self.data()[key]
28 30
 
29 31
     def __delitem__(self, key):
30
-        del(self.datas()[key])
32
+        del(self.data()[key])
31 33
 
32 34
     def __setitem__(self, key, value):
33 35
         if self.get_session_token() is None:
34 36
             self.set_session_token(SessionHandler.start())
35
-        datas = self.datas()
36
-        datas[key] = value
37
+        data = self.data()
38
+        data[key] = value
37 39
 
38 40
     def __str__(self):
39 41
         return str(self._instance)
40 42
 
41
-##@brief Abstract singleton class designed to handle client informations
43
+# @brief Abstract singleton class designed to handle client informations
42 44
 #
43 45
 # This class is designed to handle client authentication and sessions
44
-class Client(object, metaclass = ClientMetaclass):
45
-    
46
-    ##@brief Singleton instance
46
+
47
+
48
+class Client(object, metaclass=ClientMetaclass):
49
+
50
+    # @brief Singleton instance
47 51
     _instance = None
48
-    ##@brief List of dict that stores field ref for login and password
52
+    # @brief List of dict that stores field ref for login and password
49 53
     #
50
-    # Storage specs : 
54
+    # Storage specs :
51 55
     #
52 56
     # A list of dict, with keys 'login' and 'password', items are tuple.
53
-    #- login tuple contains (LeObjectChild, FieldName, link_field) with:
57
+    # - login tuple contains (LeObjectChild, FieldName, link_field) with:
54 58
     # - LeObjectChild the dynclass containing the login
55 59
     # - Fieldname the fieldname of LeObjectChild containing the login
56 60
     # - link_field None if both login and password are in the same
57 61
     # LeObjectChild. Else contains the field that make the link between
58 62
     # login LeObject and password LeObject
59
-    #- password typle contains (LeObjectChild, FieldName)
63
+    # - password typle contains (LeObjectChild, FieldName)
60 64
     _infos_fields = None
61
-    
62
-    ##@brief Constant that stores the session key that stores authentication
63
-    #informations
65
+
66
+    # @brief Constant that stores the session key that stores authentication
67
+    # informations
64 68
     _AUTH_DATANAME = '__auth_user_infos'
65
-    
66 69
 
67
-    ##@brief Constructor
70
+    # @brief Constructor
68 71
     #@param session_token mixed : Session token provided by client to interface
69
-    def __init__(self,session_token = None):
72
+    def __init__(self, session_token=None):
70 73
         logger.debug(session_token)
71 74
         if self.__class__ == Client:
72 75
             raise NotImplementedError("Abstract class")
73 76
         logger.debug("New instance of Client child class %s" %
74
-            self.__class__.__name__)
77
+                     self.__class__.__name__)
75 78
         if Client._instance is not None:
76 79
             old = Client._instance
77 80
             Client._instance = None
78 81
             del(old)
79 82
             logger.debug("Replacing old Client instance by a new one")
80 83
         else:
81
-            #first instanciation, fetching settings
84
+            # first instanciation, fetching settings
82 85
             self.fetch_settings()
83
-        ##@brief Stores infos for authenticated users (None == anonymous)
86
+        # @brief Stores infos for authenticated users (None == anonymous)
84 87
         self.__user = None
85
-        ##@brief Stores the session handler
88
+        # @brief Stores the session handler
86 89
         Client._instance = self
87
-        ##@brief Stores LodelSession instance
88
-        
89
-        self.__datas = dict()
90
+        # @brief Stores LodelSession instance
91
+        self.__data = dict()
90 92
         if session_token is not None:
91
-            self.__datas = SessionHandler.restore(session_token)
93
+            self.__data = SessionHandler.restore(session_token)
92 94
         self.__session_token = session_token
93
-        
95
+
94 96
         logger.debug("New client : %s" % self)
95
-    
97
+
96 98
     def __del__(self):
97 99
         del(self.__session_token)
98
-        del(self.__datas)
100
+        del(self.__data)
101
+    # @brief Returns session
102
+    #@ returns the dict which stores session
99 103
 
100 104
     @classmethod
101
-    def datas(cls):
102
-        return cls._instance.__datas
103
-    
105
+    def data(cls):
106
+        return cls._instance.__data
107
+
108
+    # @brief Returns the user's information contained in the session's data
104 109
     @classmethod
105 110
     def user(cls):
106
-        if '__auth_user_infos' in cls._instance.__datas:
107
-            return cls._instance.__datas['__auth_user_infos']
111
+        if '__auth_user_infos' in cls._instance.__data:
112
+            return cls._instance.__data['__auth_user_infos']
108 113
         else:
109 114
             return None
115
+
116
+    # @brief Returns the session's token
110 117
     @classmethod
111 118
     def get_session_token(cls):
112 119
         return cls._instance.__session_token
113
-    
120
+
121
+    # @brief Set the session's token
122
+    #@param the value of the token
114 123
     @classmethod
115 124
     def set_session_token(cls, value):
116 125
         cls._instance.__session_token = value
117
-    
118
-    ##@brief Try to authenticate a user with a login and a password
126
+
127
+    # @brief Try to authenticate a user with a login and a password
119 128
     #@param login str : provided login
120 129
     #@param password str : provided password (hash)
121 130
     #@warning brokes composed UID
122
-    #@note implemets multiple login/password sources (useless ?)
131
+    #@note implements multiple login/password sources (useless ?)
123 132
     #@todo composed UID broken method
124 133
     #@todo allow to provide an authentication source
125 134
     @classmethod
126
-    def authenticate(self, login = None, password = None):
127
-        #Authenticate
135
+    def authenticate(self, login=None, password=None):
136
+        # Authenticate
128 137
         for infos in self._infos_fields:
129 138
             logger.debug(self._infos_fields)
130 139
             login_cls = infos['login'][0]
131 140
             pass_cls = infos['password'][0]
132 141
             qfilter = "{passfname} = {passhash}"
133
-            uid_fname = login_cls.uid_fieldname()[0] #COMPOSED UID BROKEN
142
+            uid_fname = login_cls.uid_fieldname()[0]  # COMPOSED UID BROKEN
134 143
             if login_cls == pass_cls:
135
-                #Same EmClass for login & pass
144
+                # Same EmClass for login & pass
136 145
                 qfilter = qfilter.format(
137
-                    passfname = infos['password'][1],
138
-                    passhash = password)
146
+                    passfname=infos['password'][1],
147
+                    passhash=password)
139 148
             else:
140
-                #Different EmClass, building a relational filter
149
+                # Different EmClass, building a relational filter
141 150
                 passfname = "%s.%s" % (infos['login'][2], infos['password'][1])
142 151
                 qfilter = qfilter.format(
143
-                    passfname = passfname,
144
-                    passhash = password)
152
+                    passfname=passfname,
153
+                    passhash=password)
145 154
             getq = LeGetQuery(infos['login'][0], qfilter,
146
-                field_list = [uid_fname], limit = 1)
155
+                              field_list=[uid_fname], limit=1)
147 156
             req = getq.execute()
148 157
             if len(req) == 1:
149
-                self.__set_authenticated(infos['login'][0],req[0][uid_fname])
158
+                self.__set_authenticated(infos['login'][0], req[0][uid_fname])
150 159
                 break
151 160
         if self.is_anonymous():
152
-            self.authentication_failure() #Security logging
153
-    
154
-    ##@brief Attempt to restore a session given a session token
161
+            self.authentication_failure()  # Security logging
162
+
163
+    # @brief Attempt to restore a session given a session token
155 164
     #@param token mixed : a session token
156
-    #@return Session datas (a dict)
165
+    #@return Session data (a dict)
157 166
     #@throw ClientAuthenticationFailure if token is not valid or not
158
-    #existing
167
+    # existing
159 168
     @classmethod
160 169
     def restore_session(cls, token):
161 170
         cls._assert_instance()
162 171
         if cls._instance.__session_token is not None:
163 172
             raise ClientAuthenticationError("Trying to restore a session, but \
164
-a session is allready started !!!")
173
+a session is already started !!!")
165 174
         try:
166
-            cls._instance.__datas = SessionHandler.restore(token)
175
+            cls._instance.__data = SessionHandler.restore(token)
167 176
             cls._instance.__session_token = token
168 177
         except ClientAuthenticationFailure:
169
-            logger.warning("Session restoring fails")
170
-        return copy.copy(cls._instance.datas)
171
-    
172
-    ##@brief Return the current session token or None
178
+            logger.warning("Session restoring failed")
179
+        return copy.copy(cls._instance.data)
180
+
181
+    # @brief Returns the current session token or None
173 182
     #@return A session token or None
174 183
     @classmethod
175 184
     def session_token(cls):
176 185
         cls._assert_instance()
177 186
         return cls._instance.__session_token
178 187
 
179
-   
180
-    ##@brief Delete current session
188
+    # @brief Deletes current session
181 189
     @classmethod
182 190
     def destroy(cls):
183 191
         cls._assert_instance()
184 192
         SessionHandler.destroy(cls._instance.__session_token)
185 193
         cls._instance.__session_token = None
186
-        cls._instance.__datas = dict()
187
-    
188
-    ##@brief Delete current client and save its session
194
+        cls._instance.__data = dict()
195
+
196
+    # @brief Deletes current client and saves its session
189 197
     @classmethod
190 198
     def clean(cls):
191 199
         if cls._instance.__session_token is not None:
192
-            SessionHandler.save(cls._instance.__session_token, cls._instance.__datas)
200
+            SessionHandler.save(cls._instance.__session_token, cls._instance.__data)
193 201
         if Client._instance is not None:
194 202
             del(Client._instance)
195 203
         Client._instance = None
196
-    
197
-    ##@brief Test wether a client is anonymous or logged in
204
+
205
+    # @brief Tests if a client is anonymous or logged in
198 206
     #@return True if client is anonymous
199 207
     @classmethod
200 208
     def is_anonymous(cls):
201 209
         return Client._instance.user() is None
202
-        
203
-    ##@brief Method to call on authentication failure
210
+
211
+    # @brief Method to be called on authentication failure
204 212
     #@throw ClientAuthenticationFailure
205
-    #@throw LodelFatalError if no Client child instance found
213
+    #@throw LodelFatalError if no Client child instance is found
206 214
     @classmethod
207 215
     def authentication_failure(cls):
208 216
         cls._generic_error(ClientAuthenticationFailure)
209
-    
210
-    ##@brief Method to call on authentication error
217
+
218
+    # @brief Method to be called on authentication error
211 219
     #@throw ClientAuthenticationError
212
-    #@throw LodelFatalError if no Client child instance found
220
+    #@throw LodelFatalError if no Client child instance is found
213 221
     @classmethod
214
-    def authentication_error(cls, msg = "Unknow error"):
222
+    def authentication_error(cls, msg="Unknow error"):
215 223
         cls._generic_error(ClientAuthenticationError, msg)
216 224
 
217
-    ##@brief Method to call on permission denied error
225
+    # @brief Method to be called on permission denied error
218 226
     #@throw ClientPermissionDenied
219
-    #@throw LodelFatalError if no Client child instance found
227
+    #@throw LodelFatalError if no Client child instance is found
220 228
     @classmethod
221
-    def permission_denied_error(cls, msg = ""):
229
+    def permission_denied_error(cls, msg=""):
222 230
         cls._generic_error(ClientPermissionDenied, msg)
223
-    
224
-    ##@brief Generic error method
231
+
232
+    # @brief Generic error method
225 233
     #@see Client::authentication_failure() Client::authentication_error()
226
-    #Client::permission_denied_error()
227
-    #@throw LodelFatalError if no Client child instance found
234
+    # Client::permission_denied_error()
235
+    #@throw LodelFatalError if no Client child instance is found
228 236
     @classmethod
229
-    def _generic_error(cls, expt, msg = ""):
237
+    def _generic_error(cls, expt, msg=""):
230 238
         cls._assert_instance()
231 239
         raise expt(Client._instance, msg)
232
-    
233
-    ##@brief Assert that an instance of Client child class exists
234
-    #@throw LodelFataError if no instance of Client child class found
240
+
241
+    # @brief Asserts that an instance of Client child class exists
242
+    #@throw LodelFataError if no instance of Client child class is found
235 243
     @classmethod
236 244
     def _assert_instance(cls):
237 245
         if Client._instance is None:
238 246
             raise LodelFatalError("No client instance found. Abording.")
239 247
 
240
-    ##@brief Class method that fetches conf
248
+    # @brief Class method that fetches conf
241 249
     #
242
-    #This method populates Client._infos_fields . This attribute stores
243
-    #informations on login and password location (LeApi object & field)
250
+    # This method populates Client._infos_fields . This attribute stores
251
+    # informations on login and password location (LeApi object & field)
244 252
     @classmethod
245 253
     def fetch_settings(cls):
246 254
         LodelContext.expose_dyncode(globals(), 'dyncode')
247 255
         if cls._infos_fields is None:
248 256
             cls._infos_fields = list()
249 257
         else:
250
-            #Allready fetched
258
+            # Already fetched
251 259
             return
252 260
         infos = (
253 261
             Settings.auth.login_classfield,
@@ -266,21 +274,21 @@ a session is allready started !!!")
266 274
                 if fdh.is_reference() and res_infos[1][0] in fdh.linked_classes():
267 275
                     link_field = fname
268 276
             if link_field is None:
269
-                #Unable to find link between login & password EmClasses
277
+                # Unable to find link between login & password EmClass
270 278
                 raise AuthenticationError("Unable to find a link between \
271 279
 login EmClass '%s' and password EmClass '%s'. Abording..." % (
272 280
                     res_infos[0][0], res_infos[1][0]))
273 281
         res_infos[0] = (res_infos[0][0], res_infos[0][1], link_field)
274 282
         cls._infos_fields.append(
275
-            {'login':res_infos[0], 'password':res_infos[1]})
283
+            {'login': res_infos[0], 'password': res_infos[1]})
276 284
 
277
-    ##@brief Set a user as authenticated and start a new session
285
+    # @brief Sets a user as authenticated and starts a new session
278 286
     #@param leo LeObject child class : the LeObject the user is stored in
279 287
     #@param uid str : uniq id (in leo)
280 288
     #@return None
281 289
     @classmethod
282 290
     def __set_authenticated(cls, leo, uid):
283 291
         cls._instance.__user = {'classname': leo.__name__, 'uid': uid, 'leoclass': leo}
284
-        #Store auth infos in session
285
-        cls._instance.__datas[cls._instance.__class__._AUTH_DATANAME] = copy.copy(cls._instance.__user)
286
-
292
+        # Store auth infos in session
293
+        cls._instance.__data[cls._instance.__class__._AUTH_DATANAME] = copy.copy(
294
+            cls._instance.__user)

+ 34
- 29
lodel/editorial_model/components.py View File

@@ -36,11 +36,13 @@ class EmComponent(MlNamedObject):
36 36
         self.group = group
37 37
         super().__init__(display_name, help_text)
38 38
 
39
+    # @brief Returns the display_name of the component if it is not None, its uid else
39 40
     def __str__(self):
40 41
         if self.display_name is None:
41 42
             return str(self.uid)
42 43
         return str(self.display_name)
43 44
 
45
+    # @brief Returns a hash code for the component
44 46
     def d_hash(self):
45 47
         m = hashlib.md5()
46 48
         for data in (
@@ -57,7 +59,7 @@ class EmComponent(MlNamedObject):
57 59
 #@ingroup lodel2_em
58 60
 class EmClass(EmComponent):
59 61
 
60
-    # @brief Instanciate a new EmClass
62
+    # @brief Instanciates a new EmClass
61 63
     #@param uid str : uniq identifier
62 64
     #@param display_name MlString|str|dict : component display_name
63 65
     #@param abstract bool : set the class as asbtract if True
@@ -78,7 +80,7 @@ class EmClass(EmComponent):
78 80
         self.pure_abstract = bool(pure_abstract)
79 81
         self.__datasource = datasources
80 82
         if not isinstance(datasources, str) and len(datasources) != 2:
81
-            raise ValueError("datasources arguement can be a single datasource\
83
+            raise ValueError("datasources argument can be a single datasource\
82 84
  name or two names in a tuple or a list")
83 85
         if self.pure_abstract:
84 86
             self.abtract = True
@@ -115,8 +117,9 @@ class EmClass(EmComponent):
115 117
                 internal=True,
116 118
                 group=group)
117 119
 
118
-    # @brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
119
-    # @todo use Settings.editorialmodel.groups to determine wich fields should be returned
120
+    # @brief Property that represents a dict of all fields 
121
+    # (the EmField objects defined in this class and all their parents)
122
+    # @todo use Settings.editorialmodel.groups to determine which fields should be returned
120 123
     @property
121 124
     def __all_fields(self):
122 125
         res = dict()
@@ -130,9 +133,9 @@ class EmClass(EmComponent):
130 133
     def datasource(self):
131 134
         return self.__datasource
132 135
 
133
-    # @brief Return the list of all dependencies
136
+    # @brief Returns the list of all dependencies
134 137
     #
135
-    # Reccursive parents listing
138
+    # Recursive parents listing
136 139
     @property
137 140
     def parents_recc(self):
138 141
         if len(self.parents) == 0:
@@ -155,7 +158,7 @@ class EmClass(EmComponent):
155 158
         except KeyError:
156 159
             raise EditorialModelError("No such EmField '%s'" % uid)
157 160
 
158
-    # @brief Keep in __fields only fields contained in active groups
161
+    # @brief Keeps in __fields only fields contained in active groups
159 162
     def _set_active_fields(self, active_groups):
160 163
         if not Settings.editorialmodel.editormode:
161 164
             active_fields = []
@@ -165,10 +168,10 @@ class EmClass(EmComponent):
165 168
             self.__fields = {fname: fdh for fname, fdh in self.__fields.items()
166 169
                              if fdh in active_fields}
167 170
 
168
-    # @brief Add a field to the EmClass
171
+    # @brief Adds a field to the EmClass
169 172
     # @param emfield EmField : an EmField instance
170
-    # @warning do not add an EmField allready in another class !
171
-    # @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents)
173
+    # @warning do not add an EmField already in another class !
174
+    # @throw EditorialModelException if an EmField with same uid already in this EmClass (overwriting allowed from parents)
172 175
     # @todo End the override checks (needs methods in data_handlers)
173 176
     def add_field(self, emfield):
174 177
         assert_edit()
@@ -180,11 +183,11 @@ class EmClass(EmComponent):
180 183
             parent_field = self.__all_fields[emfield.uid]
181 184
             if not emfield.data_handler_instance.can_override(parent_field.data_handler_instance):
182 185
                 raise AttributeError(
183
-                    "'%s' field override a parent field, but data_handles are not compatible" % emfield.uid)
186
+                    "'%s' field overrides a parent field, but data_handlers are not compatible" % emfield.uid)
184 187
         self.__fields[emfield.uid] = emfield
185 188
         return emfield
186 189
 
187
-    # @brief Create a new EmField and add it to the EmClass
190
+    # @brief Creates a new EmField and adds it to the EmClass
188 191
     # @param data_handler str : A DataHandler name
189 192
     # @param uid str : the EmField uniq id
190 193
     # @param **field_kwargs :  EmField constructor parameters ( see @ref EmField.__init__() )
@@ -221,7 +224,7 @@ class EmClass(EmComponent):
221 224
 #@ingroup lodel2_em
222 225
 class EmField(EmComponent):
223 226
 
224
-    # @brief Instanciate a new EmField
227
+    # @brief Instanciates a new EmField
225 228
     # @param uid str : uniq identifier
226 229
     # @param display_name MlString|str|dict : field display_name
227 230
     # @param data_handler str : A DataHandler name
@@ -256,7 +259,7 @@ class EmField(EmComponent):
256 259
     def get_data_handler_cls(self):
257 260
         return copy.copy(self.data_handler_cls)
258 261
 
259
-    ##@brief Returne the uid of the emclass which contains this field
262
+    ##@brief Returns the uid of the emclass which contains this field
260 263
     def get_emclass_uid(self):
261 264
         return self._emclass.uid
262 265
 
@@ -277,7 +280,7 @@ class EmField(EmComponent):
277 280
 
278 281
 class EmGroup(MlNamedObject):
279 282
 
280
-    # @brief Create a new EmGroup
283
+    # @brief Creates a new EmGroup
281 284
     # @note you should NEVER call the constructor yourself. Use Model.add_group instead
282 285
     # @param uid str : Uniq identifier
283 286
     # @param depends list : A list of EmGroup dependencies
@@ -297,10 +300,10 @@ class EmGroup(MlNamedObject):
297 300
             for grp in depends:
298 301
                 if not isinstance(grp, EmGroup):
299 302
                     raise ValueError("EmGroup expected in depends argument but %s found" % grp)
300
-                self.add_dependencie(grp)
303
+                self.add_dependency(grp)
301 304
 
302
-    # @brief Returns EmGroup dependencie
303
-    # @param recursive bool : if True return all dependencies and their dependencies
305
+    # @brief Returns EmGroup dependencies
306
+    # @param recursive bool : if True returns all dependencies and their own dependencies
304 307
     # @return a dict of EmGroup identified by uid
305 308
     def dependencies(self, recursive=False):
306 309
         res = copy.copy(self.require)
@@ -316,7 +319,7 @@ class EmGroup(MlNamedObject):
316 319
         return res
317 320
 
318 321
     # @brief Returns EmGroup applicants
319
-    # @param recursive bool : if True return all dependencies and their dependencies
322
+    # @param recursive bool : if True returns all dependencies and their dependencies
320 323
     # @returns a dict of EmGroup identified by uid
321 324
     def applicants(self, recursive=False):
322 325
         res = copy.copy(self.required_by)
@@ -337,7 +340,7 @@ class EmGroup(MlNamedObject):
337 340
         return (self.__components).copy()
338 341
 
339 342
     # @brief Returns EmGroup display_name
340
-    #  @param lang str | None : If None return default lang translation
343
+    #  @param lang str | None : If None returns default lang translation
341 344
     #  @returns None if display_name is None, a str for display_name else
342 345
     def get_display_name(self, lang=None):
343 346
         name = self.display_name
@@ -346,7 +349,7 @@ class EmGroup(MlNamedObject):
346 349
         return name.get(lang)
347 350
 
348 351
     # @brief Returns EmGroup help_text
349
-    #  @param lang str | None : If None return default lang translation
352
+    #  @param lang str | None : If None returns default lang translation
350 353
     #  @returns None if display_name is None, a str for display_name else
351 354
     def get_help_text(self, lang=None):
352 355
         help = self.help_text
@@ -354,7 +357,7 @@ class EmGroup(MlNamedObject):
354 357
             return None
355 358
         return help.get(lang)
356 359
 
357
-    # @brief Add components in a group
360
+    # @brief Adds components in a group
358 361
     # @param components list : EmComponent instances list
359 362
     def add_components(self, components):
360 363
         assert_edit()
@@ -369,20 +372,20 @@ class EmGroup(MlNamedObject):
369 372
                     "Expecting components to be a list of EmComponent, but %s found in the list" % type(component))
370 373
         self.__components |= set(components)
371 374
 
372
-    # @brief Add a dependencie
373
-    # @param em_group EmGroup|iterable : an EmGroup instance or list of instance
374
-    def add_dependencie(self, grp):
375
+    # @brief Add a dependency
376
+    # @param em_group EmGroup|iterable : an EmGroup instance or list of instances
377
+    def add_dependency(self, grp):
375 378
         assert_edit()
376 379
         try:
377 380
             for group in grp:
378
-                self.add_dependencie(group)
381
+                self.add_dependency(group)
379 382
             return
380 383
         except TypeError:
381 384
             pass
382 385
 
383 386
         if grp.uid in self.require:
384 387
             return
385
-        if self.__circular_dependencie(grp):
388
+        if self.__circular_dependency(grp):
386 389
             raise EditorialModelError("Circular dependencie detected, cannot add dependencie")
387 390
         self.require[grp.uid] = grp
388 391
         grp.required_by[self.uid] = self
@@ -406,9 +409,9 @@ class EmGroup(MlNamedObject):
406 409
         self.required_by[grp.uid] = grp
407 410
         grp.require[self.uid] = self
408 411
 
409
-    # @brief Search for circular dependencie
412
+    # @brief Search for circular dependency
410 413
     # @return True if circular dep found else False
411
-    def __circular_dependencie(self, new_dep):
414
+    def __circular_dependency(self, new_dep):
412 415
         return self.uid in new_dep.dependencies(True)
413 416
 
414 417
     # @brief Search for circular applicant
@@ -424,6 +427,8 @@ class EmGroup(MlNamedObject):
424 427
         else:
425 428
             return self.display_name.get()
426 429
 
430
+    # @brief Computes a d-hash code for the EmGroup
431
+    # @return a string
427 432
     def d_hash(self):
428 433
 
429 434
         payload = "%s%s%s" % (

+ 8
- 1
lodel/editorial_model/exceptions.py View File

@@ -1,13 +1,20 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
+## @package lodel.editorial_model.exceptions
4
+# This module contains the specific exceptions related to the EditorialModel Management.
5
+
6
+
7
+## @brief Raises an Editorial Model specific exception.
3 8
 class EditorialModelError(Exception):
4 9
     pass
5 10
 
6 11
 
12
+## @brief Tries to import the settings module.
13
+# @raise EditorialModelError
7 14
 def assert_edit():
8 15
     try:
9 16
         from lodel import Settings
10
-    except ImportError: #Very dirty, but don't know how to fix the tests
17
+    except ImportError:  # Very dirty, but don't know how to fix the tests
11 18
         return
12 19
     if not Settings.editorialmodel.editormode:
13 20
         raise EditorialModelError("EM is readonly : editormode is OFF")

+ 12
- 6
lodel/editorial_model/model.py View File

@@ -42,7 +42,8 @@ class EditorialModel(MlNamedObject):
42 42
         super().__init__(display_name, help_text)
43 43
 
44 44
     # @brief EmClass uids accessor
45
-    #@return a dict of emclasses
45
+    #@return a copy of the dict containing all emclasses of the model if uid is None
46
+    # else a copy the class with uid uid
46 47
     def all_classes(self, uid=None):
47 48
         if uid is None:
48 49
             return copy.copy(self.__classes)
@@ -52,6 +53,9 @@ class EditorialModel(MlNamedObject):
52 53
             except KeyError:
53 54
                 raise EditorialModelException("EmClass not found : '%s'" % uid)
54 55
 
56
+    # @brief EmClass uids accessor
57
+    #@return the dict containing all emclasses of the model if uid is None
58
+    # else the class with uid uid
55 59
     def all_classes_ref(self, uid=None):
56 60
         if uid is None:
57 61
             return self.__classes
@@ -62,12 +66,13 @@ class EditorialModel(MlNamedObject):
62 66
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
63 67
 
64 68
     # @brief active EmClass uids accessor
65
-    #@return a list of class uids
69
+    #@return a list of active class uids
66 70
     def active_classes_uids(self):
67 71
         return list(self.__active_classes.keys())
68 72
 
69 73
     # @brief EmGroups accessor
70
-    #@return a dict of groups
74
+    #@return a copy of the dict of the model's group if uid is None
75
+    # else a copy of the group with uniq id uid
71 76
     def all_groups(self, uid=None):
72 77
         if uid is None:
73 78
             return copy.copy(self.__groups)
@@ -78,7 +83,8 @@ class EditorialModel(MlNamedObject):
78 83
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
79 84
 
80 85
     # @brief EmGroups accessor
81
-    #@return a dict of groups
86
+    #@return the dict of the model's group if uid is None
87
+    # else the group with uniq id uid
82 88
     def all_groups_ref(self, uid=None):
83 89
         if uid is None:
84 90
             return self.__groups
@@ -89,7 +95,7 @@ class EditorialModel(MlNamedObject):
89 95
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
90 96
 
91 97
     # @brief active EmClass uids accessor
92
-    #@return a list of class uids
98
+    #@return a list of active group uids
93 99
     def active_groups_uids(self):
94 100
         return list(self.__active_groups.keys())
95 101
 
@@ -97,7 +103,7 @@ class EditorialModel(MlNamedObject):
97 103
     #@param uid None | str : give this argument to get a specific EmClass
98 104
     #@return if uid is given returns an EmClass else returns an EmClass
99 105
     # iterator
100
-    #@todo use Settings.editorialmodel.groups to determine wich classes should
106
+    #@todo use Settings.editorialmodel.groups to determine which classes should
101 107
     # be returned
102 108
     def classes(self, uid=None):
103 109
         try:

+ 2
- 2
lodel/editorial_model/translator/__init__.py View File

@@ -1,4 +1,4 @@
1 1
 ## @package lodel.editorial_model.translator Editorial model translators
2 2
 #
3
-# This package contains modules that provides a save and a load function able to load and save
4
-# lodel.editorial_model.model.EditorialModel
3
+# This package is dedicated to the translation of an EditorialModel as several formats like pickle files or XML files. \n
4
+# Each module provides save and load functions to read/write an lodel.editorial_model.model.EditorialModel object from and to a file.

+ 120
- 104
lodel/editorial_model/translator/xmlfile.py View File

@@ -11,63 +11,76 @@ LodelContext.expose_modules(globals(), {
11 11
         'EmGroup'],
12 12
     'lodel.utils.mlstring': ['MlString']})
13 13
 
14
-##@package lodel.editorial_model.translator.xmlfile Translator module designed
15
-#to load & save EM in XML
14
+## @package lodel.editorial_model.translator.xmlfile
15
+# This module is a translator toolkit between and editorial model and an XML file.
16 16
 #
17
-# Structure of a xml file which represents an editorial model:
17
+# The XML file representing an editorial is composed by several nodes.
18
+#
19
+# @par \<name\>
20
+# The name of the model. It matches with the <b><em>name</em></b> field of the <b><em>EditorialModel class</em></b>
21
+#
22
+# @par \<description\>
23
+# This is the description of a composed element. Inside this node, we can have as many child node as there are languages in which it is translated. \n
24
+# Each translation is notified by a node, using the following scheme :
18 25
 # <ul>
19
-# <li>\<name\>: name of the model, field <b><em>name</em></b>  in class <b><em>EditorialModel</em></b> 
20
-# <li>\<description\>: field <b><em>description</em></b>  of a composed element, one for each language translation named 
21
-#               <ul><li>\<fre\> for french, 
22
-#               <li>\<eng\> for english,
23
-#               <li>\<esp\> for spanish,
24
-#               <li>\<ger\> for german</ul>
25
-# <li>\<classes\>: set of all <b><em>EmClass</em></b> in the model \n
26
-#    for each classe: \n
27
-#       \<class\><ul>
28
-#       <li>\<uid\>the class's id
29
-#       <li>\<display_name\> The name of the class, field <b><em>display_name</em></b>  of the <b><em>EmClass</em></b> , in different languages if they're available :
30
-#               <ul><li>\<fre\> for french, 
31
-#               <li>\<eng\> for english,
32
-#               <li>\<esp\> for spanish,
33
-#               <li>\<ger> for german</ul>
34
-#       <li>\<help_text\> Short explanation of the class's purpose, in different languages, as above
35
-#       <li>\<abstract\> True or False, field <b><em>abstract</em></b> of the <b><em>EmClass</em></b> 
36
-#       <li>\<pure_abstract\> True or False, field <b><em>pure_bastract</em></b> of the <b><em>EmClass</em></b>
37
-#       <li>\<group\><b><em>uid</em></b> of the group of the field <b><em>group</em></b> of the <b><em>EmClass</em></b>
38
-#       <li>\<fields\>: set of all the <b><em>EmField</em></b> of the <b><em>EmClass</em></b>\n
39
-#         for each field: \n
40
-#           \<field\>
41
-#               <ul><li>\<uid\> uid of the <b><em>EmField</em></b>
42
-#               <li>\<display_name\> field <b><em>display_name</em></b>  of the <b><em>EmField</em></b>, in different languages, as above
43
-#               <li>\<help_text\> Short explanation of the class's purpose, in different languages, as above
44
-#               <li>\<group\><b><em>uid</em></b> of the group of the field <b><em>group</em></b> of the <b><em>EmClass</em></b>
45
-#               <li>\<datahandler_name\> field <b><em>datahandler_name</em></b> of the Emfield, the name of a datahandler
46
-#               <li>\<datahandler_options\>, a list of xml items, each of them named with an option name and contains its value</ul></ul>
47
-# <li>\<groups\>: set of all the groups <b><em>EmGroup</em></b> in the model\n
48
-#    for each group:\n
49
-#       <ul><li>\<uid\> uid of the <b><em>EmField</em></b>
50
-#       <li>\<display_name\> field <b><em>display_name</em></b>  of the <b><em>EmField</em></b>, in different languages, as above
51
-#       <li>\<help_text\> Short explanation of the class's purpose, in different languages, as above
52
-#       <li>\<requires\> all uids of the <b><em>EmGroups</em></b> required by this group and which are in the fields <b><em>require</em></b>
53
-#       <li>\<components\> Set of all components of the <b><em>EmGroups</em></b>, representation of the field <b><em>__components</em></b> \n
54
-#         this item is splitted in two parts :\
55
-#           <ul><li>\<emfields\> all the emfields with, for each of them:\n
56
-#               \<emfield\> \n
57
-#                  <ul><li> \<uid\> <b><em>uid</em></b> of the <b><em>EmField</em></b></ul>
58
-#           <li>\<emclasses\> all the emclasses with, for each of them:\n
59
-#               \<emclass\> \n
60
-#                   <ul><li> \<uid\> <b><em>uid</em></b> of the <b><em>EmClass</em></b></ul></ul></ul>
61
-
62
-
63
-
64
-
26
+#   <li><b>\<fre\></b> : french
27
+#   <li><b>\<eng\></b> : english
28
+#   <li><b>\<esp\></b> : spanish
29
+#   <li><b>\<ger\></b> : german
30
+# </ul>
31
+#
32
+# @par \<classes\>
33
+# This node contains a set of all the <b><em>EmClass</em></b> classes we can find in the model, each represented by a <b>\<class\></b> child node.
34
+#
35
+# @par \<class\>
36
+# It is the representation of a single <b><em>EmClass</em></b> class. It is contained in the <b><em>\<classes\></em></b> node. It contains the following child nodes :
37
+# <ul>
38
+# <li><b>\<uid\></b> : The identifier of the class.
39
+# <li><b>\<display_name\></b> : The class' name, given by the <b><em>display_name</em></b> field of the <b><em>EmClass</em></b> class. This node contains the same language child nodes as the \<description\> node.
40
+# <li><b>\<help_text\></b> : A short description of the purpose of this class, using the same child nodes for each language, as above.
41
+# <li><b>\<abstract\></b> : Boolean node, with True or False as values, corresponding to the field <b><em>abstract</em></b> of the <b><em>EmClass</em></b> object.
42
+# <li><b>\<abstract\></b> : Boolean node, with True or False as values, corresponding to the field <b><em>pure_abstract</em></b> of the <b><em>EmClass</em></b> object.
43
+# <li><b>\<group\></b> : The unique identifier of the group stored in the <b><em>group</em></b> field of the <b><em>EmClass</em></b> object.
44
+# <li><b>\<fields\></b> : A set of all the <b><em>EmField</em></b> fields attached to an <b></em>EmClass</em></b> class. Each of them is represented by a <b>\<field\></b> child node.
45
+# </ul>
46
+#
47
+# @par \<field\>
48
+# This node is the XML representation of an <b><em>EmField</em></b> class. It contains the following child nodes :
49
+# <ul>
50
+# <li><b>\<uid\></b> : The identifier of the field.
51
+# <li><b>\<display_name\></b> : Displayed name, in different languages (same child nodes as above), corresponding to the <b><em>display_name</em></b> property of the <b><em>EmField</em></b>.
52
+# <li><b>\<help_text\></b> : Short explanation of the purpose of the field, in different languages (one child node for each translation, see above).
53
+# <li><b>\<group\></b> : <b><em>uid</em></b> of the group of the field <b><em>group</em></b> in the <b><em>EmField</em></b>
54
+# <li><b>\<datahandler_name\></b> : The name of the datahandler attached to this field (corresponds to the field <b><em>datahandler_name</em></b> of the Emfield)
55
+# <li><b>\<datahandler_options\></b> : A list of xml items, each of them named with an option name and containing its value
56
+# </ul>
57
+#
58
+# @par \<groups\>
59
+# This node contains a set of all the groups in the model (represented by <b><em>EmGroup</em></b> objects) with a <b>\<group\></b> child node for each one.
60
+#
61
+# @par \<group\>
62
+# Represents a single group. This node contains the following child nodes :
63
+# <ul>
64
+# <li><b>\<uid\></b> : unique id of the <b><em>EmField</em></b>.
65
+# <li><b>\<display_name\></b> : Corresponds to the <b><em>display_name</em></b>  property of the <b><em>EmField</em></b>, in different languages (see above)
66
+# <li><b>\help_text\></b> : Short explanation of the group's purpose, in different languages (see above)
67
+# <li><b>\<requires\></b> : All the unique identifiers of the <b><em>EmGroups</em></b> required by this group and which are in the fields <b><em>require</em></b>.
68
+# <li><b>\<components\></b> : A set of all components of the <b><em>EmGroups</em></b>, representation of the field <b><em>__components</em></b>. This node is splitted in two parts :
69
+#     <ul>
70
+#         <li><b>\<emfields\></b> : all the emfields with, for each of them:\n
71
+#               <b>\<emfield\></b> \n
72
+#                  <b>\<uid\></b> : <b><em>uid</em></b> of the <b><em>EmField</em></b>
73
+#           <li><b>\<emclasses\></b> : all the emclasses with, for each of them:\n
74
+#               <b>\<emclass\></b> \n
75
+#                  <b>\<uid\></b> : <b><em>uid</em></b> of the <b><em>EmClass</em></b>
76
+#     </ul>
77
+# </ul>
65 78
 
66 79
 
67
-##@brief Saves a model in a xml file
80
+## @brief Saves a model in a XML file
68 81
 # @param model EditorialModel : the model to save
69
-# @param filename str|None : if None display on stdout else writes in the file filename
70
-
82
+# @param kwargs dict : additional options.
83
+#                     - filename str|None : if None display on stdout else writes in the file filename
71 84
 def save(model, **kwargs):
72 85
     Em = etree.Element("editorial_model")
73 86
     em_name = etree.SubElement(Em, 'name')
@@ -100,19 +113,19 @@ def save(model, **kwargs):
100 113
         outfile.close()
101 114
     
102 115
     
103
-##@brief Writes a representation of a MlString in xml
104
-# @param etree : the xml object
105
-# @param elem : the element which represents a MlString
106
-# @param mlstr : the mlstr to write
116
+## @brief Writes a representation of a MlString in XML
117
+# @param etree Element : the XML object
118
+# @param elem Element : the element which represents a MlString
119
+# @param mlstr MlString: the mlstr to write
107 120
 def write_mlstring_xml(etree, elem, mlstr):
108 121
     for lang in mlstr.values:
109 122
         ss_mlstr = etree.SubElement(elem,lang)
110 123
         ss_mlstr.text = mlstr.get(lang)
111 124
         
112
-##@brief Writes the definition of a datahandler in xml
125
+## @brief Writes the definition of a datahandler in xml
113 126
 # @param etree : the xml object
114
-# @param elem : the element which defines a datahandler
115
-# @param dhdl_name : the name of the datahandler
127
+# @param elem Element : the element which defines a datahandler
128
+# @param dhdl_name str : the name of the datahandler
116 129
 # @param kwargs : the options of the datahandler
117 130
 def write_datahandler_xml(etree, elem, dhdl_name, **kwargs):
118 131
     dhdl = etree.SubElement(elem,'datahandler_name')
@@ -138,15 +151,15 @@ def write_datahandler_xml(etree, elem, dhdl_name, **kwargs):
138 151
                     opt_val = str(argu)
139 152
         arg.text = opt_val
140 153
         
141
-##@brief Writes a representation in xml of a EmField
154
+## @brief Writes a representation in xml of a EmField
142 155
 # @param etree : the xml object
143
-# @param elem : the element for the EmField
144
-# @param uid : the uid of the EmField
145
-# @param name : the name of the field
146
-# @param help_text : explanations of the EmField
147
-# @param group_uid : the uid of a group, can be None
148
-# @datahandler_name
149
-# @**kwargs : options of the datahandler
156
+# @param elem Element: the element for the EmField
157
+# @param uid str : the uid of the EmField
158
+# @param name str : the name of the field
159
+# @param help_text MlString: explanations of the EmField
160
+# @param group str|None: the uid of a group, can be None
161
+# @param datahandler_name str: Name of the datahandler attached to the field
162
+# @param **kwargs dict : options of the datahandler
150 163
 def write_emfield_xml(etree, elem, uid, name, help_text, group, datahandler_name, **kwargs):
151 164
     emfield = etree.SubElement(elem,'field')
152 165
     emfield_uid = etree.SubElement(emfield, 'uid')
@@ -169,10 +182,12 @@ def write_emfield_xml(etree, elem, uid, name, help_text, group, datahandler_name
169 182
 
170 183
 ##@brief Writes a representation of a EmGroup in xml
171 184
 # @param etree : the xml object
172
-# @param elem : the element for the EmGroup
173
-# @param name  : the name of the group
174
-# @param help_text : explanations of the EmGroup
175
-# @param requires : a list of the group's uids whose this group depends
185
+# @param elem Element : the element for the EmGroup
186
+# @param uid str : the uid of the EmGroup
187
+# @param name str : the name of the group
188
+# @param help_text MlString : explanations of the EmGroup
189
+# @param requires list : a list of the group's uids whose this group depends
190
+# @param components list : a list of the EmComponent objects contained in the group
176 191
 def write_emgroup_xml(etree, elem, uid, name, help_text, requires, components):
177 192
     emgroup = etree.SubElement(elem, 'group')
178 193
     emgroup_uid = etree.SubElement(emgroup, 'uid')
@@ -204,15 +219,16 @@ def write_emgroup_xml(etree, elem, uid, name, help_text, requires, components):
204 219
             em_group_comp_cls_ins = etree.SubElement(emgroup_comp_cls, 'emclass')
205 220
             em_group_comp_cls_ins.text = component.uid
206 221
 
207
-##@brief Writes a representation of a EmClass in xml
208
-# @param etree : the xml object
209
-# @param elem : the element for the EmClass
210
-# @param name  : the name of the group
211
-# @param help_text : explanations of the EmClass
212
-# @param fields : a dict
213
-# @param parents : a list of EmClass uids
214
-# @param abstract : a boolean
215
-# @param pure_abstract : a boolean
222
+## @brief Writes a representation of a EmClass in XML
223
+# @param etree : the XML object
224
+# @param elem Element : the element for the EmClass
225
+# @param uid str : the unique identifier of the EmClass
226
+# @param name str : the name of the group
227
+# @param help_text MlString : explanations of the EmClass
228
+# @param fields dict : a dict representing all the fields of the class
229
+# @param parents list : a list of the EmClass uids of this class' parents
230
+# @param abstract bool : a boolean
231
+# @param pure_abstract bool : a boolean
216 232
 def write_emclass_xml(etree, elem, uid, name, help_text, group, fields, parents, abstract = False, pure_abstract = False):
217 233
     emclass = etree.SubElement(elem, 'class')
218 234
     emclass_uid  = etree.SubElement(emclass, 'uid')
@@ -244,11 +260,10 @@ def write_emclass_xml(etree, elem, uid, name, help_text, group, fields, parents,
244 260
     emclass_parents = etree.SubElement(emclass, 'parents')
245 261
     emclass_parents.text = ",".join(parents_list)
246 262
 
247
-##@brief Loads a model from a xml file
248
-# @param model EditorialModel : the model to load
263
+## @brief Loads a model from a XML file
264
+# @param filename str : The file from which the editorial model will be loaded
249 265
 # @return a new EditorialModel object
250 266
 def load(filename):
251
-
252 267
     Em = etree.parse(filename)
253 268
     emodel = Em.getroot()
254 269
     name = emodel.find('name')
@@ -270,9 +285,10 @@ def load(filename):
270 285
             grp = model.add_group(grp)
271 286
     return model
272 287
 
273
-##@brief Creates a EmClass from a xml description
274
-# @param elem : the element which represents the EmClass
275
-# @param model  : the model which will contain the new class
288
+
289
+## @brief Creates a EmClass from a xml description
290
+# @param model EditorialModel : the model which will contain the new class
291
+# @param elem Element: the element which represents the EmClass
276 292
 # @return a new EmClass object
277 293
 def load_class_xml(model, elem):
278 294
     uid = elem.find('uid').text
@@ -332,11 +348,11 @@ def load_class_xml(model, elem):
332 348
             
333 349
     return emclass
334 350
     
335
-##@brief Creates a EmField from a xml description
336
-#@param elem : the element which represents the EmField
337
-#@param model  : the model which will contain the new field
338
-#@param emclass EmClass : the EmClass of the field
339
-#@return a new EmField object
351
+## @brief Creates a EmField from a XML description
352
+# @param model EditorialModel: the model which will contain the new field
353
+# @param elem Element : the element which represents the EmField
354
+# @param emclass EmClass : the EmClass of the field
355
+# @return a new EmField object
340 356
 def load_field_xml(model, elem, emclass):
341 357
     uid = elem.find('uid').text
342 358
     if elem.find('display_name').text is None:
@@ -369,10 +385,11 @@ def load_field_xml(model, elem, emclass):
369 385
     
370 386
     return emfield
371 387
 
372
-##@brief Returns datahandler options from a xml description
373
-# @param elem : the element which represents the datahandler
374
-# @param model  : the model which will contain the new field
375
-# @return datahandler options
388
+
389
+## @brief Returns datahandler options from a XML description
390
+# @param elem Element : the element which represents the datahandler
391
+# @param model EditorialModel : the model which will contain the new field
392
+# @return dict
376 393
 def load_dhdl_options_xml(model, elem):
377 394
     dhdl_options=dict()
378 395
     for opt in elem:
@@ -396,10 +413,10 @@ def load_dhdl_options_xml(model, elem):
396 413
     return dhdl_options
397 414
              
398 415
     
399
-##@brief Creates a EmGroup from a xml description
400
-# @param elem : the element which represents the EmGroup
401
-# @param model  : the model which will contain the new group
402
-# @return a new EmGroup object
416
+## @brief Creates a EmGroup from a XML description
417
+# @param model EditorialModel : the model which will contain the new group
418
+# @param elem Element : the element which represents the EmGroup
419
+# @return EmGroup
403 420
 def load_group_xml(model, elem):
404 421
     uid = elem.find('uid')
405 422
     
@@ -443,17 +460,16 @@ def load_group_xml(model, elem):
443 460
         group = model.all_groups_ref(uid.text)
444 461
         group.display_name = name
445 462
         group.help_text = help_text
446
-        group.add_dependencie(requires)
463
+        group.add_dependency(requires)
447 464
     else:
448 465
         group = EmGroup(uid.text, requires, name, help_text)
449 466
 
450 467
     group.add_components(comp)     
451 468
     return group
452 469
 
453
-##@brief Constructs a MlString from a xml description
454
-# @param elem : the element which represents the MlString
455
-# @param model  : the model which will contain the new group
456
-# @return a new MlString object
470
+## @brief Constructs a MlString from a XML description
471
+# @param elem Element : the element which represents the MlString
472
+# @return MlString
457 473
 def load_mlstring_xml(elem):
458 474
     mlstr = dict()
459 475
     for lang in elem:

+ 75
- 24
lodel/leapi/datahandlers/datas.py View File

@@ -1,4 +1,9 @@
1 1
 #-*- coding: utf-8 -*-
2
+
3
+## @package lodel.leapi.datahandlers.datas
4
+# This module contains specific datahandlers extending the basic ones from the lodel.leapi.datahandlers.datas_base module.
5
+
6
+
2 7
 import warnings
3 8
 import inspect
4 9
 import re
@@ -12,22 +17,29 @@ LodelContext.expose_modules(globals(), {
12 17
                          'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
13 18
 
14 19
 
15
-##@brief Data field designed to handle formated strings
20
+## @brief Data field designed to handle formated strings
16 21
 class FormatString(Varchar):
17 22
 
18
-    help = 'Automatic string field, designed to use the str % operator to \
19
-build its content'
23
+    help = 'Automatic string field, designed to use the str % operator to build its content'
20 24
     base_type = 'char'
21 25
     
22
-    ##@brief Build its content with a field list and a format string
23
-    # @param format_string str
24
-    # @param field_list list : List of field to use
25
-    # @param **kwargs
26
+    ## @brief Constructor
27
+    # @param _field_list list : List of fields to use
28
+    # @param _format_string str : formatted string
29
+    # @param **kwargs : additional options
26 30
     def __init__(self, format_string, field_list, **kwargs):
27 31
         self._field_list = field_list
28 32
         self._format_string = format_string
29 33
         super().__init__(internal='automatic', **kwargs)
30 34
 
35
+    ## @brief constructs the formatted string data
36
+    # The string can be truncated depending on the maximum length defined for this field.
37
+    #
38
+    # @param emcomponent EmComponent
39
+    # @param fname str
40
+    # @param datas dict
41
+    # @param cur_value str
42
+    # @return str
31 43
     def _construct_data(self, emcomponent, fname, datas, cur_value):
32 44
         ret = self._format_string % tuple(
33 45
             datas[fname] for fname in self._field_list)
@@ -35,27 +47,28 @@ build its content'
35 47
             warnings.warn("Format field overflow. Truncating value")
36 48
             ret = ret[:self.max_length]
37 49
         return ret
38
-    
39
-##@brief Varchar validated by a regex
50
+
51
+
52
+## @brief Varchar validated by a regex
40 53
 class Regex(Varchar):
41 54
 
42 55
     help = 'String field validated with a regex. Takes two options : \
43 56
 max_length and regex'
44 57
     base_type = 'char'
45 58
 
46
-    ##@brief A string field validated by a regex
47
-    # @param regex str : a regex string (passed as argument to re.compile())
59
+    ## @brief A string field validated by a regex
60
+    # @param regex str : a regex string (passed as argument to re.compile()), default value is an empty string
48 61
     # @param max_length int : the max length for this field (default : 10)
49
-    # @param **kwargs
62
+    # @param **kwargs : additional options
50 63
     def __init__(self, regex='', max_length=10, **kwargs):
51 64
         self.regex = regex
52 65
         self.compiled_re = re.compile(regex)  # trigger an error if invalid regex
53 66
         super(self.__class__, self).__init__(max_length=max_length, **kwargs)
54 67
 
55
-    ##@brief Check and cast value in appropriate type
56
-    #@param value *
57
-    #@throw FieldValidationError if value is unappropriate or can not be cast 
58
-    #@return value
68
+    ## @brief Check and cast value in appropriate type
69
+    # @param value *
70
+    # @throw FieldValidationError if value is unappropriate or can not be cast
71
+    # @return str
59 72
     def _check_data_value(self, value):
60 73
         value = super()._check_data_value(value)
61 74
         if not self.compiled_re.match(value) or len(value) > self.max_length:
@@ -63,6 +76,10 @@ max_length and regex'
63 76
             raise FieldValidationError(msg)
64 77
         return value
65 78
 
79
+    ## @brief checks if another datahandler can override this one
80
+    #
81
+    # @param data_handler Datahandler
82
+    # @return bool
66 83
     def can_override(self, data_handler):
67 84
         if not super().can_override(data_handler):
68 85
             return False
@@ -71,36 +88,58 @@ max_length and regex'
71 88
             return False
72 89
         return True
73 90
 
91
+
74 92
 ##@brief Handles uniq ID
75 93
 class UniqID(Integer):
76 94
 
77 95
     help = 'Fieldtype designed to handle editorial model UID'
78 96
     base_type = 'int'
79 97
 
80
-    ##@brief A uid field
81
-    # @param **kwargs
98
+    ## @brief A uid field
99
+    #
100
+    # @param **kwargs dict
82 101
     def __init__(self, **kwargs):
83 102
         kwargs['internal'] = 'automatic'
84 103
         super(self.__class__, self).__init__(primary_key = True, **kwargs)
85 104
 
105
+    ## @brief Constructs the field's data
106
+    # @param emcomponent EmComponent : Component corresponding to the field
107
+    # @param fname
108
+    # @param datas
109
+    # @param cur_value str : current value to use (is retrieved from the datasource if not given)
110
+    # @return str
111
+    # @remarks fname and datas are not used and should become non mandatory, cur_value should have a None default value
86 112
     def construct_data(self, emcomponent, fname, datas, cur_value):
87 113
         if cur_value is None:
88 114
             #Ask datasource to provide a new uniqID
89 115
             return emcomponent._ro_datasource.new_numeric_id(emcomponent)
90 116
         return cur_value
91 117
 
118
+
119
+## @brief Class representing a LeObject subclass
92 120
 class LeobjectSubclassIdentifier(Varchar):
93 121
     
94 122
     help = 'Datahandler designed to handle LeObject subclass identifier in DB'
95 123
     base_type = 'varchar'
96 124
 
125
+    ## @brief Constructor
126
+    # @param kwargs dict : additional options
127
+    # @throw RuntimeError
128
+    # @todo define the "internal" option that can be given in the kwargs, and document its meaning
97 129
     def __init__(self, **kwargs):
98 130
         if 'internal' in kwargs and not kwargs['internal']:
99 131
             raise RuntimeError(self.__class__.__name__+" datahandler can only \
100 132
 be internal")
101 133
         kwargs['internal'] = True
102 134
         super().__init__(**kwargs)
103
-    
135
+
136
+    ## @brief Returns the class' name
137
+    # @param emcomponent EmComponent : Component correponding to the field
138
+    # @param fname
139
+    # @param datas
140
+    # @param cur_value
141
+    # @return str
142
+    # @remarks fname, datas and cur_value should be given default values as they are not mandatory here.
104 143
     def construct_data(self, emcomponent, fname, datas, cur_value):
105 144
         cls = emcomponent
106 145
         if not inspect.isclass(emcomponent):
@@ -108,13 +147,13 @@ be internal")
108 147
         return cls.__name__
109 148
 
110 149
 
111
-##@brief Data field designed to handle concatenated fields
150
+## @brief Data field designed to handle concatenated fields
112 151
 class Concat(FormatString):
113 152
     help = 'Automatic strings concatenation'
114 153
     base_type = 'char'
115 154
     
116
-    ##@brief Build its content with a field list and a separator
117
-    # @param field_list list : List of field to use
155
+    ## @brief Build its content with a field list and a separator
156
+    # @param field_list list : List of fields to concatenate
118 157
     # @param separator str
119 158
     # @param **kwargs    
120 159
     def __init__(self, field_list, separator=' ', **kwargs):
@@ -124,22 +163,34 @@ class Concat(FormatString):
124 163
                          **kwargs)
125 164
 
126 165
 
166
+## @brief Datahandler managing a password
127 167
 class Password(Varchar):
128 168
     help = 'Handle passwords'
129 169
     base_type = 'password'
130 170
     pass
131 171
 
132
-
172
+## @brief Datahandler turning a string into a list
133 173
 class VarcharList(Varchar):
134 174
     help = 'DataHandler designed to make a list out of a string.'
135 175
     base_type = 'varchar'
136
-    
176
+
177
+    ## @brief Constructor
178
+    # @param delimiter str : default value is a whitespace character
179
+    # @param **kwargs : additional options
180
+    # @throw LodelException : this exception is raised when the delimiter is not a string
137 181
     def __init__(self, delimiter=' ', **kwargs):
138 182
         if not isinstance(delimiter, str):
139 183
             raise LodelException("The delimiter has to be a string, %s given" % type(delimiter))
140 184
         self.delimiter = str(delimiter)
141 185
         super().__init__(**kwargs)
142 186
 
187
+    ## @brief Constructs the field's data
188
+    # @param emcomponent EmComponent
189
+    # @param fname
190
+    # @param datas
191
+    # @param cur_value : current value to use
192
+    # @return list
193
+    # @remarks emcomponent, fname and datas should be given a default value as they seem to be non mandatory
143 194
     def construct_data(self, emcomponent, fname, datas, cur_value):
144 195
         result = cur_value.split(self.delimiter)
145 196
         return result

+ 2
- 0
lodel/leapi/datahandlers/exceptions.py View File

@@ -1,3 +1,5 @@
1
+## @brief Exception classes for datahandlers
2
+
1 3
 class LodelDataHandlerException(Exception):
2 4
     pass
3 5
 

+ 6
- 2
lodel/leapi/datahandlers/references.py View File

@@ -9,12 +9,13 @@ LodelContext.expose_modules(globals(), {
9 9
                          'LodelFatalError', 'DataNoneValid',
10 10
                          'FieldValidationError']})
11 11
 
12
-
12
+## @brief Child class of SingleRef. The object referenced must exist
13 13
 class Link(SingleRef):
14 14
     pass
15 15
 
16 16
 
17 17
 ## @brief Child class of MultipleRef where references are represented in the form of a python list
18
+# All the objects referenced must exist
18 19
 class List(MultipleRef):
19 20
 
20 21
     ## @brief instanciates a list reference
@@ -97,13 +98,16 @@ class Map(MultipleRef):
97 98
 
98 99
 ## @brief This Reference class is designed to handler hierarchy with some constraint
99 100
 class Hierarch(MultipleRef):
100
-    
101
+
101 102
     directly_editable = False
102 103
 
103 104
     ## @brief Instanciate a data handler handling hierarchical relation with constraints
104 105
     # @param back_reference tuple : Here it is mandatory to have a back ref (like a parent field)
105 106
     # @param max_depth int | None :  limit of depth
106 107
     # @param max_childs int | Nine : maximum number of childs by nodes
108
+    # @param kwargs : 
109
+    #   - allowed_classes list | None : list of allowed em classes if None no restriction
110
+    #   - internal bool : if False, the field is not internal
107 111
     def __init__(self, back_reference, max_depth=None, max_childs=None, **kwargs):
108 112
         super().__init__(back_reference=back_reference,
109 113
                          max_depth=max_depth,

+ 5
- 4
lodel/leapi/exceptions.py View File

@@ -4,6 +4,7 @@ from lodel.context import LodelContext
4 4
 LodelContext.expose_modules(globals(), {
5 5
     'lodel.exceptions': ['LodelExceptions', 'LodelException']})
6 6
 
7
+##@brief Handles LeApi error
7 8
 class LeApiError(LodelException):
8 9
     pass
9 10
 
@@ -13,20 +14,20 @@ class LeApiErrors(LodelExceptions, LeApiError):
13 14
     pass
14 15
 
15 16
 
16
-##@brief When an error concerns a datas
17
+##@brief When an error concerns a data
17 18
 class LeApiDataCheckError(LeApiError):
18 19
     pass
19 20
 
20
-
21
+##@brief Handles LeApi data errors
21 22
 class LeApiDataCheckErrors(LodelExceptions, LeApiError):
22 23
     pass
23 24
 
24 25
 
26
+##@brief Handles leapi query errors
25 27
 class LeApiQueryError(LeApiError):
26 28
     pass
27 29
 
28 30
 
29
-##@brief Handles mulitple query errors
31
+##@brief Handles multiple query errors
30 32
 class LeApiQueryErrors(LodelExceptions, LeApiQueryError):
31 33
     pass
32
-

+ 83
- 64
lodel/leapi/lefactory.py View File

@@ -1,22 +1,25 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
-import os, os.path
3
+import os
4
+import os.path
4 5
 import functools
5 6
 
6 7
 from lodel.context import LodelContext
7 8
 LodelContext.expose_modules(globals(), {
8 9
     'lodel.editorial_model.components': ['EmComponent', 'EmClass', 'EmField',
9
-        'EmGroup'],
10
+                                         'EmGroup'],
10 11
     'lodel.leapi.leobject': ['LeObject'],
11 12
     'lodel.leapi.datahandlers.base_classes': ['DataHandler'],
12 13
     'lodel.logger': 'logger'})
13 14
 
14
-##@brief Generate python module code from a given model
15
+# @brief Generates python module code from a given model
15 16
 # @param model lodel.editorial_model.model.EditorialModel
17
+
18
+
16 19
 def dyncode_from_em(model):
17
-    
20
+
18 21
     # Generation of LeObject child classes code
19
-    cls_code, modules, bootstrap_instr = generate_classes(model)
22
+    cls_code, bootstrap_instr = generate_classes(model)
20 23
 
21 24
     # Header
22 25
     imports = """from lodel.context import LodelContext
@@ -25,10 +28,8 @@ LodelContext.expose_modules(globals(), {
25 28
     'lodel.leapi.datahandlers.base_classes': ['DataField'],
26 29
     'lodel.plugin.hooks': ['LodelHook']})
27 30
 """
28
-    for module in modules:
29
-        imports += "import %s\n" % module
30
-    
31
-    class_list = [ LeObject.name2objname(cls.uid) for cls in get_classes(model) ]
31
+    # generates the list of all classes in the editorial model
32
+    class_list = [LeObject.name2objname(cls.uid) for cls in get_classes(model)]
32 33
 
33 34
     # formating all components of output
34 35
     res_code = """#-*- coding: utf-8 -*-
@@ -41,17 +42,21 @@ dynclasses = {class_list}
41 42
 dynclasses_dict = {class_dict}
42 43
 {common_code}
43 44
 """.format(
44
-            imports = imports,
45
-            classes = cls_code,
46
-            bootstrap_instr = bootstrap_instr,
47
-            class_list = '[' + (', '.join([cls for cls in class_list]))+']',
48
-            class_dict = '{' + (', '.join([ "'%s': %s" % (cls, cls)
49
-                for cls in class_list]))+'}',
50
-            common_code = common_code(),
45
+        imports=imports,
46
+        classes=cls_code,
47
+        bootstrap_instr=bootstrap_instr,
48
+        class_list='[' + (', '.join([cls for cls in class_list])) + ']',
49
+        class_dict='{' + (', '.join(["'%s': %s" % (cls, cls)
50
+                                     for cls in class_list])) + '}',
51
+        common_code=common_code(),
51 52
     )
52 53
     return res_code
53 54
 
54
-##@brief Return the content of lodel.leapi.lefactory_common
55
+# @brief Returns the content of lodel.leapi.lefactory_common
56
+#
57
+# @return a string
58
+
59
+
55 60
 def common_code():
56 61
     res = ""
57 62
     fname = os.path.dirname(__file__)
@@ -61,23 +66,32 @@ def common_code():
61 66
             if not line.startswith('#-'):
62 67
                 res += line
63 68
     return res
64
-    
65 69
 
66
-##@brief return A list of EmClass sorted by dependencies
70
+
71
+# @brief return A list of EmClass sorted by dependencies
67 72
 #
68
-# The first elts in the list depends on nothing, etc.
73
+# The first elts in the list depend on nothing, etc.
74
+# @param a list of Emclass instances to be sorted
69 75
 # @return a list of EmClass instances
70 76
 def emclass_sorted_by_deps(emclass_list):
71 77
     def emclass_deps_cmp(cls_a, cls_b):
72 78
         return len(cls_a.parents_recc) - len(cls_b.parents_recc)
73
-    ret = sorted(emclass_list, key = functools.cmp_to_key(emclass_deps_cmp))
79
+    ret = sorted(emclass_list, key=functools.cmp_to_key(emclass_deps_cmp))
74 80
     return ret
75 81
 
76
-##@brief Returns a list of EmClass that will be represented as LeObject child classes
82
+# @brief Returns a list of EmClass instances that will be represented as LeObject child classes
83
+# @param model : an EditorialModel instance
84
+# @return a list of EmClass instances
85
+
86
+
77 87
 def get_classes(model):
78
-    return [ cls for cls in emclass_sorted_by_deps(model.classes()) if not cls.pure_abstract ]
88
+    return [cls for cls in emclass_sorted_by_deps(model.classes()) if not cls.pure_abstract]
89
+
90
+# @brief Given an EmField returns the data_handler constructor suitable for dynamic code
91
+# @param a EmField instance
92
+# @return a string
93
+
79 94
 
80
-##@brief Given an EmField returns the data_handler constructor suitable for dynamic code
81 95
 def data_handler_constructor(emfield):
82 96
     #dh_module_name = DataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
83 97
     get_handler_class_instr = 'DataField.from_name(%s)' % repr(emfield.data_handler_name)
@@ -85,60 +99,65 @@ def data_handler_constructor(emfield):
85 99
     for name, val in emfield.data_handler_options.items():
86 100
         if name == 'back_reference' and isinstance(val, tuple):
87 101
             options.append('{optname}: ({leo_name}, {fieldname})'.format(
88
-                optname = repr(name),
89
-                leo_name = LeObject.name2objname(val[0]),
90
-                fieldname = repr(val[1]),))
102
+                optname=repr(name),
103
+                leo_name=LeObject.name2objname(val[0]),
104
+                fieldname=repr(val[1]),))
91 105
         else:
92
-            options.append(repr(name)+': '+forge_optval(val))
106
+            options.append(repr(name) + ': ' + forge_optval(val))
93 107
 
94 108
     return '{handler_instr}(**{{ {options} }})'.format(
95
-                                                        handler_instr = get_handler_class_instr,
96
-                                                        options = ', '.join(options))
97
-            
98
-##@brief Return a python repr of option values
109
+        handler_instr=get_handler_class_instr,
110
+        options=', '.join(options))
111
+
112
+# @brief Return a python repr of option values
113
+# @param A value of any type which represents option
114
+# @return a string
115
+
116
+
99 117
 def forge_optval(optval):
100 118
     if isinstance(optval, dict):
101
-        return '{' + (', '.join( [ '%s: %s' % (repr(name), forge_optval(val)) for name, val in optval.items()])) + '}'
119
+        return '{' + (', '.join(['%s: %s' % (repr(name), forge_optval(val)) for name, val in optval.items()])) + '}'
102 120
 
103 121
     if isinstance(optval, (set, list, tuple)):
104 122
         return '[' + (', '.join([forge_optval(val) for val in optval])) + ']'
105
-        
123
+
106 124
     if isinstance(optval, EmField):
107 125
         return "{leobject}.data_handler({fieldname})".format(
108
-                leobject = LeObject.name2objname(optval._emclass.uid),
109
-                fieldname = repr(optval.uid)
110
-            )
111
-    elif isinstance(optval, EmClass):
126
+            leobject=LeObject.name2objname(optval._emclass.uid),
127
+            fieldname=repr(optval.uid)
128
+        )
129
+    if isinstance(optval, EmClass):
112 130
         return LeObject.name2objname(optval.uid)
113
-    else:
114
-        return repr(optval)
115 131
 
116
-##@brief Generate dyncode from an EmClass
117
-# @param model EditorialModel : 
118
-# @todo delete imports. It is never use, consequently changed return parameters.
132
+    return repr(optval)
133
+
134
+# @brief Generate dyncode from an EmClass
135
+# @param model EditorialModel :
119 136
 # @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
137
+
138
+
120 139
 def generate_classes(model):
121 140
     res = ""
122
-    imports = list()
141
+
123 142
     bootstrap = ""
124 143
     # Generating field list for LeObjects generated from EmClass
125 144
     for em_class in get_classes(model):
126 145
         logger.info("Generating a dynamic class for %s" % em_class.uid)
127
-        uid = list()        # List of fieldnames that are part of the EmClass primary key
128
-        parents = list()    # List of parents EmClass
129
-        # Determine pk
146
+        uid = list()        # List for fieldnames that are part of the EmClass primary key
147
+        parents = list()    # List for em_class's parents
148
+        # Determines primary key
130 149
         for field in em_class.fields():
131 150
             if field.data_handler_instance.is_primary_key():
132 151
                 uid.append(field.uid)
133
-        # Determine parent for inheritance
152
+        # Determines parentsfor inheritance
134 153
         if len(em_class.parents) > 0:
135 154
             for parent in em_class.parents:
136
-               parents.append(LeObject.name2objname(parent.uid))
155
+                parents.append(LeObject.name2objname(parent.uid))
137 156
         else:
138 157
             parents.append('LeObject')
139 158
         datasource_name = em_class.datasource
140
-        
141
-        # Dynamic code generation for LeObject childs classes
159
+
160
+        # Dynamic code generation for LeObject child classes
142 161
         em_cls_code = """
143 162
 class {clsname}({parents}):
144 163
     _abstract = {abstract}
@@ -150,12 +169,12 @@ class {clsname}({parents}):
150 169
     _child_classes = None
151 170
 
152 171
 """.format(
153
-    clsname = LeObject.name2objname(em_class.uid),
154
-    parents = ', '.join(parents),
155
-    abstract = 'True' if em_class.abstract else 'False',
156
-    uid_list = repr(uid),
157
-    datasource_name = repr(datasource_name),
158
-)
172
+            clsname=LeObject.name2objname(em_class.uid),
173
+            parents=', '.join(parents),
174
+            abstract='True' if em_class.abstract else 'False',
175
+            uid_list=repr(uid),
176
+            datasource_name=repr(datasource_name),
177
+        )
159 178
         res += em_cls_code
160 179
         # Dyncode fields bootstrap instructions
161 180
         child_classes = model.get_class_childs(em_class.uid)
@@ -163,14 +182,14 @@ class {clsname}({parents}):
163 182
             child_classes = 'tuple()'
164 183
         else:
165 184
             child_classes = '(%s,)' % (', '.join(
166
-                [ LeObject.name2objname(emcls.uid) for emcls in child_classes]))
185
+                [LeObject.name2objname(emcls.uid) for emcls in child_classes]))
167 186
         bootstrap += """{classname}._set__fields({fields})
168 187
 {classname}._child_classes = {child_classes}
169 188
 """.format(
170
-    classname = LeObject.name2objname(em_class.uid),
171
-    fields = '{' + (', '.join(['\n\t%s: %s' % (repr(emfield.uid),data_handler_constructor(emfield)) for emfield in em_class.fields()])) + '}',
172
-    child_classes = child_classes,
173
-)
189
+            classname=LeObject.name2objname(em_class.uid),
190
+            fields='{' + (', '.join(['\n\t%s: %s' % (repr(emfield.uid),
191
+                                                     data_handler_constructor(emfield)) for emfield in em_class.fields()])) + '}',
192
+            child_classes=child_classes,
193
+        )
174 194
     bootstrap += "\n"
175
-    return res, set(imports), bootstrap
176
-    
195
+    return res, bootstrap

+ 3
- 4
lodel/leapi/lefactory_common.py View File

@@ -5,7 +5,7 @@
5 5
 #- All lines that begins with #- will be deleted from dynamically generated
6 6
 #- code...
7 7
 
8
-##@brief Return a dynamically generated class given it's name
8
+##@brief Returns a dynamically generated class given its name
9 9
 #@param name str : The dynamic class name
10 10
 #@return False or a child class of LeObject
11 11
 def name2class(name):
@@ -14,7 +14,7 @@ def name2class(name):
14 14
     return dynclasses_dict[name]
15 15
 
16 16
 
17
-##@brief Return a dynamically generated class given it's name
17
+##@brief Returns a dynamically generated class given its name
18 18
 #@note Case insensitive version of name2class
19 19
 #@param name str
20 20
 #@return False or a child class of LeObject
@@ -26,11 +26,10 @@ def lowername2class(name):
26 26
     return new_dict[name]
27 27
 
28 28
 
29
-##@brief Trigger dynclasses datasources initialisation
29
+##@brief Triggers dynclasses datasources initialisation
30 30
 @LodelHook("lodel2_plugins_loaded")
31 31
 def lodel2_dyncode_datasources_init(self, caller, payload):
32 32
     for cls in dynclasses:
33 33
         cls._init_datasources()
34 34
     LodelContext.expose_modules(globals(), {'lodel.plugin.hooks': ['LodelHook']})
35 35
     LodelHook.call_hook("lodel2_dyncode_loaded", __name__, dynclasses)
36
-

+ 119
- 105
lodel/leapi/leobject.py View File

@@ -1,5 +1,9 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
+## @package lodel.leapi.leobject
4
+# This module is centered around the basic LeObject class, which is the main class for all the objects managed by lodel.
5
+
6
+
3 7
 import importlib
4 8
 import warnings
5 9
 import copy
@@ -22,20 +26,16 @@ LodelContext.expose_modules(globals(), {
22 26
     'lodel.plugin': ['Plugin', 'DatasourcePlugin'],
23 27
     'lodel.leapi.datahandlers.base_classes': ['DatasConstructor', 'Reference']})
24 28
 
25
-# @brief Stores the name of the field present in each LeObject that indicates
26
-# the name of LeObject subclass represented by this object
29
+## @brief Stores the name of the field present in each LeObject that indicates the name of LeObject subclass represented by this object
27 30
 CLASS_ID_FIELDNAME = "classname"
28 31
 
29
-# @brief Wrapper class for LeObject getter & setter
32
+
33
+## @brief Wrapper class for LeObject getter & setter
30 34
 #
31
-# This class intend to provide easy & friendly access to LeObject fields values
32
-# without name collision problems
35
+# This class intend to provide easy & friendly access to LeObject fields values without name collision problems
33 36
 # @note Wrapped methods are : LeObject.data() & LeObject.set_data()
34
-
35
-
36 37
 class LeObjectValues(object):
37 38
 
38
-    # @brief Construct a new LeObjectValues
39 39
     # @param fieldnames_callback method
40 40
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
41 41
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
@@ -43,47 +43,49 @@ class LeObjectValues(object):
43 43
         self._setter = set_callback
44 44
         self._getter = get_callback
45 45
 
46
-    # @brief Provide read access to datas values
46
+    ## @brief Provides read access to datas values
47 47
     # @note Read access should be provided for all fields
48 48
     # @param fname str : Field name
49
+    # @return method
49 50
     def __getattribute__(self, fname):
50 51
         getter = super().__getattribute__('_getter')
51 52
         return getter(fname)
52 53
 
53
-    # @brief Provide write access to datas values
54
+    ## @brief Provides write access to datas values
54 55
     # @note Write acces shouldn't be provided for internal or immutable fields
55 56
     # @param fname str : Field name
56 57
     # @param fval * : the field value
58
+    # @return method
57 59
     def __setattribute__(self, fname, fval):
58 60
         setter = super().__getattribute__('_setter')
59 61
         return setter(fname, fval)
60 62
 
61 63
 
64
+## @brief Represents a handled object in Lodel.
62 65
 class LeObject(object):
63 66
 
64
-    # @brief boolean that tells if an object is abtract or not
67
+    ## @brief boolean that tells if an object is abtract or not
65 68
     _abstract = None
66
-    # @brief A dict that stores DataHandler instances indexed by field name
69
+    ## @brief A dict that stores DataHandler instances indexed by field name
67 70
     _fields = None
68
-    # @brief A tuple of fieldname (or a uniq fieldname) representing uid
71
+    ## @brief A tuple of fieldname (or a uniq fieldname) representing uid
69 72
     _uid = None
70
-    # @brief Read only datasource ( see @ref lodel2_datasources )
73
+    ## @brief Read only datasource ( see @ref lodel2_datasources )
71 74
     _ro_datasource = None
72
-    # @brief Read & write datasource ( see @ref lodel2_datasources )
75
+    ## @brief Read & write datasource ( see @ref lodel2_datasources )
73 76
     _rw_datasource = None
74
-    # @brief Store the list of child classes
77
+    ## @brief Store the list of child classes
75 78
     _child_classes = None
76
-    # @brief Name of the datasource plugin
79
+    ## @brief Name of the datasource plugin
77 80
     _datasource_name = None
78 81
 
79 82
     def __new__(cls, **kwargs):
80
-
81 83
         self = object.__new__(cls)
82
-        # @brief A dict that stores fieldvalues indexed by fieldname
84
+        ## @brief A dict that stores fieldvalues indexed by fieldname
83 85
         self.__datas = {fname: None for fname in self._fields}
84
-        # @brief Store a list of initianilized fields when instanciation not complete else store True
86
+        ## @brief Store a list of initianilized fields when instanciation not complete else store True
85 87
         self.__initialized = list()
86
-        # @brief Datas accessor. Instance of @ref LeObjectValues
88
+        ## @brief Datas accessor. Instance of @ref LeObjectValues
87 89
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
88 90
         for fieldname, fieldval in kwargs.items():
89 91
             self.__datas[fieldname] = fieldval
@@ -92,8 +94,10 @@ class LeObject(object):
92 94
         self.__set_initialized()
93 95
         return self
94 96
 
95
-    # @brief Construct an object representing an Editorial component
96 97
     # @note Can be considered as EmClass instance
98
+    # @param **kwargs
99
+    # @throw NotImplementedError when the class being instanciated is noted as abstract and then should not be instanciated.
100
+    # @throw LeApiError in case of missing or invalid data.
97 101
     def __init__(self, **kwargs):
98 102
         if self._abstract:
99 103
             raise NotImplementedError(
@@ -130,19 +134,21 @@ class LeObject(object):
130 134
     #   Fields datas handling methods   #
131 135
     #-----------------------------------#
132 136
 
133
-    # @brief Property method True if LeObject is initialized else False
137
+    ## @brief Property method True if LeObject is initialized else False
138
+    # @return bool
134 139
     @property
135 140
     def initialized(self):
136 141
         return self.__is_initialized
137 142
 
138
-    # @return The uid field name
143
+    ## @brief Returns the uid field name
144
+    # @return str
139 145
     @classmethod
140 146
     def uid_fieldname(cls):
141 147
         return cls._uid
142 148
 
143
-    # @brief Return a list of fieldnames
144
-    # @param include_ro bool : if True include read only field names
145
-    # @return a list of str
149
+    ## @brief Returns a list of fieldnames
150
+    # @param include_ro bool : if True includes the read only field names
151
+    # @return list of string
146 152
     @classmethod
147 153
     def fieldnames(cls, include_ro=False):
148 154
         if not include_ro:
@@ -150,13 +156,17 @@ class LeObject(object):
150 156
         else:
151 157
             return list(cls._fields.keys())
152 158
 
159
+    ## @brief Returns a name, capitalizing the first character of each word
160
+    # @param name str
161
+    # @return str
153 162
     @classmethod
154 163
     def name2objname(cls, name):
155 164
         return name.title()
156 165
 
157
-    # @brief Return the datahandler asssociated with a LeObject field
158
-    # @param fieldname str : The fieldname
166
+    ## @brief Returns the datahandler asssociated with a LeObject field
167
+    # @param fieldname str : The field's name
159 168
     # @return A data handler instance
169
+    # @throw NameError when the given field name doesn't exist
160 170
     #@todo update class of exception raised
161 171
     @classmethod
162 172
     def data_handler(cls, fieldname):
@@ -164,9 +174,9 @@ class LeObject(object):
164 174
             raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
165 175
         return cls._fields[fieldname]
166 176
 
167
-    # @brief Getter for references datahandlers
168
-    #@param with_backref bool : if true return only references with back_references
169
-    #@return <code>{'fieldname': datahandler, ...}</code>
177
+    ## @brief Returns a dictionary containing the reference datahandlers
178
+    # @param with_backref bool : if true return only references with back_references
179
+    # @return dict : <code>{'fieldname': datahandler, ...}</code>
170 180
     @classmethod
171 181
     def reference_handlers(cls, with_backref=True):
172 182
         return {fname: fdh
@@ -174,11 +184,12 @@ class LeObject(object):
174 184
                 if fdh.is_reference() and
175 185
                 (not with_backref or fdh.back_reference is not None)}
176 186
 
177
-    # @brief Return a LeObject child class from a name
187
+    ## @brief Returns a LeObject child class from a name
178 188
     # @warning This method has to be called from dynamically generated LeObjects
179 189
     # @param leobject_name str : LeObject name
180 190
     # @return A LeObject child class
181
-    # @throw NameError if invalid name given
191
+    # @throw NotImplementedError if the method is abstract (if we use the LeObject class)
192
+    # @throw LeApiError if an unexisting name is given
182 193
     @classmethod
183 194
     def name2class(cls, leobject_name):
184 195
         if cls.__module__ == 'lodel.leapi.leobject':
@@ -189,14 +200,16 @@ class LeObject(object):
189 200
         except (AttributeError, TypeError):
190 201
             raise LeApiError("No LeObject named '%s'" % leobject_name)
191 202
 
203
+    ## @brief Checks if the class is abstract or not
204
+    # @return bool
192 205
     @classmethod
193 206
     def is_abstract(cls):
194 207
         return cls._abstract
195 208
 
196
-    # @brief Field data handler getter
197
-    #@param fieldname str : The field name
198
-    #@return A datahandler instance
199
-    #@throw NameError if the field doesn't exist
209
+    ## @brief Field data handler getter
210
+    # @param fieldname str : The field name
211
+    # @return A datahandler instance
212
+    # @throw NameError if the field doesn't exist
200 213
     @classmethod
201 214
     def field(cls, fieldname):
202 215
         try:
@@ -204,8 +217,9 @@ class LeObject(object):
204 217
         except KeyError:
205 218
             raise NameError("No field named '%s' in %s" % (fieldname,
206 219
                                                            cls.__name__))
207
-    # @return A dict with fieldname as key and datahandler as instance
208
-
220
+    ## @brief Returns the fields' datahandlers as a dictionary
221
+    # @param include_ro bool : if True, includes the read-only fields (default value : False)
222
+    # @return dict
209 223
     @classmethod
210 224
     def fields(cls, include_ro=False):
211 225
         if include_ro:
@@ -214,14 +228,12 @@ class LeObject(object):
214 228
             return {fname: cls._fields[fname] for fname in cls._fields\
215 229
                     if not cls._fields[fname].is_internal()}
216 230
 
217
-    # @brief Return the list of parents classes
231
+    ## @brief Return the list of parents classes
218 232
     #
219
-    #@note the first item of the list is the current class, the second is it's
220
-    # parent etc...
221
-    #@param cls
222
-    #@warning multiple inheritance broken by this method
223
-    #@return a list of LeObject child classes
224
-    #@todo multiple parent capabilities implementation
233
+    # @note the first item of the list is the current class, the second is its parent etc...
234
+    # @warning multiple inheritance broken by this method
235
+    # @return a list of LeObject child classes
236
+    # @todo multiple parent capabilities implementation
225 237
     @classmethod
226 238
     def hierarch(cls):
227 239
         res = [cls]
@@ -234,16 +246,15 @@ class LeObject(object):
234 246
                 res.append(cur)
235 247
         return res
236 248
 
237
-    # @brief Return a tuple a child classes
238
-    #@return a tuple of child classes
249
+    ## @brief Returns a tuple of child classes
250
+    # @return tuple
239 251
     @classmethod
240 252
     def child_classes(cls):
241 253
         return copy.copy(cls._child_classes)
242 254
 
243
-    # @brief Return the parent class that is the "source" of uid
255
+    ## @brief Returns the parent class that defines the unique id
244 256
     #
245
-    # The method goal is to return the parent class that defines UID.
246
-    #@return a LeObject child class or false if no UID defined
257
+    # @return a LeObject child class or false if no UID defined
247 258
     @classmethod
248 259
     def uid_source(cls):
249 260
         if cls._uid is None or len(cls._uid) == 0:
@@ -259,11 +270,11 @@ class LeObject(object):
259 270
             prev = pcls
260 271
         return prev
261 272
 
262
-    # @brief Initialise both datasources (ro and rw)
273
+    ## @brief Initialise both datasources (ro and rw)
263 274
     #
264 275
     # This method is used once at dyncode load to replace the datasource string
265 276
     # by a datasource instance to avoid doing this operation for each query
266
-    #@see LeObject::_init_datasource()
277
+    # @see LeObject::_init_datasource()
267 278
     @classmethod
268 279
     def _init_datasources(cls):
269 280
         if isinstance(cls._datasource_name, str):
@@ -291,16 +302,16 @@ class LeObject(object):
291 302
             log_msg %= (ro_ds, cls.__name__)
292 303
             logger.debug(log_msg)
293 304
 
294
-    # @brief Return the uid of the current LeObject instance
295
-    #@return the uid value
296
-    #@warning Broke multiple uid capabilities
305
+    ## @brief Returns the uid of the current LeObject instance
306
+    # @return str
307
+    # @warning Broke multiple uid capabilities
297 308
     def uid(self):
298 309
         return self.data(self._uid[0])
299 310
 
300
-    # @brief Read only access to all datas
311
+    ## @brief Returns the value of a field
301 312
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
302
-    # @param field_name str : field name
303
-    # @return the Value
313
+    # @param field_name str : field's name
314
+    # @return the value
304 315
     # @throw RuntimeError if the field is not initialized yet
305 316
     # @throw NameError if name is not an existing field name
306 317
     def data(self, field_name):
@@ -311,18 +322,19 @@ class LeObject(object):
311 322
                 "The field %s is not initialized yet (and have no value)" % field_name)
312 323
         return self.__datas[field_name]
313 324
 
314
-    # @brief Read only access to all datas
315
-    #@return a dict representing datas of current instance
325
+    ## @brief Returns a dictionary containing all the fields' values
326
+    # @return dict
316 327
     def datas(self, internal=False):
317 328
         return {fname: self.data(fname) for fname in self.fieldnames(internal)}
318 329
 
319
-    # @brief Datas setter
330
+    ## @brief Datas setter
320 331
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
321
-    # @param fname str : field name
332
+    # @param fname str : field's name
322 333
     # @param fval * : field value
323 334
     # @return the value that is really set
324 335
     # @throw NameError if fname is not valid
325 336
     # @throw AttributeError if the field is not writtable
337
+    # @throw LeApiErrors if the data check generates an error
326 338
     def set_data(self, fname, fval):
327 339
         if fname not in self.fieldnames(include_ro=False):
328 340
             if fname not in self._fields.keys():
@@ -353,18 +365,18 @@ class LeObject(object):
353 365
             else:
354 366
                 self.__datas[fname] = val
355 367
 
356
-    # @brief Update the __initialized attribute according to LeObject internal state
368
+    ## @brief Updates the __initialized attribute according to LeObject internal state
357 369
     #
358
-    # Check the list of initialized fields and set __initialized to True if all fields initialized
370
+    # Checks the list of initialized fields and sets __initialized at True if all fields initialized
359 371
     def __set_initialized(self):
360 372
         if isinstance(self.__initialized, list):
361 373
             expected_fields = self.fieldnames(include_ro=False) + self._uid
362 374
             if set(expected_fields) == set(self.__initialized):
363 375
                 self.__is_initialized = True
364 376
 
365
-    # @brief Designed to be called when datas are modified
377
+    ## @brief Designed to be called when datas are modified
366 378
     #
367
-    # Make different checks on the LeObject given it's state (fully initialized or not)
379
+    # Makes different checks on the LeObject given it's state (fully initialized or not)
368 380
     # @return None if checks succeded else return an exception list
369 381
     def __check_modified_values(self):
370 382
         err_list = dict()
@@ -409,24 +421,24 @@ class LeObject(object):
409 421
     #   Other methods    #
410 422
     #--------------------#
411 423
 
412
-    # @brief Temporary method to set private fields attribute at dynamic code generation
424
+    ## @brief Temporary method to set private fields attribute at dynamic code generation
413 425
     #
414 426
     # This method is used in the generated dynamic code to set the _fields attribute
415 427
     # at the end of the dyncode parse
416 428
     # @warning This method is deleted once the dynamic code loaded
417
-    # @param field_list list : list of EmField instance
418 429
     # @param cls
430
+    # @param field_list list : list of EmField instance
419 431
     @classmethod
420 432
     def _set__fields(cls, field_list):
421 433
         cls._fields = field_list
422 434
 
423
-    # @brief Check that datas are valid for this type
435
+    ## @brief Checks if the data is valid for this type
424 436
     # @param datas dict : key == field name value are field values
425
-    # @param complete bool : if True expect that datas provide values for all non internal fields
426
-    # @param allow_internal bool : if True don't raise an error if a field is internal
437
+    # @param complete bool : if True expects that values are provided for all non internal fields
438
+    # @param allow_internal bool : if True does not raise an error if a field is internal
427 439
     # @param cls
428 440
     # @return Checked datas
429
-    # @throw LeApiDataCheckError if errors reported during check
441
+    # @throw LeApiDataCheckErrors if errors are reported during check
430 442
     @classmethod
431 443
     def check_datas_value(cls, datas, complete=False, allow_internal=True):
432 444
         err_l = dict()  # Error storing
@@ -459,14 +471,13 @@ class LeObject(object):
459 471
             raise LeApiDataCheckErrors("Error while checking datas", err_l)
460 472
         return checked_datas
461 473
 
462
-    # @brief Check and prepare datas
474
+    ## @brief Checks and prepares all the data
463 475
     #
464 476
     # @warning when complete = False we are not able to make construct_datas() and _check_data_consistency()
465 477
     #
466 478
     # @param datas dict : {fieldname : fieldvalue, ...}
467
-    # @param complete bool : If True you MUST give all the datas
468
-    # @param allow_internal : Wether or not interal fields are expected in datas
469
-    # @param cls
479
+    # @param complete bool : If True you MUST give all the datas (default value : False)
480
+    # @param allow_internal : Wether or not interal fields are expected in datas (default value : True)
470 481
     # @return Datas ready for use
471 482
     # @todo: complete is very unsafe, find a way to get rid of it
472 483
     @classmethod
@@ -482,9 +493,8 @@ construction and consitency when datas are not complete\n")
482 493
             cls._check_datas_consistency(ret_datas)
483 494
         return ret_datas
484 495
 
485
-    # @brief Construct datas values
496
+    ## @brief Constructs datas values
486 497
     #
487
-    # @param cls
488 498
     # @param datas dict : Datas that have been returned by LeCrud.check_datas_value() methods
489 499
     # @return A new dict of datas
490 500
     # @todo IMPLEMENTATION
@@ -498,12 +508,12 @@ construction and consitency when datas are not complete\n")
498 508
         }
499 509
         return ret
500 510
 
501
-    # @brief Check datas consistency
511
+    ## @brief Checks datas consistency
502 512
503 513
     # @warning assert that datas is complete
504 514
     # @param cls
505 515
     # @param datas dict : Datas that have been returned by LeCrud._construct_datas() method
506
-    # @throw LeApiDataCheckError if fails
516
+    # @throw LeApiDataCheckError in case of failure
507 517
     @classmethod
508 518
     def _check_datas_consistency(cls, datas):
509 519
         err_l = []
@@ -516,27 +526,28 @@ construction and consitency when datas are not complete\n")
516 526
         if len(err_l) > 0:
517 527
             raise LeApiDataCheckError("Datas consistency checks fails", err_l)
518 528
 
519
-    # @brief Check datas consistency
529
+    ## @brief Checks data consistency
520 530
521 531
     # @warning assert that datas is complete
522
-    # @param cls
523
-    # @param datas dict : Datas that have been returned by LeCrud.prepare_datas() method
532
+    # @param datas dict : Data that have been returned by prepare_datas() method
524 533
     # @param type_query str : Type of query to be performed , default value : insert
525 534
     @classmethod
526 535
     def make_consistency(cls, datas, type_query='insert'):
527 536
         for fname, dh in cls._fields.items():
528 537
             ret = dh.make_consistency(fname, datas, type_query)
529 538
 
530
-    # @brief Add a new instance of LeObject
531
-    # @return a new uid en case of success, False otherwise
539
+    ## @brief Adds a new instance of LeObject
540
+    # @param datas dict : LeObject's data
541
+    # @return a new uid in case of success, False otherwise
532 542
     @classmethod
533 543
     def insert(cls, datas):
534 544
         query = LeInsertQuery(cls)
535 545
         return query.execute(datas)
536 546
 
537
-    # @brief Update an instance of LeObject
547
+    ## @brief Update an instance of LeObject
538 548
     #
539
-    #@param datas : list of new datas
549
+    # @param datas : list of new datas
550
+    # @return LeObject
540 551
     def update(self, datas=None):
541 552
         datas = self.datas(internal=False) if datas is None else datas
542 553
         uids = self._uid
@@ -555,9 +566,9 @@ construction and consitency when datas are not complete\n")
555 566
 
556 567
         return result
557 568
 
558
-    # @brief Delete an instance of LeObject
569
+    ## @brief Delete an instance of LeObject
559 570
     #
560
-    #@return 1 if the objet has been deleted
571
+    # @return 1 if the objet has been deleted
561 572
     def delete(self):
562 573
         uids = self._uid
563 574
         query_filter = list()
@@ -570,9 +581,9 @@ construction and consitency when datas are not complete\n")
570 581
 
571 582
         return result
572 583
 
573
-    # @brief Delete instances of LeObject
574
-    #@param query_filters list
575
-    #@returns the number of deleted items
584
+    ## @brief Deletes instances of LeObject
585
+    # @param query_filters list
586
+    # @return the number of deleted items
576 587
     @classmethod
577 588
     def delete_bundle(cls, query_filters):
578 589
         deleted = 0
@@ -589,16 +600,16 @@ construction and consitency when datas are not complete\n")
589 600
             deleted += result
590 601
         return deleted
591 602
 
592
-    # @brief Get instances of LeObject
603
+    ## @brief Gets instances of LeObject
593 604
     #
594
-    #@param query_filters dict : (filters, relational filters), with filters is a list of tuples : (FIELD, OPERATOR, VALUE) )
595
-    #@param field_list list|None : list of string representing fields see
596
-    #@ref leobject_filters
597
-    #@param order list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
598
-    #@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
599
-    #@param limit int : The maximum number of returned results
600
-    #@param offset int : offset
601
-    #@return a list of items (lists of (fieldname, fieldvalue))
605
+    # @param query_filters dict : (filters, relational filters), with filters is a list of tuples : (FIELD, OPERATOR, VALUE) )
606
+    # @param field_list list|None : list of string representing fields see
607
+    # @ref leobject_filters
608
+    # @param order list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
609
+    # @param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
610
+    # @param limit int : The maximum number of returned results
611
+    # @param offset int : offset (default value : 0)
612
+    # @return a list of items (lists of (fieldname, fieldvalue))
602 613
     @classmethod
603 614
     def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
604 615
         if field_list is not None:
@@ -628,7 +639,10 @@ construction and consitency when datas are not complete\n")
628 639
 
629 640
         return objects
630 641
 
631
-    # @brief Retrieve an object given an UID
642
+    ## @brief Retrieves an object given an UID
643
+    # @param uid str : Unique ID of the searched LeObject
644
+    # @return LeObject
645
+    # @throw LodelFatalError if the class does not have such a UID defined or if duplicates are found
632 646
     #@todo broken multiple UID
633 647
     @classmethod
634 648
     def get_from_uid(cls, uid):
@@ -645,7 +659,7 @@ construction and consitency when datas are not complete\n")
645 659
             while len(res_cp) > 0:
646 660
                 cur_res = res_cp.pop()
647 661
                 if cur_res.uid() in [r.uid() for r in res_cp]:
648
-                    logger.error("DOUBLON detected in query results !!!")
662
+                    logger.error("Duplicates detected in query results !!!")
649 663
                 else:
650 664
                     res.append(cur_res)
651 665
         if len(res) > 1:

+ 194
- 188
lodel/leapi/query.py View File

@@ -8,20 +8,22 @@ import warnings
8 8
 from lodel.context import LodelContext
9 9
 LodelContext.expose_modules(globals(), {
10 10
     'lodel.leapi.exceptions': ['LeApiError', 'LeApiErrors',
11
-        'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError',
12
-        'LeApiQueryErrors'],
11
+                               'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError',
12
+                               'LeApiQueryErrors'],
13 13
     'lodel.plugin.hooks': ['LodelHook'],
14 14
     'lodel.logger': ['logger']})
15 15
 
16
-##@todo check datas when running query
16
+# @todo check data when running query
17
+
18
+
17 19
 class LeQuery(object):
18 20
 
19
-    ##@brief Hookname prefix
21
+    # @brief Hookname prefix
20 22
     _hook_prefix = None
21
-    ##@brief arguments for the LeObject.check_data_value()
23
+    # @brief arguments for the LeObject.check_data_value()
22 24
     _data_check_args = {'complete': False, 'allow_internal': False}
23 25
 
24
-    ##@brief Abstract constructor
26
+    # @brief Abstract constructor
25 27
     # @param target_class LeObject : class of object the query is about
26 28
     def __init__(self, target_class):
27 29
         from .leobject import LeObject
@@ -29,77 +31,80 @@ class LeQuery(object):
29 31
             raise NotImplementedError("Abstract class")
30 32
         if not inspect.isclass(target_class) or \
31 33
            not issubclass(target_class, LeObject):
32
-            raise TypeError("target class has to be a child class of LeObject but %s given"% target_class)
34
+            raise TypeError(
35
+                "target class has to be a child class of LeObject but %s given" % target_class)
33 36
         self._target_class = target_class
34 37
         self._ro_datasource = target_class._ro_datasource
35 38
         self._rw_datasource = target_class._rw_datasource
36 39
 
37
-    ##@brief Execute a query and return the result
38
-    #@param **datas
40
+    # @brief Executes a query and returns the result
41
+    #@param **data
39 42
     #@return the query result
40 43
     #@see LeQuery._query()
41 44
     #@todo check that the check_datas_value is not duplicated/useless
42
-    def execute(self, datas):
43
-        if not datas is None:
45
+    def execute(self, data):
46
+        if data is not None:
44 47
             self._target_class.check_datas_value(
45
-                                                    datas,
46
-                                                    **self._data_check_args)
47
-            self._target_class.prepare_datas(datas) #not yet implemented
48
+                data,
49
+                **self._data_check_args)
50
+            self._target_class.prepare_datas(data)  # not yet implemented
48 51
         if self._hook_prefix is None:
49 52
             raise NotImplementedError("Abstract method")
50
-        LodelHook.call_hook(self._hook_prefix+'pre',
51
-                                self._target_class,
52
-                                datas)
53
-        ret = self._query(datas=datas)
54
-        ret = LodelHook.call_hook(self._hook_prefix+'post',
55
-                                    self._target_class,
56
-                                    ret)
53
+        LodelHook.call_hook(self._hook_prefix + 'pre',
54
+                            self._target_class,
55
+                            data)
56
+        ret = self._query(data=data)
57
+        ret = LodelHook.call_hook(self._hook_prefix + 'post',
58
+                                  self._target_class,
59
+                                  ret)
57 60
         return ret
58 61
 
59
-    ##@brief Childs classes implements this method to execute the query
60
-    #@param **datas
62
+    # @brief Child classes implement this method to execute the query
63
+    #@param **data
61 64
     #@return query result
62
-    def _query(self, **datas):
65
+    def _query(self, **data):
63 66
         raise NotImplementedError("Asbtract method")
64 67
 
65
-    ##@return a dict with query infos
68
+    # @return a dict with query infos
66 69
     def dump_infos(self):
67 70
         return {'target_class': self._target_class}
68 71
 
69 72
     def __repr__(self):
70 73
         ret = "<{classname} target={target_class}>"
71 74
         return ret.format(
72
-                            classname=self.__class__.__name__,
73
-                            target_class = self._target_class)
75
+            classname=self.__class__.__name__,
76
+            target_class=self._target_class)
77
+
78
+# @brief Abstract class handling query with filters
79
+
74 80
 
75
-##@brief Abstract class handling query with filters
76 81
 class LeFilteredQuery(LeQuery):
77
-    ##@brief The available operators used in query definitions
82
+    # @brief The available operators used in query definitions
78 83
     _query_operators = [
79
-                        ' = ',
80
-                        ' <= ',
81
-                        ' >= ',
82
-                        ' != ',
83
-                        ' < ',
84
-                        ' > ',
85
-                        ' in ',
86
-                        ' not in ',
87
-                        ' like ',
88
-                        ' not like ']
89
-
90
-    ##@brief Regular expression to process filters
84
+        ' = ',
85
+        ' <= ',
86
+        ' >= ',
87
+        ' != ',
88
+        ' < ',
89
+        ' > ',
90
+        ' in ',
91
+        ' not in ',
92
+        ' like ',
93
+        ' not like ']
94
+
95
+    # @brief Regular expression to process filters
91 96
     _query_re = None
92 97
 
93
-    ##@brief Abtract constructor for queries with filter
98
+    # @brief Abtract constructor for queries with filter
94 99
     #@param target_class LeObject : class of object the query is about
95 100
     #@param query_filters list : with a tuple (only one filter) or a list of
96 101
     # tuple or a dict: {OP,list(filters)} with OP = 'OR' or 'AND for tuple
97 102
     # (FIELD,OPERATOR,VALUE)
98 103
     def __init__(self, target_class, query_filters=None):
99 104
         super().__init__(target_class)
100
-        ##@brief The query filter tuple(std_filter, relational_filters)
105
+        # @brief The query filter tuple(std_filter, relational_filters)
101 106
         self._query_filter = None
102
-        ##@brief Stores potential subqueries (used when a query implies
107
+        # @brief Stores potential subqueries (used when a query implies
103 108
         # more than one datasource.
104 109
         #
105 110
         # Subqueries are tuple(target_class_ref_field, LeGetQuery)
@@ -107,11 +112,11 @@ class LeFilteredQuery(LeQuery):
107 112
         query_filters = [] if query_filters is None else query_filters
108 113
         self.set_query_filter(query_filters)
109 114
 
110
-    ##@brief Abstract FilteredQuery execution method
115
+    # @brief Abstract FilteredQuery execution method
111 116
     #
112 117
     # This method takes care to execute subqueries before calling super execute
113
-    def execute(self, datas=None):
114
-        #copy originals filters
118
+    def execute(self, data=None):
119
+        # copy originals filters
115 120
         orig_filters = copy.copy(self._query_filter)
116 121
         std_filters, rel_filters = self._query_filter
117 122
 
@@ -123,17 +128,17 @@ class LeFilteredQuery(LeQuery):
123 128
         try:
124 129
 
125 130
             filters, rel_filters = self._query_filter
126
-            res = super().execute(datas)
131
+            res = super().execute(data)
127 132
         except Exception as e:
128
-            #restoring filters even if an exception is raised
133
+            # restoring filters even if an exception is raised
129 134
             self.__query_filter = orig_filters
130 135
 
131
-            raise e #reraise
132
-        #restoring filters
136
+            raise e  # reraise
137
+        # restoring filters
133 138
         self._query_filter = orig_filters
134 139
         return res
135 140
 
136
-    ##@brief Add filter(s) to the query
141
+    # @brief Add filter(s) to the query
137 142
     #
138 143
     # This method is also able to slice query if different datasources are
139 144
     # implied in the request
@@ -144,39 +149,39 @@ class LeFilteredQuery(LeQuery):
144 149
     def set_query_filter(self, query_filter):
145 150
         if isinstance(query_filter, str):
146 151
             query_filter = [query_filter]
147
-        #Query filter prepration
152
+        # Query filter prepration
148 153
         filters_orig, rel_filters = self._prepare_filters(query_filter)
149 154
         # Here we now that each relational filter concern only one datasource
150 155
         # thank's to _prepare_relational_fields
151 156
 
152
-        #Multiple datasources detection
157
+        # Multiple datasources detection
153 158
         self_ds_name = self._target_class._datasource_name
154
-        result_rel_filters = list() # The filters that will stay in the query
159
+        result_rel_filters = list()  # The filters that will stay in the query
155 160
         other_ds_filters = dict()
156 161
         for rfilter in rel_filters:
157 162
             (rfield, ref_dict), op, value = rfilter
158
-            #rfield : the field in self._target_class
159
-            tmp_rel_filter = dict() #designed to stores rel_field of same DS
163
+            # rfield : the field in self._target_class
164
+            tmp_rel_filter = dict()  # designed to stores rel_field of same DS
160 165
             # First step : simplification
161 166
             # Trying to delete relational filters done on referenced class uid
162 167
             for tclass, tfield in copy.copy(ref_dict).items():
163
-                #tclass : reference target class
164
-                #tfield : referenced field from target class
168
+                # tclass : reference target class
169
+                # tfield : referenced field from target class
165 170
                 #
166 171
                 #   !!!WARNING!!!
167 172
                 # The line below brake multi UID support
168 173
                 #
169 174
                 if tfield == tclass.uid_fieldname()[0]:
170
-                    #This relational filter can be simplified as
175
+                    # This relational filter can be simplified as
171 176
                     # ref_field, op, value
172 177
                     # Note : we will have to dedup filters_orig
173 178
                     filters_orig.append((rfield, op, value))
174 179
                     del(ref_dict[tclass])
175 180
             if len(ref_dict) == 0:
176 181
                 continue
177
-            #Determine what to do with other relational filters given
182
+            # Determine what to do with other relational filters given
178 183
             # referenced class datasource
179
-            #Remember : each class in a relational filter has the same
184
+            # Remember : each class in a relational filter has the same
180 185
             # datasource
181 186
             tclass = list(ref_dict.keys())[0]
182 187
             cur_ds = tclass._datasource_name
@@ -189,23 +194,23 @@ class LeFilteredQuery(LeQuery):
189 194
                     other_ds_filters[cur_ds] = list()
190 195
                 other_ds_filters[cur_ds].append(
191 196
                     ((rfield, ref_dict), op, value))
192
-        #deduplication of std filters
197
+        # deduplication of std filters
193 198
         filters_cp = set()
194 199
         if not isinstance(filters_orig, set):
195 200
             for i, cfilt in enumerate(filters_orig):
196 201
                 a, b, c = cfilt
197
-                if isinstance(c, list): #list are not hashable
202
+                if isinstance(c, list):  # list are not hashable
198 203
                     newc = tuple(c)
199 204
                 else:
200 205
                     newc = c
201 206
                 old_len = len(filters_cp)
202
-                filters_cp |= set((a,b,newc))
207
+                filters_cp |= set((a, b, newc))
203 208
                 if len(filters_cp) == old_len:
204 209
                     del(filters_orig[i])
205 210
         # Sets _query_filter attribute of self query
206 211
         self._query_filter = (filters_orig, result_rel_filters)
207 212
 
208
-        #Sub queries creation
213
+        # Sub queries creation
209 214
         subq = list()
210 215
         for ds, rfilters in other_ds_filters.items():
211 216
             for rfilter in rfilters:
@@ -218,7 +223,7 @@ class LeFilteredQuery(LeQuery):
218 223
                     subq.append((rfield, query))
219 224
         self.subqueries = subq
220 225
 
221
-    ##@return informations
226
+    # @return informations
222 227
     def dump_infos(self):
223 228
         ret = super().dump_infos()
224 229
         ret['query_filter'] = self._query_filter
@@ -238,16 +243,16 @@ class LeFilteredQuery(LeQuery):
238 243
         res += '>'
239 244
         return res
240 245
 
241
-    ## @brief Prepare filters for datasource
246
+    # @brief Prepare filters for datasource
242 247
     #
243
-    #A filter can be a string or a tuple with len = 3.
248
+    # A filter can be a string or a tuple with len = 3.
244 249
     #
245
-    #This method divide filters in two categories :
250
+    # This method divide filters in two categories :
246 251
     #
247 252
     #@par Simple filters
248 253
     #
249
-    #Those filters concerns fields that represent object values (a title,
250
-    #the content, etc.) They are composed of three elements : FIELDNAME OP
254
+    # Those filters concerns fields that represent object values (a title,
255
+    # the content, etc.) They are composed of three elements : FIELDNAME OP
251 256
     # VALUE . Where :
252 257
     #- FIELDNAME is the name of the field
253 258
     #- OP is one of the authorized comparison operands (see
@@ -256,14 +261,14 @@ class LeFilteredQuery(LeQuery):
256 261
     #
257 262
     #@par Relational filters
258 263
     #
259
-    #Those filters concerns on reference fields (see the corresponding
260
-    #abstract datahandler @ref lodel.leapi.datahandlers.base_classes.Reference)
261
-    #The filter as quite the same composition than simple filters :
264
+    # Those filters concerns on reference fields (see the corresponding
265
+    # abstract datahandler @ref lodel.leapi.datahandlers.base_classes.Reference)
266
+    # The filter as quite the same composition than simple filters :
262 267
     # FIELDNAME[.REF_FIELD] OP VALUE . Where :
263 268
     #- FIELDNAME is the name of the reference field
264 269
     #- REF_FIELD is an optionnal addon to the base field. It indicate on wich
265
-    #field of the referenced object the comparison as to be done. If no
266
-    #REF_FIELD is indicated the comparison will be done on identifier.
270
+    # field of the referenced object the comparison as to be done. If no
271
+    # REF_FIELD is indicated the comparison will be done on identifier.
267 272
     #
268 273
     #@param cls
269 274
     #@param filters_l list : This list of str or tuple (or both)
@@ -271,11 +276,11 @@ class LeFilteredQuery(LeQuery):
271 276
     #@todo move this doc in another place (a dedicated page ?)
272 277
     #@warning Does not supports multiple UID for an EmClass
273 278
     def _prepare_filters(self, filters_l):
274
-        filters=list()
279
+        filters = list()
275 280
         res_filters = list()
276 281
         rel_filters = list()
277 282
         err_l = dict()
278
-        #Splitting in tuple if necessary
283
+        # Splitting in tuple if necessary
279 284
         for i, fil in enumerate(filters_l):
280 285
             if len(fil) == 3 and not isinstance(fil, str):
281 286
                 filters.append(tuple(fil))
@@ -286,7 +291,7 @@ class LeFilteredQuery(LeQuery):
286 291
                     err_l["filter %d" % i] = e
287 292
 
288 293
         for field, operator, value in filters:
289
-            err_key = "%s %s %s" % (field, operator, value) #to push in err_l
294
+            err_key = "%s %s %s" % (field, operator, value)  # to push in err_l
290 295
             # Spliting field name to be able to detect a relational field
291 296
             field_spl = field.split('.')
292 297
             if len(field_spl) == 2:
@@ -310,12 +315,12 @@ field name" % field)
310 315
                 # inconsistency
311 316
                 err_l[field] = NameError("The field '%s' in %s is not \
312 317
 a relational field, but %s.%s was present in the filter"
313
-                                            % (field,
314
-                                                self._target_class.__name__,
315
-                                                field,
316
-                                                ref_field))
318
+                                         % (field,
319
+                                            self._target_class.__name__,
320
+                                            field,
321
+                                            ref_field))
317 322
             if field_datahandler.is_reference():
318
-                #Relationnal field
323
+                # Relationnal field
319 324
                 if ref_field is None:
320 325
                     # ref_field default value
321 326
                     #
@@ -350,14 +355,14 @@ field to use for the relational filter"
350 355
                 value, error = field_datahandler.check_data_value(value)
351 356
                 if isinstance(error, Exception):
352 357
                     value = value_orig
353
-                res_filters.append((field,operator, value))
358
+                res_filters.append((field, operator, value))
354 359
         if len(err_l) > 0:
355 360
             raise LeApiDataCheckErrors(
356
-                                        "Error while preparing filters : ",
357
-                                        err_l)
361
+                "Error while preparing filters : ",
362
+                err_l)
358 363
         return (res_filters, rel_filters)
359 364
 
360
-    ## @brief Check and split a query filter
365
+    # @brief Check and split a query filter
361 366
     # @note The query_filter format is "FIELD OPERATOR VALUE"
362 367
     # @param query_filter str : A query_filter string
363 368
     # @param cls
@@ -382,18 +387,18 @@ field to use for the relational filter"
382 387
                 raise ValueError(msg % query_filter)
383 388
         return result
384 389
 
385
-    ## @brief Compile the regex for query_filter processing
390
+    # @brief Compile the regex for query_filter processing
386 391
     # @note Set _LeObject._query_re
387 392
     @classmethod
388 393
     def __compile_query_re(cls):
389 394
         op_re_piece = '(?P<operator>(%s)'
390 395
         op_re_piece %= cls._query_operators[0].replace(' ', '\s')
391 396
         for operator in cls._query_operators[1:]:
392
-            op_re_piece += '|(%s)'%operator.replace(' ', '\s')
397
+            op_re_piece += '|(%s)' % operator.replace(' ', '\s')
393 398
         op_re_piece += ')'
394 399
 
395 400
         re_full = '^\s*(?P<field>([a-z_][a-z0-9\-_]*\.)?[a-z_][a-z0-9\-_]*)\s*'
396
-        re_full += op_re_piece+'\s*(?P<value>.*)\s*$'
401
+        re_full += op_re_piece + '\s*(?P<value>.*)\s*$'
397 402
 
398 403
         cls._query_re = re.compile(re_full, flags=re.IGNORECASE)
399 404
         pass
@@ -407,10 +412,10 @@ field to use for the relational filter"
407 412
             msg %= (fieldname, target_class.__name__)
408 413
             return NameError(msg)
409 414
 
410
-    ##@brief Prepare a relational filter
415
+    # @brief Prepare a relational filter
411 416
     #
412
-    #Relational filters are composed of a tuple like the simple filters
413
-    #but the first element of this tuple is a tuple to :
417
+    # Relational filters are composed of a tuple like the simple filters
418
+    # but the first element of this tuple is a tuple to :
414 419
     #
415 420
     #<code>((FIELDNAME, {REF_CLASS: REF_FIELD}), OP, VALUE)</code>
416 421
     # Where :
@@ -419,9 +424,9 @@ field to use for the relational filter"
419 424
     # - REF_CLASS as key. It's a LeObject child class
420 425
     # - REF_FIELD as value. The name of the referenced field in the REF_CLASS
421 426
     #
422
-    #Visibly the REF_FIELD value of the dict will vary only when
423
-    #no REF_FIELD is explicitly given in the filter string notation
424
-    #and REF_CLASSES has differents uid
427
+    # Visibly the REF_FIELD value of the dict will vary only when
428
+    # no REF_FIELD is explicitly given in the filter string notation
429
+    # and REF_CLASSES has differents uid
425 430
     #
426 431
     #@par String notation examples
427 432
     #<pre>contributeur IN (1,2,3,5)</pre> will be transformed into :
@@ -439,7 +444,7 @@ field to use for the relational filter"
439 444
     #
440 445
     #@param fieldname str : The relational field name
441 446
     #@param ref_field str|None : The referenced field name (if None use
442
-    #uniq identifiers as referenced field
447
+    # uniq identifiers as referenced field
443 448
     #@return a well formed relational filter tuple or an Exception instance
444 449
     def _prepare_relational_fields(self, fieldname, ref_field=None):
445 450
         datahandler = self._target_class.field(fieldname)
@@ -467,12 +472,12 @@ the relational filter %s"
467 472
                     logger.debug(msg)
468 473
         if len(ref_dict) == 0:
469 474
             return NameError("No field named '%s' in referenced objects [%s]"
470
-                                % (ref_field,
471
-                                    ','.join([rc.__name__ for rc in ref_classes])))
475
+                             % (ref_field,
476
+                                ','.join([rc.__name__ for rc in ref_classes])))
472 477
         return (fieldname, ref_dict)
473 478
 
474 479
 
475
-##@brief A query to insert a new object
480
+# @brief A query to insert a new object
476 481
 class LeInsertQuery(LeQuery):
477 482
     _hook_prefix = 'leapi_insert_'
478 483
     _data_check_args = {'complete': True, 'allow_internal': False}
@@ -483,49 +488,49 @@ class LeInsertQuery(LeQuery):
483 488
 abstract LeObject : %s" % target_class)
484 489
         super().__init__(target_class)
485 490
 
486
-    ## @brief Implements an insert query operation, with only one insertion
487
-    # @param datas : datas to be inserted
488
-    def _query(self, datas):
489
-        datas = self._target_class.prepare_datas(datas, True, False)
490
-        id_inserted = self._rw_datasource.insert(self._target_class, datas)
491
+    #  @brief Implements an insert query operation, with only one insertion
492
+    # @param data : data to be inserted
493
+    def _query(self, data):
494
+        data = self._target_class.prepare_datas(data, True, False)
495
+        id_inserted = self._rw_datasource.insert(self._target_class, data)
491 496
         return id_inserted
492 497
     """
493 498
     ## @brief Implements an insert query operation, with multiple insertions
494
-    # @param datas : list of **datas to be inserted
495
-    def _query(self, datas):
499
+    # @param data : list of **data to be inserted
500
+    def _query(self, data):
496 501
         nb_inserted = self._datasource.insert_multi(
497
-            self._target_class,datas_list)
502
+            self._target_class,data_list)
498 503
         if nb_inserted < 0:
499 504
             raise LeApiQueryError("Multiple insertions error")
500 505
         return nb_inserted
501 506
     """
502 507
 
503
-    ## @brief Execute the insert query
504
-    def execute(self, datas):
505
-        return super().execute(datas=datas)
508
+    #  @brief Execute the insert query
509
+    def execute(self, data):
510
+        return super().execute(data=data)
506 511
 
507 512
 
508
-##@brief A query to update datas for a given object
513
+# @brief A query to update data for a given object
509 514
 #
510 515
 #@todo Change behavior, Huge optimization problem when updating using filters
511
-#and not instance. We have to run a GET and then 1 update by fecthed object...
516
+# and not instance. We have to run a GET and then 1 update by fecthed object...
512 517
 class LeUpdateQuery(LeFilteredQuery):
513 518
     _hook_prefix = 'leapi_update_'
514 519
     _data_check_args = {'complete': False, 'allow_internal': False}
515 520
 
516
-    ##@brief Instanciate an update query
521
+    # @brief Instanciate an update query
517 522
     #
518
-    #If a class and not an instance is given, no query_filters are expected
519
-    #and the update will be fast and simple. Else we have to run a get query
520
-    #before updating (to fetch datas, update them and then, construct them
521
-    #and check their consistency)
523
+    # If a class and not an instance is given, no query_filters are expected
524
+    # and the update will be fast and simple. Else we have to run a get query
525
+    # before updating (to fetch data, update them and then, construct them
526
+    # and check their consistency)
522 527
     #@param target LeObject clas or instance
523 528
     #@param query_filters list|None
524
-    #@todo change strategy with instance update. We have to accept datas for
525
-    #the execute method
529
+    #@todo change strategy with instance update. We have to accept data for
530
+    # the execute method
526 531
     def __init__(self, target, query_filters=None):
527
-        ##@brief This attr is set only if the target argument is an
528
-        #instance of a LeObject subclass
532
+        # @brief This attr is set only if the target argument is an
533
+        # instance of a LeObject subclass
529 534
         self.__leobject_instance_datas = None
530 535
         target_class = target
531 536
 
@@ -542,16 +547,16 @@ target to LeUpdateQuery constructor"
542 547
 
543 548
         super().__init__(target_class, query_filters)
544 549
 
545
-    ##@brief Implements an update query
546
-    #@param datas dict : datas to update
550
+    # @brief Implements an update query
551
+    #@param data dict : data to be updated
547 552
     #@returns the number of updated items
548
-    #@todo change stategy for instance update. Datas should be allowed
549
-    #for execute method (and query)
550
-    def _query(self, datas):
553
+    #@todo change stategy for instance update. Data should be allowed
554
+    # for execute method (and query)
555
+    def _query(self, data):
551 556
         uid_name = self._target_class._uid[0]
552 557
         if self.__leobject_instance_datas is not None:
553
-            #Instance update
554
-            #Building query_filter
558
+            # Instance update
559
+            # Building query_filter
555 560
             filters = [(
556 561
                 uid_name,
557 562
                 '=',
@@ -560,59 +565,60 @@ target to LeUpdateQuery constructor"
560 565
                 self._target_class, filters, [],
561 566
                 self.__leobject_instance_datas)
562 567
         else:
563
-            #Update by filters, we have to fetch datas before updating
568
+            # Update by filters, we have to fetch data before updating
564 569
             res = self._ro_datasource.select(
565 570
                 self._target_class, self._target_class.fieldnames(True),
566 571
                 self._query_filter[0],
567 572
                 self._query_filter[1])
568
-            #Checking and constructing datas
569
-            upd_datas = dict()
573
+            # Checking and constructing data
574
+            upd_data = dict()
570 575
             for res_data in res:
571
-                res_data.update(datas)
572
-                res_datas = self._target_class.prepare_datas(
576
+                res_data.update(data)
577
+                res_data = self._target_class.prepare_datas(
573 578
                     res_data, True, True)
574 579
                 filters = [(uid_name, '=', res_data[uid_name])]
575 580
                 res = self._rw_datasource.update(
576 581
                     self._target_class, filters, [],
577
-                    res_datas)
582
+                    res_data)
578 583
         return res
579 584
 
580
-    ## @brief Execute the update query
581
-    def execute(self, datas=None):
582
-        if self.__leobject_instance_datas is not None and datas is not None:
583
-            raise LeApiQueryError("No datas expected when running an update \
585
+    #  @brief Execute the update query
586
+    def execute(self, data=None):
587
+        if self.__leobject_instance_datas is not None and data is not None:
588
+            raise LeApiQueryError("No data expected when running an update \
584 589
 query on an instance")
585
-        if self.__leobject_instance_datas is None and datas is None:
586
-            raise LeApiQueryError("Datas are mandatory when running an update \
590
+        if self.__leobject_instance_datas is None and data is None:
591
+            raise LeApiQueryError("Data are mandatory when running an update \
587 592
 query on a class with filters")
588
-        return super().execute(datas=datas)
593
+        return super().execute(data=data)
589 594
 
590 595
 
591
-##@brief A query to delete an object
596
+# @brief A query to delete an object
592 597
 class LeDeleteQuery(LeFilteredQuery):
593 598
     _hook_prefix = 'leapi_delete_'
594 599
 
595 600
     def __init__(self, target_class, query_filter):
596 601
         super().__init__(target_class, query_filter)
597 602
 
598
-    ## @brief Execute the delete query
599
-    # @param datas
600
-    def execute(self, datas=None):
603
+    #  @brief Execute the delete query
604
+    # @param data
605
+    def execute(self, data=None):
601 606
         return super().execute()
602 607
 
603
-    ##@brief Implements delete query operations
604
-    # @param datas
608
+    # @brief Implements delete query operations
609
+    # @param data
605 610
     #@returns the number of deleted items
606
-    def _query(self, datas=None):
611
+    def _query(self, data=None):
607 612
         filters, rel_filters = self._query_filter
608 613
         nb_deleted = self._rw_datasource.delete(
609 614
             self._target_class, filters, rel_filters)
610 615
         return nb_deleted
611 616
 
617
+
612 618
 class LeGetQuery(LeFilteredQuery):
613 619
     _hook_prefix = 'leapi_get_'
614 620
 
615
-    ##@brief Instanciate a new get query
621
+    # @brief Instanciate a new get query
616 622
     #@param target_class LeObject : class of object the query is about
617 623
     #@param query_filters dict : {OP, list of query filters}
618 624
     # or tuple (FIELD, OPERATOR, VALUE) )
@@ -624,33 +630,33 @@ class LeGetQuery(LeFilteredQuery):
624 630
     #   - offset int : offset
625 631
     def __init__(self, target_class, query_filters, **kwargs):
626 632
         super().__init__(target_class, query_filters)
627
-        ##@brief The fields to get
633
+        # @brief The fields to get
628 634
         self._field_list = None
629
-        ##@brief An equivalent to the SQL ORDER BY
635
+        # @brief An equivalent to the SQL ORDER BY
630 636
         self._order = None
631
-        ##@brief An equivalent to the SQL GROUP BY
637
+        # @brief An equivalent to the SQL GROUP BY
632 638
         self._group = None
633
-        ##@brief An equivalent to the SQL LIMIT x
639
+        # @brief An equivalent to the SQL LIMIT x
634 640
         self._limit = None
635
-        ##@brief An equivalent to the SQL LIMIT x, OFFSET
641
+        # @brief An equivalent to the SQL LIMIT x, OFFSET
636 642
         self._offset = 0
637 643
 
638 644
         # Checking kwargs and assigning default values if there is some
639 645
         for argname in kwargs:
640 646
             if argname not in (
641
-                'field_list', 'order', 'group', 'limit', 'offset'):
647
+                    'field_list', 'order', 'group', 'limit', 'offset'):
642 648
                 raise TypeError("Unexpected argument '%s'" % argname)
643 649
 
644 650
         if 'field_list' not in kwargs:
645
-            self.set_field_list(target_class.fieldnames(include_ro = True))
651
+            self.set_field_list(target_class.fieldnames(include_ro=True))
646 652
         else:
647 653
             self.set_field_list(kwargs['field_list'])
648 654
 
649 655
         if 'order' in kwargs:
650
-            #check kwargs['order']
656
+            # check kwargs['order']
651 657
             self._order = kwargs['order']
652 658
         if 'group' in kwargs:
653
-            #check kwargs['group']
659
+            # check kwargs['group']
654 660
             self._group = kwargs['group']
655 661
         if 'limit' in kwargs and kwargs['limit'] is not None:
656 662
             try:
@@ -669,7 +675,7 @@ class LeGetQuery(LeFilteredQuery):
669 675
                 msg = "offset argument expected to be an integer >= 0"
670 676
                 raise ValueError(msg)
671 677
 
672
-    ##@brief Set the field list
678
+    # @brief Set the field list
673 679
     # @param field_list list | None : If None use all fields
674 680
     # @return None
675 681
     # @throw LeApiQueryError if unknown field given
@@ -682,41 +688,41 @@ class LeGetQuery(LeFilteredQuery):
682 688
                     msg = "No field named '%s' in %s"
683 689
                     msg %= (fieldname, self._target_class.__name__)
684 690
                     expt = NameError(msg)
685
-                    err_l[fieldname] =  expt
691
+                    err_l[fieldname] = expt
686 692
             if len(err_l) > 0:
687 693
                 msg = "Error while setting field_list in a get query"
688
-                raise LeApiQueryErrors(msg = msg, exceptions = err_l)
694
+                raise LeApiQueryErrors(msg=msg, exceptions=err_l)
689 695
             self._field_list = list(set(field_list))
690 696
 
691
-    ##@brief Execute the get query
692
-    def execute(self, datas=None):
697
+    # @brief Execute the get query
698
+    def execute(self, data=None):
693 699
         return super().execute()
694 700
 
695
-    ##@brief Implements select query operations
701
+    # @brief Implements select query operations
696 702
     # @returns a list containing the item(s)
697
-    def _query(self, datas=None):
698
-        # select datas corresponding to query_filter
703
+    def _query(self, data=None):
704
+        # select data corresponding to query_filter
699 705
         fl = list(self._field_list) if self._field_list is not None else None
700
-        l_datas=self._ro_datasource.select(
701
-            target = self._target_class,
702
-            field_list = fl,
703
-            filters = self._query_filter[0],
704
-            relational_filters = self._query_filter[1],
705
-            order = self._order,
706
-            group = self._group,
707
-            limit = self._limit,
708
-            offset = self._offset)
709
-        return l_datas
710
-
711
-    ##@return a dict with query infos
706
+        l_data = self._ro_datasource.select(
707
+            target=self._target_class,
708
+            field_list=fl,
709
+            filters=self._query_filter[0],
710
+            relational_filters=self._query_filter[1],
711
+            order=self._order,
712
+            group=self._group,
713
+            limit=self._limit,
714
+            offset=self._offset)
715
+        return l_data
716
+
717
+    # @return a dict with query infos
712 718
     def dump_infos(self):
713 719
         ret = super().dump_infos()
714
-        ret.update({  'field_list' : self._field_list,
715
-                        'order' : self._order,
716
-                        'group' : self._group,
717
-                        'limit' : self._limit,
718
-                        'offset': self._offset,
719
-       })
720
+        ret.update({'field_list': self._field_list,
721
+                    'order': self._order,
722
+                    'group': self._group,
723
+                    'limit': self._limit,
724
+                    'offset': self._offset,
725
+                    })
720 726
         return ret
721 727
 
722 728
     def __repr__(self):
@@ -725,7 +731,7 @@ field_list={field_list} order={order} group={group} limit={limit} \
725 731
 offset={offset}"
726 732
         res = res.format(**self.dump_infos())
727 733
         if len(self.subqueries) > 0:
728
-            for n,subq in enumerate(self.subqueries):
734
+            for n, subq in enumerate(self.subqueries):
729 735
                 res += "\n\tSubquerie %d : %s"
730 736
                 res %= (n, subq)
731 737
         res += ">"

+ 5
- 0
lodel/mlnamedobject/__init__.py View File

@@ -0,0 +1,5 @@
1
+## @package lodel.mlnamedobject A package dedicated to manage the objects' properties which can be 
2
+# translated in several languages.
3
+#
4
+# Each object in Lodel which can be translated will see his displayed name and help text being 
5
+# managed by a MlNamedObject instance, a class that is part of this package.

+ 8
- 5
lodel/mlnamedobject/mlnamedobject.py View File

@@ -4,15 +4,18 @@ from lodel.context import LodelContext
4 4
 LodelContext.expose_modules(globals(), {
5 5
     'lodel.utils.mlstring': ['MlString']})
6 6
 
7
-# @package lodel.mlnamedobject Lodel2 description of objects module
7
+## @package lodel.mlnamedobject.mlnamedobject Lodel2 description of objects module
8 8
 #
9 9
 # Display name and Description of a lodel2 object
10 10
 
11
-# @brief Class allows display name and help text for lodel2 objects and fields
12
-
13
-
11
+## @brief Represents a multi-language object (dealing with its translations) 
14 12
 class MlNamedObject(object):
15
-
13
+    
14
+    ##
15
+    # @param display_name str|dict : displayed string to name the object (either a string or a dictionnary of the translated strings can be passed)
16
+    # @param help_text str|dict : description text for this object (either a string or a dictionnary of the translated strings can be passed)
16 17
     def __init__(self, display_name=None, help_text=None):
18
+        ## @brief The object's name which will be used in all the user interfaces
17 19
         self.display_name = None if display_name is None else MlString(display_name)
20
+        ## @brief Description text for this object
18 21
         self.help_text = None if help_text is None else MlString(help_text)

+ 15
- 7
lodel/plugin/core_hooks.py View File

@@ -6,11 +6,15 @@ LodelContext.expose_modules(globals(), {
6 6
     'lodel.settings': ['Settings'],
7 7
     'lodel.logger': 'logger'})
8 8
 
9
-##@package lodel.plugin.core_hooks
10
-#@brief Lodel2 internal hooks declaration
11
-#@ingroup lodel2_plugins
9
+## @package lodel.plugin.core_hooks
10
+# @brief Lodel2 internal hooks declaration
11
+# @ingroup lodel2_plugins
12 12
 
13
-##@brief Bootstrap hook to check datasources configuration
13
+## @brief Bootstrap hook that checks datasources configuration
14
+# @param hook_name str
15
+# @param caller * : the hook's caller
16
+# @param payload * : data to be given to the hook
17
+# @throw NameError when : a set datasource family name can not be found or a datasource identifier does not match with a configured datasource.
14 18
 @LodelHook('lodel2_bootstraped')
15 19
 def datasources_bootstrap_hook(hook_name, caller, payload):
16 20
     for ds_name in Settings.datasources._fields:
@@ -32,12 +36,14 @@ def datasources_bootstrap_hook(hook_name, caller, payload):
32 36
             msg %= identifier
33 37
             raise NameError(msg)
34 38
 
35
-
36 39
         log_msg = "Found a datasource named '%s' identified by '%s'"
37 40
         log_msg %= (ds_name, identifier)
38 41
         logger.debug(log_msg)
39 42
 
40
-##@brief Bootstrap hook that print debug infos about registered hooks
43
+## @brief Bootstrap hook that prints debug infos about registered hooks
44
+# @param name str
45
+# @param caller * : the hook's caller
46
+# @param payload * : data to be given to the hook
41 47
 @LodelHook('lodel2_bootstraped')
42 48
 def list_hook_debug_hook(name, caller, payload):
43 49
     LodelContext.expose_modules(globals(), {
@@ -55,7 +61,9 @@ def list_hook_debug_hook(name, caller, payload):
55 61
         
56 62
 
57 63
 
58
-##@brief Hooks that trigger custom methods injection in dynmic classes
64
+## @brief Hook that triggers custom methods injection in dynamic classes
65
+# @param caller * : the hook's caller
66
+# @param dynclasses list : a list of classes in which the injection will occur
59 67
 @LodelHook("lodel2_dyncode_loaded")
60 68
 def lodel2_plugins_custom_methods(self, caller, dynclasses):
61 69
     LodelContext.expose_modules(globals(), {

+ 105
- 98
lodel/plugin/datasource_plugin.py View File

@@ -1,3 +1,8 @@
1
+## @package lodel.plugin.datasource_plugin Datasource plugins management module
2
+#
3
+# It contains the base classes for all the datasource plugins that could be added to Lodel
4
+
5
+
1 6
 from lodel.context import LodelContext
2 7
 LodelContext.expose_modules(globals(), {
3 8
     'lodel.plugin.plugins': ['Plugin'],
@@ -7,58 +12,61 @@ LodelContext.expose_modules(globals(), {
7 12
     'lodel.exceptions': ['LodelException', 'LodelExceptions',
8 13
         'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
9 14
 
15
+## @brief The plugin type that is used in the global settings of Lodel
10 16
 _glob_typename = 'datasource'
11 17
 
12 18
 
13
-##@brief Datasource class in plugins HAVE TO inherit from this abstract class
19
+## @brief Main abstract class from which the plugins' datasource classes must inherit.
14 20
 class AbstractDatasource(object):
15 21
     
16
-    ##@brief Trigger LodelFatalError when abtract method called
22
+    ## @brief Trigger LodelFatalError when abtract method called
23
+    # @throw LodelFatalError if there is an attempt to instanciate an object from this class
17 24
     @staticmethod
18 25
     def _abs_err():
19 26
         raise LodelFatalError("This method is abstract and HAVE TO be \
20 27
 reimplemented by plugin datasource child class")
21 28
     
22
-    ##@brief The constructor
29
+    ##
30
+    # @param *conn_args
31
+    # @param **conn_kwargs
23 32
     def __init__(self, *conn_args, **conn_kwargs):
24 33
         self._abs_err()
25 34
 
26
-    ##@brief Provide a new uniq numeric ID
27
-    #@param emcomp LeObject subclass (not instance) : To know on wich things we
28
-    #have to be uniq
29
-    #@return an integer
35
+    ## @brief Provides a new uniq numeric ID
36
+    # @param emcomp LeObject subclass (not instance) : defines against which objects type the id should be unique
37
+    # @return int
30 38
     def new_numeric_id(self, emcomp):
31 39
         self._abs_err()
32 40
 
33
-    ##@brief returns a selection of documents from the datasource
34
-    #@param target Emclass
35
-    #@param field_list list
36
-    #@param filters list : List of filters
37
-    #@param rel_filters list : List of relational filters
38
-    #@param order list : List of column to order. ex: order = [('title', 'ASC'),]
39
-    #@param group list : List of tupple representing the column to group together. ex: group = [('title', 'ASC'),]
40
-    #@param limit int : Number of records to be returned
41
-    #@param offset int: used with limit to choose the start record
42
-    #@param instanciate bool : If true, the records are returned as instances, else they are returned as dict
43
-    #@return list
41
+    ## @brief Returns a selection of documents from the datasource
42
+    # @param target Emclass : class of the documents
43
+    # @param field_list list : fields to get from the datasource
44
+    # @param filters list : List of filters
45
+    # @param rel_filters list : List of relational filters (default value : None)
46
+    # @param order list : List of column to order. ex: order = [('title', 'ASC'),] (default value : None)
47
+    # @param group list : List of tupple representing the column to group together. ex: group = [('title', 'ASC'),] (default value : None)
48
+    # @param limit int : Number of records to be returned (default value None)
49
+    # @param offset int: used with limit to choose the start record (default value : 0)
50
+    # @param instanciate bool : If true, the records are returned as instances, else they are returned as dict (default value : True)
51
+    # @return list
44 52
     def select(self, target, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
45 53
                instanciate=True):
46 54
         self._abs_err()
47 55
 
48
-    ##@brief Deletes records according to given filters
49
-    #@param target Emclass : class of the record to delete
50
-    #@param filters list : List of filters
51
-    #@param relational_filters list : List of relational filters
52
-    #@return int : number of deleted records
56
+    ## @brief Deletes records according to given filters
57
+    # @param target Emclass : class of the record to delete
58
+    # @param filters list : List of filters
59
+    # @param relational_filters list : List of relational filters
60
+    # @return int : number of deleted records
53 61
     def delete(self, target, filters, relational_filters):
54 62
         self._abs_err()
55 63
 
56 64
     ## @brief updates records according to given filters
57
-    #@param target Emclass : class of the object to insert
58
-    #@param filters list : List of filters
59
-    #@param relational_filters list : List of relational filters
60
-    #@param upd_datas dict : datas to update (new values)
61
-    #@return int : Number of updated records
65
+    # @param target Emclass : class of the object to insert
66
+    # @param filters list : List of filters
67
+    # @param relational_filters list : List of relational filters
68
+    # @param upd_datas dict : datas to update (new values)
69
+    # @return int : Number of updated records
62 70
     def update(self, target, filters, relational_filters, upd_datas):
63 71
         self._abs_err()
64 72
 
@@ -77,22 +85,21 @@ reimplemented by plugin datasource child class")
77 85
         self._abs_err()
78 86
 
79 87
 
80
-##@brief Designed to handles datasources plugins
88
+## @brief Represents a Datasource plugin
81 89
 #
82
-#A datasource provide data access to LeAPI typically a connector on a DB
83
-#or an API
90
+# It will provide an access to a data collection to LeAPI (i.e. database connector, API ...).
84 91
 #
85
-#Provide methods to initialize datasource attribute in LeAPI LeObject child
86
-#classes (see @ref leapi.leobject.LeObject._init_datasources() )
92
+# It provides the methods needed to initialize the datasource attribute in LeAPI LeObject child
93
+# classes (see @ref leapi.leobject.LeObject._init_datasources() )
87 94
 #
88
-#@note For the moment implementation is done with a retro-compatibilities
89
-#priority and not with a convenience priority.
90
-#@todo Refactor and rewrite lodel2 datasource handling
91
-#@todo Write abstract classes for Datasource and MigrationHandler !!!
95
+# @note For the moment implementation is done with a retro-compatibilities priority and not with a convenience priority.
96
+# @todo Refactor and rewrite lodel2 datasource handling
97
+# @todo Write abstract classes for Datasource and MigrationHandler !!!
92 98
 class DatasourcePlugin(Plugin):
93 99
     
94 100
     _type_conf_name = _glob_typename
95
-    ##@brief Stores confspecs indicating where DatasourcePlugin list is stored
101
+  
102
+    ## @brief Stores confspecs indicating where DatasourcePlugin list is stored
96 103
     _plist_confspecs = {
97 104
         'section': 'lodel2',
98 105
         'key': 'datasource_connectors',
@@ -104,15 +111,17 @@ class DatasourcePlugin(Plugin):
104 111
                 'none_is_valid': False})
105 112
         }
106 113
  
107
-    ##@brief Construct a DatasourcePlugin 
108
-    #@param name str : plugin name
109
-    #@see plugins.Plugin
114
+    ##
115
+    # @param name str : plugin's name
116
+    # @see plugins.Plugin
110 117
     def __init__(self, name):
111 118
         super().__init__(name)
112 119
         self.__datasource_cls = None
113 120
     
114
-    ##@brief Accessor to the datasource class
115
-    #@return A python datasource class
121
+    ## @brief Returns an accessor to the datasource class
122
+    # @return A python datasource class
123
+    # @throw DatasourcePluginError if the plugin's datasource class is not a child of 
124
+    # @ref lodel.plugin.datasource_plugin.AbstractDatasource
116 125
     def datasource_cls(self):
117 126
         if self.__datasource_cls is None:
118 127
             self.__datasource_cls = self.loader_module().Datasource
@@ -122,17 +131,15 @@ class DatasourcePlugin(Plugin):
122 131
 lodel.plugin.datasource_plugin.AbstractDatasource" % (self.name))
123 132
         return self.__datasource_cls
124 133
     
125
-    ##@brief Accessor to migration handler class
126
-    #@return A python migration handler class
134
+    ## @brief Returns an accessor to migration handler class
135
+    # @return A python migration handler class
127 136
     def migration_handler_cls(self):
128 137
         return self.loader_module().migration_handler_class()
129 138
 
130
-    ##@brief Return an initialized Datasource instance
131
-    #@param ds_name str : The name of the datasource to instanciate
132
-    #@param ro bool
133
-    #@return A properly initialized Datasource instance
134
-    #@throw SettingsError if an error occurs in settings
135
-    #@throw DatasourcePluginError for various errors
139
+    ## @brief Returns an initialized Datasource instance
140
+    # @param ds_name str : The name of the datasource to instanciate
141
+    # @param ro bool : indicates if it will be in read only mode, else it will be in write only mode
142
+    # @return A properly initialized Datasource instance
136 143
     @classmethod
137 144
     def init_datasource(cls, ds_name, ro):
138 145
         plugin_name, ds_identifier = cls.plugin_name(ds_name, ro)
@@ -140,9 +147,10 @@ lodel.plugin.datasource_plugin.AbstractDatasource" % (self.name))
140 147
         ds_cls = cls.get_datasource(plugin_name)
141 148
         return ds_cls(**ds_conf)
142 149
     
143
-    ##@brief Return an initialized MigrationHandler instance
144
-    #@param ds_name str : The datasource name
145
-    #@return A properly initialized MigrationHandler instance
150
+    ## @brief Returns an initialized MigrationHandler instance
151
+    # @param ds_name str : The datasource name
152
+    # @return A properly initialized MigrationHandler instance
153
+    # @throw PluginError if a read only datasource instance was given to the migration handler. 
146 154
     @classmethod
147 155
     def init_migration_handler(cls, ds_name):
148 156
         plugin_name, ds_identifier = cls.plugin_name(ds_name, False)
@@ -156,13 +164,12 @@ migration handler !!!")
156 164
         return mh_cls(**ds_conf)
157 165
 
158 166
 
159
-    ##@brief Given a datasource name returns a DatasourcePlugin name
160
-    #@param ds_name str : datasource name
161
-    #@param ro bool : if true consider the datasource as readonly
162
-    #@return a DatasourcePlugin name
163
-    #@throw PluginError if datasource name not found
164
-    #@throw DatasourcePermError if datasource is read_only but ro flag arg is
165
-    #false
167
+    ## @brief Given a datasource name returns a DatasourcePlugin name
168
+    # @param ds_name str : datasource name
169
+    # @param ro bool : if true consider the datasource as readonly
170
+    # @return a DatasourcePlugin name
171
+    # @throw DatasourcePluginError if the given datasource is unknown or not configured, or if there is a conflict in its "read-only" property (between the instance and the settings).
172
+    # @throw SettingsError if there are misconfigured datasource settings.
166 173
     @staticmethod
167 174
     def plugin_name(ds_name, ro):
168 175
         LodelContext.expose_modules(globals(), {
@@ -195,11 +202,11 @@ True found in settings for datasource '%s'" % ds_name)
195 202
 DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
196 203
         return res
197 204
 
198
-    ##@brief Try to fetch a datasource configuration
199
-    #@param ds_identifier str : datasource name
200
-    #@param ds_plugin_name : datasource plugin name
201
-    #@return a dict containing datasource initialisation options
202
-    #@throw NameError if a datasource plugin or instance cannot be found
205
+    ## @brief Returns a datasource's configuration
206
+    # @param ds_identifier str : datasource name
207
+    # @param ds_plugin_name : datasource plugin name
208
+    # @return a dict containing datasource initialisation options
209
+    # @throw DatasourcePluginError if a datasource plugin or instance cannot be found
203 210
     @staticmethod
204 211
     def _get_ds_connection_conf(ds_identifier,ds_plugin_name):
205 212
         LodelContext.expose_modules(globals(), {
@@ -216,56 +223,55 @@ DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
216 223
         ds_conf = getattr(ds_conf, ds_identifier)
217 224
         return {k: getattr(ds_conf,k) for k in ds_conf._fields }
218 225
 
219
-    ##@brief DatasourcePlugin instance accessor
220
-    #@param ds_name str : plugin name
221
-    #@return a DatasourcePlugin instance
222
-    #@throw PluginError if no plugin named ds_name found
223
-    #@throw PluginTypeError if ds_name ref to a plugin that is not a 
224
-    #DatasourcePlugin
226
+    ## @brief Returns a DatasourcePlugin instance from a plugin's name
227
+    # @param ds_name str : plugin name
228
+    # @return DatasourcePlugin
229
+    # @throw PluginError if no plugin named ds_name found (@see lodel.plugin.plugins.Plugin)
230
+    # @throw PluginTypeError if ds_name ref to a plugin that is not a DatasourcePlugin
225 231
     @classmethod
226 232
     def get(cls, ds_name):
227
-        pinstance = super().get(ds_name) #Will raise PluginError if bad name
233
+        pinstance = super().get(ds_name)  # Will raise PluginError if bad name
228 234
         if not isinstance(pinstance, DatasourcePlugin):
229 235
            raise PluginTypeErrror("A name of a DatasourcePlugin was excepted \
230 236
 but %s is a %s" % (ds_name, pinstance.__class__.__name__))
231 237
         return pinstance
232 238
 
233
-    ##@brief Return a datasource class given a datasource name
234
-    #@param ds_plugin_name str : datasource plugin name
235
-    #@throw PluginError if ds_name is not an existing plugin name
236
-    #@throw PluginTypeError if ds_name is not the name of a DatasourcePlugin
239
+    ## @brief Returns a datasource class given a datasource name
240
+    # @param ds_plugin_name str : datasource plugin name
241
+    # @return Datasource class
237 242
     @classmethod
238 243
     def get_datasource(cls, ds_plugin_name):
239 244
         return cls.get(ds_plugin_name).datasource_cls()
240 245
     
241
-    ##@brief Given a plugin name returns a migration handler class
242
-    #@param ds_plugin_name str : a datasource plugin name
246
+    ## @brief Returns a migration handler class, given a plugin name
247
+    # @param ds_plugin_name str : a datasource plugin name
248
+    # @return MigrationHandler class
243 249
     @classmethod
244 250
     def get_migration_handler(cls, ds_plugin_name):
245 251
         return cls.get(ds_plugin_name).migration_handler_cls()
246 252
 
247 253
 
248
-##@page lodel2_datasources Lodel2 datasources
254
+## @page lodel2_datasources Lodel2 datasources
249 255
 #
250
-#@par lodel2_datasources_intro Intro
256
+# @par lodel2_datasources_intro Introduction
251 257
 # A single lodel2 website can interact with multiple datasources. This page
252
-# aims to describe configuration & organisation of datasources in lodel2.
258
+# aims to describe configuration and organisation of datasources in lodel2.
253 259
 # Each object is attached to a datasource. This association is done in the
254
-# editorial model, the datasource is identified by a name.
260
+# editorial model, in which the datasource is identified by its name.
255 261
 #
256
-#@par Datasources declaration
257
-# To define a datasource you have to write something like this in confs file :
258
-#<pre>
259
-#[lodel2.datasources.DATASOURCE_NAME]
260
-#identifier = DATASOURCE_FAMILY.SOURCE_NAME
261
-#</pre>
262
-# See below for DATASOURCE_FAMILY & SOURCE_NAME
262
+# @par Datasources declaration
263
+# To define a datasource you have to write something like this in configuration file :
264
+# <pre>
265
+# [lodel2.datasources.DATASOURCE_NAME]
266
+# identifier = DATASOURCE_FAMILY.SOURCE_NAME
267
+# </pre>
268
+#  See below for DATASOURCE_FAMILY & SOURCE_NAME
263 269
 #
264
-#@par Datasources plugins
265
-# Each datasource family is a plugin ( 
266
-#@ref plugin_doc "More informations on plugins" ). For example mysql or a 
267
-#mongodb plugins. Here is the CONFSPEC variable templates for datasources 
268
-#plugin
270
+# @par Datasources plugins
271
+# Each datasource family is a plugin ( @ref plugin_doc "More informations on plugins" ). 
272
+# For example mysql or a mongodb plugins. \n
273
+#
274
+# Here is the CONFSPEC variable templates for datasources plugin
269 275
 #<pre>
270 276
 #CONFSPEC = {
271 277
 #                'lodel2.datasource.example.*' : {
@@ -275,7 +281,8 @@ but %s is a %s" % (ds_name, pinstance.__class__.__name__))
275 281
 #                }
276 282
 #}
277 283
 #</pre>
278
-#MySQL example
284
+# 
285
+#MySQL example \n
279 286
 #<pre>
280 287
 #CONFSPEC = {
281 288
 #                'lodel2.datasource.mysql.*' : {
@@ -291,8 +298,8 @@ but %s is a %s" % (ds_name, pinstance.__class__.__name__))
291 298
 #}
292 299
 #</pre>
293 300
 #
294
-#@par Configuration example
295
-#<pre>
301
+# @par Configuration example
302
+# <pre>
296 303
 # [lodel2.datasources.main]
297 304
 # identifier = mysql.Core
298 305
 # [lodel2.datasources.revues_write]

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

@@ -1,3 +1,6 @@
1
+## @package lodel.plugin.exceptions Plugin management specific exceptions
2
+
3
+
1 4
 class PluginError(Exception):
2 5
     pass
3 6
 

+ 10
- 2
lodel/plugin/extensions.py View File

@@ -1,3 +1,6 @@
1
+## @package lodel.plugin.extensions A package to manage the Extension plugins
2
+
3
+
1 4
 from lodel.context import LodelContext
2 5
 LodelContext.expose_modules(globals(), {
3 6
     'lodel.plugin.plugins': ['Plugin'],
@@ -7,9 +10,12 @@ LodelContext.expose_modules(globals(), {
7 10
 
8 11
 _glob_typename = 'extension'
9 12
 
10
-
13
+## @brief A class representing a basic Extension plugin
14
+# 
15
+# This class will be extended for each plugin of this type.
11 16
 class Extension(Plugin):
12 17
     
18
+    ## @brief Specifies the settings linked to this plugin
13 19
     _plist_confspecs = {
14 20
         'section': 'lodel2',
15 21
         'key': 'extensions',
@@ -20,6 +26,8 @@ class Extension(Plugin):
20 26
                 'ptype': _glob_typename,
21 27
                 'none_is_valid': False})
22 28
         }
23
-
29
+    
30
+    ## @brief A property defining the type's name of this plugin.
31
+    # By default, it's the global type name ("extension" here).
24 32
     _type_conf_name = _glob_typename
25 33
 

+ 18
- 17
lodel/plugin/hooks.py View File

@@ -1,20 +1,22 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
+## @package lodel.plugin.hooks This module deals with the Hook management in Lodel
4
+
3 5
 import os
4 6
 import copy
5 7
 from lodel.context import LodelContext
6 8
 
7 9
 
8
-##@brief Class designed to handle a hook's callback with a priority
10
+## @brief Class designed to handle a hook's callback with a priority
9 11
 class DecoratedWrapper(object):
10
-    ##@brief Constructor
12
+    ##
11 13
     # @param hook function : the function to wrap
12 14
     # @param priority int : the callbacl priority
13 15
     def __init__(self, hook, priority):
14 16
         self._priority = priority
15 17
         self._hook = hook
16 18
     
17
-    ##@brief Call the callback
19
+    ## @brief Calls the callback
18 20
     # @param hook_name str : The name of the called hook
19 21
     # @param caller * : The caller (depends on the hook)
20 22
     # @param payload * : Datas that depends on the hook
@@ -22,12 +24,14 @@ class DecoratedWrapper(object):
22 24
     def __call__(self, hook_name, caller, payload):
23 25
         return self._hook(hook_name, caller, payload)
24 26
 
27
+    ## @brief Returns the string representation of the class
28
+    # It shows the name and the priority of the hook
25 29
     def __str__(self):
26 30
         return "<LodelHook '%s' priority = %s>" % (
27 31
             self._hook.__name__, self._priority)
28 32
 
29
-##@brief Decorator designed to register hook's callbacks
30
-#@ingroup lodel2_plugins
33
+## @brief Decorator designed to register hook's callbacks
34
+# @ingroup lodel2_plugins
31 35
 #
32 36
 # @note Decorated functions are expected to take 3 arguments :
33 37
 #  - hook_name : the called hook name
@@ -35,17 +39,17 @@ class DecoratedWrapper(object):
35 39
 #  - payload : datas depending on the hook
36 40
 class LodelHook(object):
37 41
     
38
-    ##@brief Stores all hooks (DecoratedWrapper instances)
42
+    ## @brief Stores all hooks (DecoratedWrapper instances)
39 43
     _hooks = dict()
40 44
     
41
-    ##@brief Decorator constructor
45
+    ##
42 46
     # @param hook_name str : the name of the hook to register to
43
-    # @param priority int : the hook priority
47
+    # @param priority int : the hook priority (default value : None)
44 48
     def __init__(self, hook_name, priority = None):
45 49
         self._hook_name = hook_name
46 50
         self._priority = 0xFFFF if priority is None else priority
47 51
     
48
-    ##@brief called just after __init__
52
+    ## @brief called just after __init__
49 53
     # @param hook function : the decorated function
50 54
     # @return the hook argument
51 55
     def __call__(self, hook):
@@ -56,11 +60,10 @@ class LodelHook(object):
56 60
         self._hooks[self._hook_name] = sorted(self._hooks[self._hook_name], key = lambda h: h._priority)
57 61
         return hook
58 62
 
59
-    ##@brief Call hooks
63
+    ## @brief Calls a hook
60 64
     # @param hook_name str : the hook's name
61 65
     # @param caller * : the hook caller (depends on the hook)
62 66
     # @param payload * : datas for the hook
63
-    # @param cls
64 67
     # @return modified payload
65 68
     @classmethod
66 69
     def call_hook(cls, hook_name, caller, payload):
@@ -73,10 +76,9 @@ class LodelHook(object):
73 76
                 payload = hook(hook_name, caller, payload)
74 77
         return payload
75 78
     
76
-    ##@brief Fetch registered hooks
77
-    # @param names list | None : optionnal filter on name
78
-    # @param cls
79
-    # @return a list of functions
79
+    ## @brief Fetches registered hooks
80
+    # @param names list | None : optionnal filter on name (default value : None)
81
+    # @return dict containing for each name a list of the hooks and their priorities
80 82
     @classmethod
81 83
     def hook_list(cls, names = None):
82 84
         res = None
@@ -86,8 +88,7 @@ class LodelHook(object):
86 88
             res = copy.copy(cls._hooks)
87 89
         return { name: [(hook._hook, hook._priority) for hook in hooks] for name, hooks in res.items() }
88 90
     
89
-    ##@brief Unregister all hooks
90
-    # @param cls
91
+    ## @brief Unregister all hooks
91 92
     # @warning REALLY NOT a good idea !
92 93
     # @note implemented for testing purpose
93 94
     @classmethod

+ 12
- 4
lodel/plugin/interface.py View File

@@ -1,3 +1,5 @@
1
+## @package lodel.plugin.interface Handles the Interface type plugins
2
+
1 3
 from lodel.context import LodelContext
2 4
 LodelContext.expose_modules(globals(), {
3 5
     'lodel.plugin.plugins': ['Plugin'],
@@ -5,16 +7,18 @@ LodelContext.expose_modules(globals(), {
5 7
         'LodelScriptError', 'DatasourcePluginError'],
6 8
     'lodel.validator.validator': ['Validator']})
7 9
 
10
+## @brief Global type name used in the settings of Lodel for this type of plugins
8 11
 _glob_typename = 'ui'
9 12
 
10 13
 
11
-##@brief Handles interfaces plugin
14
+##@brief A plugin Interface
12 15
 #@note It's a singleton class. Only 1 interface allowed by instance.
13 16
 class InterfacePlugin(Plugin):
14 17
     
15
-    ##@brief Singleton instance storage
18
+    ## @brief Singleton instance storage
16 19
     _instance = None
17 20
 
21
+    ## @brief Settings description
18 22
     _plist_confspecs = {
19 23
         'section': 'lodel2',
20 24
         'key': 'interface',
@@ -22,16 +26,20 @@ class InterfacePlugin(Plugin):
22 26
         'validator': Validator(
23 27
             'plugin', none_is_valid = True, ptype = _glob_typename)}
24 28
 
29
+    ## @brief plugin type name
25 30
     _type_conf_name = _glob_typename
26 31
     
32
+    ##
33
+    # @param name str : Name of the interface plugin
34
+    # @throw PluginError if there is already an interface plugin instanciated
27 35
     def __init__(self, name):
28 36
         if InterfacePlugin._instance is not None:
29 37
             raise PluginError("Maximum one interface allowed")
30 38
         super().__init__(name)
31 39
         self._instance = self
32 40
 
33
-    ##@brief Clear class
34
-    #@see plugins.Plugin::clear()
41
+    ## @brief Clears the singleton from its active instance
42
+    # @see plugins.Plugin::clear()
35 43
     @classmethod
36 44
     def clear_cls(cls):
37 45
         if cls._instance is not None:

+ 27
- 37
lodel/plugins/mongodb_datasource/datasource.py View File

@@ -84,9 +84,9 @@ class MongoDbDatasource(AbstractDatasource):
84 84
         target = emcomp.uid_source()
85 85
         tuid = target._uid[0] # Multiple UID broken here
86 86
         results = self.select(
87
-            target, field_list = [tuid], filters = [], 
87
+            target, field_list = [tuid], filters = [],
88 88
             order=[(tuid, 'DESC')], limit = 1)
89
-        if len(results) == 0: 
89
+        if len(results) == 0:
90 90
             return 1
91 91
         return results[0][tuid]+1
92 92
 
@@ -95,23 +95,23 @@ class MongoDbDatasource(AbstractDatasource):
95 95
     #@param field_list list
96 96
     #@param filters list : List of filters
97 97
     #@param relational_filters list : List of relational filters
98
-    #@param order list : List of column to order. ex: order = 
98
+    #@param order list : List of column to order. ex: order =
99 99
     #[('title', 'ASC'),]
100
-    #@param group list : List of tupple representing the column used as 
100
+    #@param group list : List of tupple representing the column used as
101 101
     #"group by" fields. ex: group = [('title', 'ASC'),]
102 102
     #@param limit int : Number of records to be returned
103 103
     #@param offset int: used with limit to choose the start record
104 104
     #@return list
105 105
     #@todo Implement group for abstract LeObject childs
106
-    def select(self, target, field_list, filters = None, 
107
-            relational_filters=None, order=None, group=None, limit=None, 
106
+    def select(self, target, field_list, filters = None,
107
+            relational_filters=None, order=None, group=None, limit=None,
108 108
             offset=0):
109 109
         if target.is_abstract():
110 110
             #Reccursiv calls for abstract LeObject child
111 111
             results =  self.__act_on_abstract(target, filters,
112 112
                 relational_filters, self.select, field_list = field_list,
113 113
                 order = order, group = group, limit = limit)
114
-            
114
+
115 115
             #Here we may implement the group
116 116
             #If sorted query we have to sort again
117 117
             if order is not None:
@@ -138,11 +138,11 @@ class MongoDbDatasource(AbstractDatasource):
138 138
 
139 139
         query_filters = self.__process_filters(
140 140
             target, filters, relational_filters)
141
-        
141
+
142 142
         query_result_ordering = None
143 143
         if order is not None:
144 144
             query_result_ordering = utils.parse_query_order(order)
145
-        
145
+
146 146
         if group is None:
147 147
             if field_list is None:
148 148
                 field_list = dict()
@@ -189,7 +189,7 @@ class MongoDbDatasource(AbstractDatasource):
189 189
         results = list()
190 190
         for document in cursor:
191 191
             results.append(document)
192
-        
192
+
193 193
         return results
194 194
 
195 195
     ##@brief Deletes records according to given filters
@@ -236,7 +236,7 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
236 236
         self.__update_backref_filtered(target, filters, relational_filters,
237 237
             upd_datas, old_datas_l)
238 238
         return res
239
-    
239
+
240 240
     ##@brief Designed to be called by backref update in order to avoid
241 241
     #infinite updates between back references
242 242
     #@see update()
@@ -269,7 +269,7 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
269 269
             raise MongoDataSourceError("Missing UID data will inserting a new \
270 270
 %s" % target.__class__)
271 271
         res = self.__collection(target).insert(new_datas)
272
-        self.__update_backref(target, new_datas[uidname], None, new_datas) 
272
+        self.__update_backref(target, new_datas[uidname], None, new_datas)
273 273
         return str(res)
274 274
 
275 275
     ## @brief Inserts a list of records in a given collection
@@ -281,10 +281,10 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
281 281
             self._data_cast(datas)
282 282
         res = self.__collection(target).insert_many(datas_list)
283 283
         for new_datas in datas_list:
284
-            self.__update_backref(target, None, new_datas) 
284
+            self.__update_backref(target, None, new_datas)
285 285
             target.make_consistency(datas=new_datas)
286 286
         return list(res.inserted_ids)
287
-    
287
+
288 288
     ##@brief Update backref giving an action
289 289
     #@param target leObject child class
290 290
     #@param filters
@@ -303,7 +303,7 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
303 303
             old_datas_l = self.__collection(target).find(
304 304
                 mongo_filters)
305 305
             old_datas_l = list(old_datas_l)
306
-        
306
+
307 307
         uidname = target.uid_fieldname()[0] #MULTIPLE UID BROKEN HERE
308 308
         for old_datas in old_datas_l:
309 309
             self.__update_backref(
@@ -312,7 +312,7 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
312 312
     ##@brief Update back references of an object
313 313
     #@ingroup plugin_mongodb_bref_op
314 314
     #
315
-    #old_datas and new_datas arguments are set to None to indicate 
315
+    #old_datas and new_datas arguments are set to None to indicate
316 316
     #insertion or deletion. Calls examples :
317 317
     #@par LeObject insert __update backref call
318 318
     #<pre>
@@ -441,8 +441,8 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
441 441
                 self.__update_no_backref(
442 442
                     leo.__class__, [(leo.uid_fieldname()[0], '=', uidval)],
443 443
                     [], datas)
444
-    
445
-    ##@brief Utility function designed to handle the upd_dict of 
444
+
445
+    ##@brief Utility function designed to handle the upd_dict of
446 446
     #__update_backref()
447 447
     #
448 448
     #Basically checks if a key exists at some level, if not create it with
@@ -453,7 +453,7 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
453 453
     #@param uid_val mixed : the UID of the referenced object
454 454
     #@return the updated version of upd_dict
455 455
     @staticmethod
456
-    def __update_backref_upd_dict_prepare(upd_dict,bref_infos, bref_fname, 
456
+    def __update_backref_upd_dict_prepare(upd_dict,bref_infos, bref_fname,
457 457
             uid_val):
458 458
         bref_cls, bref_leo, bref_dh, bref_value = bref_infos
459 459
         if bref_cls not in upd_dict:
@@ -463,8 +463,8 @@ abstract, preparing reccursiv calls" % (target, filters, relational_filters))
463 463
         if bref_fname not in upd_dict[bref_cls][uid_val]:
464 464
             upd_dict[bref_cls][uid_val][1][bref_fname] = bref_value
465 465
         return upd_dict
466
-        
467
-        
466
+
467
+
468 468
     ##@brief Prepare a one value back reference update
469 469
     #@param fname str : the source Reference field name
470 470
     #@param fdh DataHandler : the source Reference DataHandler
@@ -520,7 +520,7 @@ have expected value. Expected was %s but found %s in %s" % (
520 520
                 return bref_val
521 521
             elif oldd and not newdd:
522 522
                 #deletion
523
-                if not hasattr(bref_dh, "default"): 
523
+                if not hasattr(bref_dh, "default"):
524 524
                     raise MongoDbConsistencyError("Unable to delete a \
525 525
 value for a back reference update. The concerned field don't have a default \
526 526
 value : in %s field %s" % (bref_leo,fname))
@@ -528,7 +528,7 @@ value : in %s field %s" % (bref_leo,fname))
528 528
             elif not oldd and newdd:
529 529
                 bref_val = tuid
530 530
         return bref_val
531
-    
531
+
532 532
     ##@brief Fetch back reference informations
533 533
     #@warning thank's to __update_backref_act() this method is useless
534 534
     #@param bref_cls LeObject child class : __back_reference[0]
@@ -608,7 +608,7 @@ on non abstract childs" % act.__name__)
608 608
             port = self.__db_infos['port'],
609 609
             db_name = db_name,
610 610
             ro = ro)
611
-        
611
+
612 612
         self.__conn_hash = conn_h = hash(conn_string)
613 613
         if conn_h in self._connections:
614 614
             self._connections[conn_h]['conn_count'] += 1
@@ -619,7 +619,7 @@ on non abstract childs" % act.__name__)
619 619
                 'conn_count': 1,
620 620
                 'db': utils.connect(conn_string)}
621 621
             return self._connections[conn_h]['db'][self.__db_infos['db_name']]
622
-                    
622
+
623 623
 
624 624
     ##@brief Return a pymongo collection given a LeObject child class
625 625
     #@param leobject LeObject child class (no instance)
@@ -760,7 +760,7 @@ on non abstract childs" % act.__name__)
760 760
                     rfilters[fname][repr_leo][rfield] = list()
761 761
                 rfilters[fname][repr_leo][rfield].append((op, value))
762 762
         return rfilters
763
-    
763
+
764 764
     ##@brief Convert lodel2 filters to pymongo conditions
765 765
     #@param filters list : list of lodel filters
766 766
     #@return dict representing pymongo conditions
@@ -859,7 +859,7 @@ field/operator couple in a query. We will keep only the first one")
859 859
             1 if (a[fname]>b[fname] if cmpdir == 'ASC' else a[fname]<b[fname])\
860 860
             else -1)
861 861
 
862
-    
862
+
863 863
     ##@brief Correct some datas before giving them to pymongo
864 864
     #
865 865
     #For example sets has to be casted to lise
@@ -874,13 +874,3 @@ field/operator couple in a query. We will keep only the first one")
874 874
                 #with sets
875 875
                 datas[dname] = list(datas[dname])
876 876
         return datas
877
-
878
-    ##@brief Tool to check if a record with unique id uid is set in the target_class representation
879
-    #@param target_class : class to check in 
880
-    #@param uid : a unique id in target_class
881
-    #@returns true if a record with unique id uid exists in the target_class representation, false if not
882
-    def is_exist(self, target_class, uid):
883
-    # retrouver la table qui correspond à target_class
884
-    # vérifier qu'il existe, ou pas, un enregistrement contenant uid
885
-        result = self.select(self, target_class, [target_class.uid_fieldname], filters = [(target_class.uid_fieldname, '=', uid)])
886
-        return len(result) == 1

+ 13
- 13
tests/editorial_model/test_model.py View File

@@ -23,7 +23,7 @@ class EditorialModelTestCase(unittest.TestCase):
23 23
         grp1.add_components((cls1, c1f1))
24 24
         grp2 = model.new_group('testgroup2')
25 25
         grp2.add_components((cls2, c1f2, c2f1, c2f2))
26
-        grp2.add_dependencie(grp1)
26
+        grp2.add_dependency(grp1)
27 27
         e_hash = 0x250eab75e782e51bbf212f47c6159571
28 28
         self.assertEqual(model.d_hash(), e_hash)
29 29
 
@@ -181,10 +181,10 @@ class EmGroupTestCase(unittest.TestCase):
181 181
         grp3 = EmGroup('grp3')
182 182
         grp4 = EmGroup('grp4')
183 183
 
184
-        grp2.add_dependencie(grp1)
185
-        grp3.add_dependencie(grp2)
186
-        grp4.add_dependencie(grp2)
187
-        grp4.add_dependencie(grp1)
184
+        grp2.add_dependency(grp1)
185
+        grp3.add_dependency(grp2)
186
+        grp4.add_dependency(grp2)
187
+        grp4.add_dependency(grp1)
188 188
 
189 189
         self.assertEqual(set(grp1.dependencies().values()), set())
190 190
         self.assertEqual(set(grp2.dependencies().values()), set([grp1]))
@@ -261,10 +261,10 @@ class EmGroupTestCase(unittest.TestCase):
261 261
     def test_deps_complex(self):
262 262
         """ More complex dependencies handling test """
263 263
         grps = [ EmGroup('group%d' % i) for i in range(6) ]
264
-        grps[5].add_dependencie( (grps[1], grps[2], grps[4]) )
265
-        grps[4].add_dependencie( (grps[1], grps[3]) )
266
-        grps[3].add_dependencie( (grps[0],) )
267
-        grps[1].add_dependencie( (grps[2], grps[0]) )
264
+        grps[5].add_dependency( (grps[1], grps[2], grps[4]) )
265
+        grps[4].add_dependency( (grps[1], grps[3]) )
266
+        grps[3].add_dependency( (grps[0],) )
267
+        grps[1].add_dependency( (grps[2], grps[0]) )
268 268
         self.assertEqual(
269 269
                             set(grps[5].dependencies(True).values()),
270 270
                             set( grps[i] for i in range(5))
@@ -273,7 +273,7 @@ class EmGroupTestCase(unittest.TestCase):
273 273
                             set(grps[4].dependencies(True).values()),
274 274
                             set( grps[i] for i in range(4))
275 275
         )
276
-        grps[2].add_dependencie(grps[0])
276
+        grps[2].add_dependency(grps[0])
277 277
         self.assertEqual(
278 278
                             set(grps[5].dependencies(True).values()),
279 279
                             set( grps[i] for i in range(5))
@@ -284,18 +284,18 @@ class EmGroupTestCase(unittest.TestCase):
284 284
         )
285 285
         # Inserting circular deps
286 286
         with self.assertRaises(EditorialModelError):
287
-            grps[0].add_dependencie(grps[5])
287
+            grps[0].add_dependency(grps[5])
288 288
 
289 289
     def test_circular_dep(self):
290 290
         """ Test circular dependencies detection """
291 291
         grps = [ EmGroup('group%d' % i) for i in range(10) ]
292 292
         for i in range(1,10):
293
-            grps[i].add_dependencie(grps[i-1])
293
+            grps[i].add_dependency(grps[i-1])
294 294
 
295 295
         for i in range(1,10):
296 296
             for j in range(i+1,10):
297 297
                 with self.assertRaises(EditorialModelError):
298
-                    grps[i].add_dependencie(grps[j])
298
+                    grps[i].add_dependency(grps[j])
299 299
 
300 300
     def test_d_hash(self):
301 301
         """ Test the deterministic hash method """

+ 1
- 1
tests/editorial_model/test_translator_picklefile.py View File

@@ -26,7 +26,7 @@ class PickleFileTestCase(unittest.TestCase):
26 26
         grp2 = model.new_group('testgroup2')
27 27
         grp2.add_components((cls2, c1f2, c2f1, c2f2))
28 28
 
29
-        grp2.add_dependencie(grp1)
29
+        grp2.add_dependency(grp1)
30 30
         
31 31
         tmpfd, temp_file = tempfile.mkstemp()
32 32
         os.close(tmpfd)

+ 1
- 1
tests/editorial_model/test_translator_xmlfile.py View File

@@ -44,7 +44,7 @@ class XmlFileTestCase(unittest.TestCase):
44 44
         grp2 = emmodel.new_group('testgroup2')
45 45
         grp2.add_components((cls2, c1f2, c2f1, c2f2))
46 46
 
47
-        grp2.add_dependencie(grp1)
47
+        grp2.add_dependency(grp1)
48 48
         
49 49
         f_tmp, file_name = tempfile.mkstemp()
50 50
         os.close(f_tmp)

+ 3
- 3
tests/leapi/query/test_datasource.py View File

@@ -105,7 +105,7 @@ class LeQueryDatasourceTestCase(unittest.TestCase):
105 105
             [(('alias', {cls: 'firstname'}), '=', 'foo')])
106 106
         self.check_nocall(read = False, exclude = ['delete'])
107 107
         self.check_nocall(read = True)
108
-    
108
+
109 109
     @unittest.skip("Waiting references checks stack implementation")
110 110
     def test_insert(self):
111 111
         """ Testing LeInsertQuery mocking datasource """
@@ -145,8 +145,8 @@ class LeQueryDatasourceTestCase(unittest.TestCase):
145 145
         query = LeUpdateQuery(inst)
146 146
 
147 147
         with self.assertRaises(LeApiQueryError):
148
-            # Bad call, giving datas while an instance was given to __init__
149
-            query.execute(datas = {'firstname': 'ooba'})
148
+            # Bad call, giving data while an instance was given to __init__
149
+            query.execute(data = {'firstname': 'ooba'})
150 150
 
151 151
         query.execute()
152 152
         self.mockwrite.update.assert_called_once_with(

Loading…
Cancel
Save