瀏覽代碼

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

Quentin Bonaventure 7 年之前
父節點
當前提交
cb95014a9b

+ 109
- 101
lodel/auth/client.py 查看文件

11
     'lodel.logger': 'logger',
11
     'lodel.logger': 'logger',
12
     'lodel.plugin': [('SessionHandlerPlugin', 'SessionHandler')],
12
     'lodel.plugin': [('SessionHandlerPlugin', 'SessionHandler')],
13
     'lodel.auth.exceptions': ['ClientError', 'ClientAuthenticationFailure',
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
 #@todo Maybe we can delete this metaclass....
20
 #@todo Maybe we can delete this metaclass....
21
+
22
+
21
 class ClientMetaclass(type):
23
 class ClientMetaclass(type):
22
-    
24
+
23
     def __init__(self, name, bases, attrs):
25
     def __init__(self, name, bases, attrs):
24
         return super(ClientMetaclass, self).__init__(name, bases, attrs)
26
         return super(ClientMetaclass, self).__init__(name, bases, attrs)
25
 
27
 
26
     def __getitem__(self, key):
28
     def __getitem__(self, key):
27
-        return self.datas()[key]
29
+        return self.data()[key]
28
 
30
 
29
     def __delitem__(self, key):
31
     def __delitem__(self, key):
30
-        del(self.datas()[key])
32
+        del(self.data()[key])
31
 
33
 
32
     def __setitem__(self, key, value):
34
     def __setitem__(self, key, value):
33
         if self.get_session_token() is None:
35
         if self.get_session_token() is None:
34
             self.set_session_token(SessionHandler.start())
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
     def __str__(self):
40
     def __str__(self):
39
         return str(self._instance)
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
 # This class is designed to handle client authentication and sessions
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
     _instance = None
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
     # A list of dict, with keys 'login' and 'password', items are tuple.
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
     # - LeObjectChild the dynclass containing the login
58
     # - LeObjectChild the dynclass containing the login
55
     # - Fieldname the fieldname of LeObjectChild containing the login
59
     # - Fieldname the fieldname of LeObjectChild containing the login
56
     # - link_field None if both login and password are in the same
60
     # - link_field None if both login and password are in the same
57
     # LeObjectChild. Else contains the field that make the link between
61
     # LeObjectChild. Else contains the field that make the link between
58
     # login LeObject and password LeObject
62
     # login LeObject and password LeObject
59
-    #- password typle contains (LeObjectChild, FieldName)
63
+    # - password typle contains (LeObjectChild, FieldName)
60
     _infos_fields = None
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
     _AUTH_DATANAME = '__auth_user_infos'
68
     _AUTH_DATANAME = '__auth_user_infos'
65
-    
66
 
69
 
67
-    ##@brief Constructor
70
+    # @brief Constructor
68
     #@param session_token mixed : Session token provided by client to interface
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
         logger.debug(session_token)
73
         logger.debug(session_token)
71
         if self.__class__ == Client:
74
         if self.__class__ == Client:
72
             raise NotImplementedError("Abstract class")
75
             raise NotImplementedError("Abstract class")
73
         logger.debug("New instance of Client child class %s" %
76
         logger.debug("New instance of Client child class %s" %
74
-            self.__class__.__name__)
77
+                     self.__class__.__name__)
75
         if Client._instance is not None:
78
         if Client._instance is not None:
76
             old = Client._instance
79
             old = Client._instance
77
             Client._instance = None
80
             Client._instance = None
78
             del(old)
81
             del(old)
79
             logger.debug("Replacing old Client instance by a new one")
82
             logger.debug("Replacing old Client instance by a new one")
80
         else:
83
         else:
81
-            #first instanciation, fetching settings
84
+            # first instanciation, fetching settings
82
             self.fetch_settings()
85
             self.fetch_settings()
83
-        ##@brief Stores infos for authenticated users (None == anonymous)
86
+        # @brief Stores infos for authenticated users (None == anonymous)
84
         self.__user = None
87
         self.__user = None
85
-        ##@brief Stores the session handler
88
+        # @brief Stores the session handler
86
         Client._instance = self
89
         Client._instance = self
87
-        ##@brief Stores LodelSession instance
88
-        
89
-        self.__datas = dict()
90
+        # @brief Stores LodelSession instance
91
+        self.__data = dict()
90
         if session_token is not None:
92
         if session_token is not None:
91
-            self.__datas = SessionHandler.restore(session_token)
93
+            self.__data = SessionHandler.restore(session_token)
92
         self.__session_token = session_token
94
         self.__session_token = session_token
93
-        
95
+
94
         logger.debug("New client : %s" % self)
96
         logger.debug("New client : %s" % self)
95
-    
97
+
96
     def __del__(self):
98
     def __del__(self):
97
         del(self.__session_token)
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
     @classmethod
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
     @classmethod
109
     @classmethod
105
     def user(cls):
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
         else:
113
         else:
109
             return None
114
             return None
115
+
116
+    # @brief Returns the session's token
110
     @classmethod
117
     @classmethod
111
     def get_session_token(cls):
118
     def get_session_token(cls):
112
         return cls._instance.__session_token
119
         return cls._instance.__session_token
113
-    
120
+
121
+    # @brief Set the session's token
122
+    #@param the value of the token
114
     @classmethod
123
     @classmethod
115
     def set_session_token(cls, value):
124
     def set_session_token(cls, value):
116
         cls._instance.__session_token = value
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
     #@param login str : provided login
128
     #@param login str : provided login
120
     #@param password str : provided password (hash)
129
     #@param password str : provided password (hash)
121
     #@warning brokes composed UID
130
     #@warning brokes composed UID
122
-    #@note implemets multiple login/password sources (useless ?)
131
+    #@note implements multiple login/password sources (useless ?)
123
     #@todo composed UID broken method
132
     #@todo composed UID broken method
124
     #@todo allow to provide an authentication source
133
     #@todo allow to provide an authentication source
125
     @classmethod
134
     @classmethod
126
-    def authenticate(self, login = None, password = None):
127
-        #Authenticate
135
+    def authenticate(self, login=None, password=None):
136
+        # Authenticate
128
         for infos in self._infos_fields:
137
         for infos in self._infos_fields:
129
             logger.debug(self._infos_fields)
138
             logger.debug(self._infos_fields)
130
             login_cls = infos['login'][0]
139
             login_cls = infos['login'][0]
131
             pass_cls = infos['password'][0]
140
             pass_cls = infos['password'][0]
132
             qfilter = "{passfname} = {passhash}"
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
             if login_cls == pass_cls:
143
             if login_cls == pass_cls:
135
-                #Same EmClass for login & pass
144
+                # Same EmClass for login & pass
136
                 qfilter = qfilter.format(
145
                 qfilter = qfilter.format(
137
-                    passfname = infos['password'][1],
138
-                    passhash = password)
146
+                    passfname=infos['password'][1],
147
+                    passhash=password)
139
             else:
148
             else:
140
-                #Different EmClass, building a relational filter
149
+                # Different EmClass, building a relational filter
141
                 passfname = "%s.%s" % (infos['login'][2], infos['password'][1])
150
                 passfname = "%s.%s" % (infos['login'][2], infos['password'][1])
142
                 qfilter = qfilter.format(
151
                 qfilter = qfilter.format(
143
-                    passfname = passfname,
144
-                    passhash = password)
152
+                    passfname=passfname,
153
+                    passhash=password)
145
             getq = LeGetQuery(infos['login'][0], qfilter,
154
             getq = LeGetQuery(infos['login'][0], qfilter,
146
-                field_list = [uid_fname], limit = 1)
155
+                              field_list=[uid_fname], limit=1)
147
             req = getq.execute()
156
             req = getq.execute()
148
             if len(req) == 1:
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
                 break
159
                 break
151
         if self.is_anonymous():
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
     #@param token mixed : a session token
164
     #@param token mixed : a session token
156
-    #@return Session datas (a dict)
165
+    #@return Session data (a dict)
157
     #@throw ClientAuthenticationFailure if token is not valid or not
166
     #@throw ClientAuthenticationFailure if token is not valid or not
158
-    #existing
167
+    # existing
159
     @classmethod
168
     @classmethod
160
     def restore_session(cls, token):
169
     def restore_session(cls, token):
161
         cls._assert_instance()
170
         cls._assert_instance()
162
         if cls._instance.__session_token is not None:
171
         if cls._instance.__session_token is not None:
163
             raise ClientAuthenticationError("Trying to restore a session, but \
172
             raise ClientAuthenticationError("Trying to restore a session, but \
164
-a session is allready started !!!")
173
+a session is already started !!!")
165
         try:
174
         try:
166
-            cls._instance.__datas = SessionHandler.restore(token)
175
+            cls._instance.__data = SessionHandler.restore(token)
167
             cls._instance.__session_token = token
176
             cls._instance.__session_token = token
168
         except ClientAuthenticationFailure:
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
     #@return A session token or None
182
     #@return A session token or None
174
     @classmethod
183
     @classmethod
175
     def session_token(cls):
184
     def session_token(cls):
176
         cls._assert_instance()
185
         cls._assert_instance()
177
         return cls._instance.__session_token
186
         return cls._instance.__session_token
178
 
187
 
179
-   
180
-    ##@brief Delete current session
188
+    # @brief Deletes current session
181
     @classmethod
189
     @classmethod
182
     def destroy(cls):
190
     def destroy(cls):
183
         cls._assert_instance()
191
         cls._assert_instance()
184
         SessionHandler.destroy(cls._instance.__session_token)
192
         SessionHandler.destroy(cls._instance.__session_token)
185
         cls._instance.__session_token = None
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
     @classmethod
197
     @classmethod
190
     def clean(cls):
198
     def clean(cls):
191
         if cls._instance.__session_token is not None:
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
         if Client._instance is not None:
201
         if Client._instance is not None:
194
             del(Client._instance)
202
             del(Client._instance)
195
         Client._instance = None
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
     #@return True if client is anonymous
206
     #@return True if client is anonymous
199
     @classmethod
207
     @classmethod
200
     def is_anonymous(cls):
208
     def is_anonymous(cls):
201
         return Client._instance.user() is None
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
     #@throw ClientAuthenticationFailure
212
     #@throw ClientAuthenticationFailure
205
-    #@throw LodelFatalError if no Client child instance found
213
+    #@throw LodelFatalError if no Client child instance is found
206
     @classmethod
214
     @classmethod
207
     def authentication_failure(cls):
215
     def authentication_failure(cls):
208
         cls._generic_error(ClientAuthenticationFailure)
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
     #@throw ClientAuthenticationError
219
     #@throw ClientAuthenticationError
212
-    #@throw LodelFatalError if no Client child instance found
220
+    #@throw LodelFatalError if no Client child instance is found
213
     @classmethod
221
     @classmethod
214
-    def authentication_error(cls, msg = "Unknow error"):
222
+    def authentication_error(cls, msg="Unknow error"):
215
         cls._generic_error(ClientAuthenticationError, msg)
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
     #@throw ClientPermissionDenied
226
     #@throw ClientPermissionDenied
219
-    #@throw LodelFatalError if no Client child instance found
227
+    #@throw LodelFatalError if no Client child instance is found
220
     @classmethod
228
     @classmethod
221
-    def permission_denied_error(cls, msg = ""):
229
+    def permission_denied_error(cls, msg=""):
222
         cls._generic_error(ClientPermissionDenied, msg)
230
         cls._generic_error(ClientPermissionDenied, msg)
223
-    
224
-    ##@brief Generic error method
231
+
232
+    # @brief Generic error method
225
     #@see Client::authentication_failure() Client::authentication_error()
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
     @classmethod
236
     @classmethod
229
-    def _generic_error(cls, expt, msg = ""):
237
+    def _generic_error(cls, expt, msg=""):
230
         cls._assert_instance()
238
         cls._assert_instance()
231
         raise expt(Client._instance, msg)
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
     @classmethod
243
     @classmethod
236
     def _assert_instance(cls):
244
     def _assert_instance(cls):
237
         if Client._instance is None:
245
         if Client._instance is None:
238
             raise LodelFatalError("No client instance found. Abording.")
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
     @classmethod
252
     @classmethod
245
     def fetch_settings(cls):
253
     def fetch_settings(cls):
246
         LodelContext.expose_dyncode(globals(), 'dyncode')
254
         LodelContext.expose_dyncode(globals(), 'dyncode')
247
         if cls._infos_fields is None:
255
         if cls._infos_fields is None:
248
             cls._infos_fields = list()
256
             cls._infos_fields = list()
249
         else:
257
         else:
250
-            #Allready fetched
258
+            # Already fetched
251
             return
259
             return
252
         infos = (
260
         infos = (
253
             Settings.auth.login_classfield,
261
             Settings.auth.login_classfield,
266
                 if fdh.is_reference() and res_infos[1][0] in fdh.linked_classes():
274
                 if fdh.is_reference() and res_infos[1][0] in fdh.linked_classes():
267
                     link_field = fname
275
                     link_field = fname
268
             if link_field is None:
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
                 raise AuthenticationError("Unable to find a link between \
278
                 raise AuthenticationError("Unable to find a link between \
271
 login EmClass '%s' and password EmClass '%s'. Abording..." % (
279
 login EmClass '%s' and password EmClass '%s'. Abording..." % (
272
                     res_infos[0][0], res_infos[1][0]))
280
                     res_infos[0][0], res_infos[1][0]))
273
         res_infos[0] = (res_infos[0][0], res_infos[0][1], link_field)
281
         res_infos[0] = (res_infos[0][0], res_infos[0][1], link_field)
274
         cls._infos_fields.append(
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
     #@param leo LeObject child class : the LeObject the user is stored in
286
     #@param leo LeObject child class : the LeObject the user is stored in
279
     #@param uid str : uniq id (in leo)
287
     #@param uid str : uniq id (in leo)
280
     #@return None
288
     #@return None
281
     @classmethod
289
     @classmethod
282
     def __set_authenticated(cls, leo, uid):
290
     def __set_authenticated(cls, leo, uid):
283
         cls._instance.__user = {'classname': leo.__name__, 'uid': uid, 'leoclass': leo}
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 查看文件

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

+ 8
- 1
lodel/editorial_model/exceptions.py 查看文件

1
 #-*- coding: utf-8 -*-
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
 class EditorialModelError(Exception):
8
 class EditorialModelError(Exception):
4
     pass
9
     pass
5
 
10
 
6
 
11
 
12
+## @brief Tries to import the settings module.
13
+# @raise EditorialModelError
7
 def assert_edit():
14
 def assert_edit():
8
     try:
15
     try:
9
         from lodel import Settings
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
         return
18
         return
12
     if not Settings.editorialmodel.editormode:
19
     if not Settings.editorialmodel.editormode:
13
         raise EditorialModelError("EM is readonly : editormode is OFF")
20
         raise EditorialModelError("EM is readonly : editormode is OFF")

+ 12
- 6
lodel/editorial_model/model.py 查看文件

42
         super().__init__(display_name, help_text)
42
         super().__init__(display_name, help_text)
43
 
43
 
44
     # @brief EmClass uids accessor
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
     def all_classes(self, uid=None):
47
     def all_classes(self, uid=None):
47
         if uid is None:
48
         if uid is None:
48
             return copy.copy(self.__classes)
49
             return copy.copy(self.__classes)
52
             except KeyError:
53
             except KeyError:
53
                 raise EditorialModelException("EmClass not found : '%s'" % uid)
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
     def all_classes_ref(self, uid=None):
59
     def all_classes_ref(self, uid=None):
56
         if uid is None:
60
         if uid is None:
57
             return self.__classes
61
             return self.__classes
62
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
66
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
63
 
67
 
64
     # @brief active EmClass uids accessor
68
     # @brief active EmClass uids accessor
65
-    #@return a list of class uids
69
+    #@return a list of active class uids
66
     def active_classes_uids(self):
70
     def active_classes_uids(self):
67
         return list(self.__active_classes.keys())
71
         return list(self.__active_classes.keys())
68
 
72
 
69
     # @brief EmGroups accessor
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
     def all_groups(self, uid=None):
76
     def all_groups(self, uid=None):
72
         if uid is None:
77
         if uid is None:
73
             return copy.copy(self.__groups)
78
             return copy.copy(self.__groups)
78
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
83
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
79
 
84
 
80
     # @brief EmGroups accessor
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
     def all_groups_ref(self, uid=None):
88
     def all_groups_ref(self, uid=None):
83
         if uid is None:
89
         if uid is None:
84
             return self.__groups
90
             return self.__groups
89
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
95
                 raise EditorialModelException("EmGroup not found : '%s'" % uid)
90
 
96
 
91
     # @brief active EmClass uids accessor
97
     # @brief active EmClass uids accessor
92
-    #@return a list of class uids
98
+    #@return a list of active group uids
93
     def active_groups_uids(self):
99
     def active_groups_uids(self):
94
         return list(self.__active_groups.keys())
100
         return list(self.__active_groups.keys())
95
 
101
 
97
     #@param uid None | str : give this argument to get a specific EmClass
103
     #@param uid None | str : give this argument to get a specific EmClass
98
     #@return if uid is given returns an EmClass else returns an EmClass
104
     #@return if uid is given returns an EmClass else returns an EmClass
99
     # iterator
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
     # be returned
107
     # be returned
102
     def classes(self, uid=None):
108
     def classes(self, uid=None):
103
         try:
109
         try:

+ 2
- 2
lodel/editorial_model/translator/__init__.py 查看文件

1
 ## @package lodel.editorial_model.translator Editorial model translators
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 查看文件

11
         'EmGroup'],
11
         'EmGroup'],
12
     'lodel.utils.mlstring': ['MlString']})
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
 # <ul>
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
 # @param model EditorialModel : the model to save
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
 def save(model, **kwargs):
84
 def save(model, **kwargs):
72
     Em = etree.Element("editorial_model")
85
     Em = etree.Element("editorial_model")
73
     em_name = etree.SubElement(Em, 'name')
86
     em_name = etree.SubElement(Em, 'name')
100
         outfile.close()
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
 def write_mlstring_xml(etree, elem, mlstr):
120
 def write_mlstring_xml(etree, elem, mlstr):
108
     for lang in mlstr.values:
121
     for lang in mlstr.values:
109
         ss_mlstr = etree.SubElement(elem,lang)
122
         ss_mlstr = etree.SubElement(elem,lang)
110
         ss_mlstr.text = mlstr.get(lang)
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
 # @param etree : the xml object
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
 # @param kwargs : the options of the datahandler
129
 # @param kwargs : the options of the datahandler
117
 def write_datahandler_xml(etree, elem, dhdl_name, **kwargs):
130
 def write_datahandler_xml(etree, elem, dhdl_name, **kwargs):
118
     dhdl = etree.SubElement(elem,'datahandler_name')
131
     dhdl = etree.SubElement(elem,'datahandler_name')
138
                     opt_val = str(argu)
151
                     opt_val = str(argu)
139
         arg.text = opt_val
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
 # @param etree : the xml object
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
 def write_emfield_xml(etree, elem, uid, name, help_text, group, datahandler_name, **kwargs):
163
 def write_emfield_xml(etree, elem, uid, name, help_text, group, datahandler_name, **kwargs):
151
     emfield = etree.SubElement(elem,'field')
164
     emfield = etree.SubElement(elem,'field')
152
     emfield_uid = etree.SubElement(emfield, 'uid')
165
     emfield_uid = etree.SubElement(emfield, 'uid')
169
 
182
 
170
 ##@brief Writes a representation of a EmGroup in xml
183
 ##@brief Writes a representation of a EmGroup in xml
171
 # @param etree : the xml object
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
 def write_emgroup_xml(etree, elem, uid, name, help_text, requires, components):
191
 def write_emgroup_xml(etree, elem, uid, name, help_text, requires, components):
177
     emgroup = etree.SubElement(elem, 'group')
192
     emgroup = etree.SubElement(elem, 'group')
178
     emgroup_uid = etree.SubElement(emgroup, 'uid')
193
     emgroup_uid = etree.SubElement(emgroup, 'uid')
204
             em_group_comp_cls_ins = etree.SubElement(emgroup_comp_cls, 'emclass')
219
             em_group_comp_cls_ins = etree.SubElement(emgroup_comp_cls, 'emclass')
205
             em_group_comp_cls_ins.text = component.uid
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
 def write_emclass_xml(etree, elem, uid, name, help_text, group, fields, parents, abstract = False, pure_abstract = False):
232
 def write_emclass_xml(etree, elem, uid, name, help_text, group, fields, parents, abstract = False, pure_abstract = False):
217
     emclass = etree.SubElement(elem, 'class')
233
     emclass = etree.SubElement(elem, 'class')
218
     emclass_uid  = etree.SubElement(emclass, 'uid')
234
     emclass_uid  = etree.SubElement(emclass, 'uid')
244
     emclass_parents = etree.SubElement(emclass, 'parents')
260
     emclass_parents = etree.SubElement(emclass, 'parents')
245
     emclass_parents.text = ",".join(parents_list)
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
 # @return a new EditorialModel object
265
 # @return a new EditorialModel object
250
 def load(filename):
266
 def load(filename):
251
-
252
     Em = etree.parse(filename)
267
     Em = etree.parse(filename)
253
     emodel = Em.getroot()
268
     emodel = Em.getroot()
254
     name = emodel.find('name')
269
     name = emodel.find('name')
270
             grp = model.add_group(grp)
285
             grp = model.add_group(grp)
271
     return model
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
 # @return a new EmClass object
292
 # @return a new EmClass object
277
 def load_class_xml(model, elem):
293
 def load_class_xml(model, elem):
278
     uid = elem.find('uid').text
294
     uid = elem.find('uid').text
332
             
348
             
333
     return emclass
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
 def load_field_xml(model, elem, emclass):
356
 def load_field_xml(model, elem, emclass):
341
     uid = elem.find('uid').text
357
     uid = elem.find('uid').text
342
     if elem.find('display_name').text is None:
358
     if elem.find('display_name').text is None:
369
     
385
     
370
     return emfield
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
 def load_dhdl_options_xml(model, elem):
393
 def load_dhdl_options_xml(model, elem):
377
     dhdl_options=dict()
394
     dhdl_options=dict()
378
     for opt in elem:
395
     for opt in elem:
396
     return dhdl_options
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
 def load_group_xml(model, elem):
420
 def load_group_xml(model, elem):
404
     uid = elem.find('uid')
421
     uid = elem.find('uid')
405
     
422
     
443
         group = model.all_groups_ref(uid.text)
460
         group = model.all_groups_ref(uid.text)
444
         group.display_name = name
461
         group.display_name = name
445
         group.help_text = help_text
462
         group.help_text = help_text
446
-        group.add_dependencie(requires)
463
+        group.add_dependency(requires)
447
     else:
464
     else:
448
         group = EmGroup(uid.text, requires, name, help_text)
465
         group = EmGroup(uid.text, requires, name, help_text)
449
 
466
 
450
     group.add_components(comp)     
467
     group.add_components(comp)     
451
     return group
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
 def load_mlstring_xml(elem):
473
 def load_mlstring_xml(elem):
458
     mlstr = dict()
474
     mlstr = dict()
459
     for lang in elem:
475
     for lang in elem:

+ 75
- 24
lodel/leapi/datahandlers/datas.py 查看文件

1
 #-*- coding: utf-8 -*-
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
 import warnings
7
 import warnings
3
 import inspect
8
 import inspect
4
 import re
9
 import re
12
                          'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
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
 class FormatString(Varchar):
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
     base_type = 'char'
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
     def __init__(self, format_string, field_list, **kwargs):
30
     def __init__(self, format_string, field_list, **kwargs):
27
         self._field_list = field_list
31
         self._field_list = field_list
28
         self._format_string = format_string
32
         self._format_string = format_string
29
         super().__init__(internal='automatic', **kwargs)
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
     def _construct_data(self, emcomponent, fname, datas, cur_value):
43
     def _construct_data(self, emcomponent, fname, datas, cur_value):
32
         ret = self._format_string % tuple(
44
         ret = self._format_string % tuple(
33
             datas[fname] for fname in self._field_list)
45
             datas[fname] for fname in self._field_list)
35
             warnings.warn("Format field overflow. Truncating value")
47
             warnings.warn("Format field overflow. Truncating value")
36
             ret = ret[:self.max_length]
48
             ret = ret[:self.max_length]
37
         return ret
49
         return ret
38
-    
39
-##@brief Varchar validated by a regex
50
+
51
+
52
+## @brief Varchar validated by a regex
40
 class Regex(Varchar):
53
 class Regex(Varchar):
41
 
54
 
42
     help = 'String field validated with a regex. Takes two options : \
55
     help = 'String field validated with a regex. Takes two options : \
43
 max_length and regex'
56
 max_length and regex'
44
     base_type = 'char'
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
     # @param max_length int : the max length for this field (default : 10)
61
     # @param max_length int : the max length for this field (default : 10)
49
-    # @param **kwargs
62
+    # @param **kwargs : additional options
50
     def __init__(self, regex='', max_length=10, **kwargs):
63
     def __init__(self, regex='', max_length=10, **kwargs):
51
         self.regex = regex
64
         self.regex = regex
52
         self.compiled_re = re.compile(regex)  # trigger an error if invalid regex
65
         self.compiled_re = re.compile(regex)  # trigger an error if invalid regex
53
         super(self.__class__, self).__init__(max_length=max_length, **kwargs)
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
     def _check_data_value(self, value):
72
     def _check_data_value(self, value):
60
         value = super()._check_data_value(value)
73
         value = super()._check_data_value(value)
61
         if not self.compiled_re.match(value) or len(value) > self.max_length:
74
         if not self.compiled_re.match(value) or len(value) > self.max_length:
63
             raise FieldValidationError(msg)
76
             raise FieldValidationError(msg)
64
         return value
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
     def can_override(self, data_handler):
83
     def can_override(self, data_handler):
67
         if not super().can_override(data_handler):
84
         if not super().can_override(data_handler):
68
             return False
85
             return False
71
             return False
88
             return False
72
         return True
89
         return True
73
 
90
 
91
+
74
 ##@brief Handles uniq ID
92
 ##@brief Handles uniq ID
75
 class UniqID(Integer):
93
 class UniqID(Integer):
76
 
94
 
77
     help = 'Fieldtype designed to handle editorial model UID'
95
     help = 'Fieldtype designed to handle editorial model UID'
78
     base_type = 'int'
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
     def __init__(self, **kwargs):
101
     def __init__(self, **kwargs):
83
         kwargs['internal'] = 'automatic'
102
         kwargs['internal'] = 'automatic'
84
         super(self.__class__, self).__init__(primary_key = True, **kwargs)
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
     def construct_data(self, emcomponent, fname, datas, cur_value):
112
     def construct_data(self, emcomponent, fname, datas, cur_value):
87
         if cur_value is None:
113
         if cur_value is None:
88
             #Ask datasource to provide a new uniqID
114
             #Ask datasource to provide a new uniqID
89
             return emcomponent._ro_datasource.new_numeric_id(emcomponent)
115
             return emcomponent._ro_datasource.new_numeric_id(emcomponent)
90
         return cur_value
116
         return cur_value
91
 
117
 
118
+
119
+## @brief Class representing a LeObject subclass
92
 class LeobjectSubclassIdentifier(Varchar):
120
 class LeobjectSubclassIdentifier(Varchar):
93
     
121
     
94
     help = 'Datahandler designed to handle LeObject subclass identifier in DB'
122
     help = 'Datahandler designed to handle LeObject subclass identifier in DB'
95
     base_type = 'varchar'
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
     def __init__(self, **kwargs):
129
     def __init__(self, **kwargs):
98
         if 'internal' in kwargs and not kwargs['internal']:
130
         if 'internal' in kwargs and not kwargs['internal']:
99
             raise RuntimeError(self.__class__.__name__+" datahandler can only \
131
             raise RuntimeError(self.__class__.__name__+" datahandler can only \
100
 be internal")
132
 be internal")
101
         kwargs['internal'] = True
133
         kwargs['internal'] = True
102
         super().__init__(**kwargs)
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
     def construct_data(self, emcomponent, fname, datas, cur_value):
143
     def construct_data(self, emcomponent, fname, datas, cur_value):
105
         cls = emcomponent
144
         cls = emcomponent
106
         if not inspect.isclass(emcomponent):
145
         if not inspect.isclass(emcomponent):
108
         return cls.__name__
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
 class Concat(FormatString):
151
 class Concat(FormatString):
113
     help = 'Automatic strings concatenation'
152
     help = 'Automatic strings concatenation'
114
     base_type = 'char'
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
     # @param separator str
157
     # @param separator str
119
     # @param **kwargs    
158
     # @param **kwargs    
120
     def __init__(self, field_list, separator=' ', **kwargs):
159
     def __init__(self, field_list, separator=' ', **kwargs):
124
                          **kwargs)
163
                          **kwargs)
125
 
164
 
126
 
165
 
166
+## @brief Datahandler managing a password
127
 class Password(Varchar):
167
 class Password(Varchar):
128
     help = 'Handle passwords'
168
     help = 'Handle passwords'
129
     base_type = 'password'
169
     base_type = 'password'
130
     pass
170
     pass
131
 
171
 
132
-
172
+## @brief Datahandler turning a string into a list
133
 class VarcharList(Varchar):
173
 class VarcharList(Varchar):
134
     help = 'DataHandler designed to make a list out of a string.'
174
     help = 'DataHandler designed to make a list out of a string.'
135
     base_type = 'varchar'
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
     def __init__(self, delimiter=' ', **kwargs):
181
     def __init__(self, delimiter=' ', **kwargs):
138
         if not isinstance(delimiter, str):
182
         if not isinstance(delimiter, str):
139
             raise LodelException("The delimiter has to be a string, %s given" % type(delimiter))
183
             raise LodelException("The delimiter has to be a string, %s given" % type(delimiter))
140
         self.delimiter = str(delimiter)
184
         self.delimiter = str(delimiter)
141
         super().__init__(**kwargs)
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
     def construct_data(self, emcomponent, fname, datas, cur_value):
194
     def construct_data(self, emcomponent, fname, datas, cur_value):
144
         result = cur_value.split(self.delimiter)
195
         result = cur_value.split(self.delimiter)
145
         return result
196
         return result

+ 2
- 0
lodel/leapi/datahandlers/exceptions.py 查看文件

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

+ 6
- 2
lodel/leapi/datahandlers/references.py 查看文件

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

+ 5
- 4
lodel/leapi/exceptions.py 查看文件

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

+ 83
- 64
lodel/leapi/lefactory.py 查看文件

1
 #-*- coding: utf-8 -*-
1
 #-*- coding: utf-8 -*-
2
 
2
 
3
-import os, os.path
3
+import os
4
+import os.path
4
 import functools
5
 import functools
5
 
6
 
6
 from lodel.context import LodelContext
7
 from lodel.context import LodelContext
7
 LodelContext.expose_modules(globals(), {
8
 LodelContext.expose_modules(globals(), {
8
     'lodel.editorial_model.components': ['EmComponent', 'EmClass', 'EmField',
9
     'lodel.editorial_model.components': ['EmComponent', 'EmClass', 'EmField',
9
-        'EmGroup'],
10
+                                         'EmGroup'],
10
     'lodel.leapi.leobject': ['LeObject'],
11
     'lodel.leapi.leobject': ['LeObject'],
11
     'lodel.leapi.datahandlers.base_classes': ['DataHandler'],
12
     'lodel.leapi.datahandlers.base_classes': ['DataHandler'],
12
     'lodel.logger': 'logger'})
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
 # @param model lodel.editorial_model.model.EditorialModel
16
 # @param model lodel.editorial_model.model.EditorialModel
17
+
18
+
16
 def dyncode_from_em(model):
19
 def dyncode_from_em(model):
17
-    
20
+
18
     # Generation of LeObject child classes code
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
     # Header
24
     # Header
22
     imports = """from lodel.context import LodelContext
25
     imports = """from lodel.context import LodelContext
25
     'lodel.leapi.datahandlers.base_classes': ['DataField'],
28
     'lodel.leapi.datahandlers.base_classes': ['DataField'],
26
     'lodel.plugin.hooks': ['LodelHook']})
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
     # formating all components of output
34
     # formating all components of output
34
     res_code = """#-*- coding: utf-8 -*-
35
     res_code = """#-*- coding: utf-8 -*-
41
 dynclasses_dict = {class_dict}
42
 dynclasses_dict = {class_dict}
42
 {common_code}
43
 {common_code}
43
 """.format(
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
     return res_code
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
 def common_code():
60
 def common_code():
56
     res = ""
61
     res = ""
57
     fname = os.path.dirname(__file__)
62
     fname = os.path.dirname(__file__)
61
             if not line.startswith('#-'):
66
             if not line.startswith('#-'):
62
                 res += line
67
                 res += line
63
     return res
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
 # @return a list of EmClass instances
75
 # @return a list of EmClass instances
70
 def emclass_sorted_by_deps(emclass_list):
76
 def emclass_sorted_by_deps(emclass_list):
71
     def emclass_deps_cmp(cls_a, cls_b):
77
     def emclass_deps_cmp(cls_a, cls_b):
72
         return len(cls_a.parents_recc) - len(cls_b.parents_recc)
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
     return ret
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
 def get_classes(model):
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
 def data_handler_constructor(emfield):
95
 def data_handler_constructor(emfield):
82
     #dh_module_name = DataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
96
     #dh_module_name = DataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
83
     get_handler_class_instr = 'DataField.from_name(%s)' % repr(emfield.data_handler_name)
97
     get_handler_class_instr = 'DataField.from_name(%s)' % repr(emfield.data_handler_name)
85
     for name, val in emfield.data_handler_options.items():
99
     for name, val in emfield.data_handler_options.items():
86
         if name == 'back_reference' and isinstance(val, tuple):
100
         if name == 'back_reference' and isinstance(val, tuple):
87
             options.append('{optname}: ({leo_name}, {fieldname})'.format(
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
         else:
105
         else:
92
-            options.append(repr(name)+': '+forge_optval(val))
106
+            options.append(repr(name) + ': ' + forge_optval(val))
93
 
107
 
94
     return '{handler_instr}(**{{ {options} }})'.format(
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
 def forge_optval(optval):
117
 def forge_optval(optval):
100
     if isinstance(optval, dict):
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
     if isinstance(optval, (set, list, tuple)):
121
     if isinstance(optval, (set, list, tuple)):
104
         return '[' + (', '.join([forge_optval(val) for val in optval])) + ']'
122
         return '[' + (', '.join([forge_optval(val) for val in optval])) + ']'
105
-        
123
+
106
     if isinstance(optval, EmField):
124
     if isinstance(optval, EmField):
107
         return "{leobject}.data_handler({fieldname})".format(
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
         return LeObject.name2objname(optval.uid)
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
 # @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
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
 def generate_classes(model):
139
 def generate_classes(model):
121
     res = ""
140
     res = ""
122
-    imports = list()
141
+
123
     bootstrap = ""
142
     bootstrap = ""
124
     # Generating field list for LeObjects generated from EmClass
143
     # Generating field list for LeObjects generated from EmClass
125
     for em_class in get_classes(model):
144
     for em_class in get_classes(model):
126
         logger.info("Generating a dynamic class for %s" % em_class.uid)
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
         for field in em_class.fields():
149
         for field in em_class.fields():
131
             if field.data_handler_instance.is_primary_key():
150
             if field.data_handler_instance.is_primary_key():
132
                 uid.append(field.uid)
151
                 uid.append(field.uid)
133
-        # Determine parent for inheritance
152
+        # Determines parentsfor inheritance
134
         if len(em_class.parents) > 0:
153
         if len(em_class.parents) > 0:
135
             for parent in em_class.parents:
154
             for parent in em_class.parents:
136
-               parents.append(LeObject.name2objname(parent.uid))
155
+                parents.append(LeObject.name2objname(parent.uid))
137
         else:
156
         else:
138
             parents.append('LeObject')
157
             parents.append('LeObject')
139
         datasource_name = em_class.datasource
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
         em_cls_code = """
161
         em_cls_code = """
143
 class {clsname}({parents}):
162
 class {clsname}({parents}):
144
     _abstract = {abstract}
163
     _abstract = {abstract}
150
     _child_classes = None
169
     _child_classes = None
151
 
170
 
152
 """.format(
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
         res += em_cls_code
178
         res += em_cls_code
160
         # Dyncode fields bootstrap instructions
179
         # Dyncode fields bootstrap instructions
161
         child_classes = model.get_class_childs(em_class.uid)
180
         child_classes = model.get_class_childs(em_class.uid)
163
             child_classes = 'tuple()'
182
             child_classes = 'tuple()'
164
         else:
183
         else:
165
             child_classes = '(%s,)' % (', '.join(
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
         bootstrap += """{classname}._set__fields({fields})
186
         bootstrap += """{classname}._set__fields({fields})
168
 {classname}._child_classes = {child_classes}
187
 {classname}._child_classes = {child_classes}
169
 """.format(
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
     bootstrap += "\n"
194
     bootstrap += "\n"
175
-    return res, set(imports), bootstrap
176
-    
195
+    return res, bootstrap

+ 3
- 4
lodel/leapi/lefactory_common.py 查看文件

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

+ 119
- 105
lodel/leapi/leobject.py 查看文件

1
 #-*- coding: utf-8 -*-
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
 import importlib
7
 import importlib
4
 import warnings
8
 import warnings
5
 import copy
9
 import copy
22
     'lodel.plugin': ['Plugin', 'DatasourcePlugin'],
26
     'lodel.plugin': ['Plugin', 'DatasourcePlugin'],
23
     'lodel.leapi.datahandlers.base_classes': ['DatasConstructor', 'Reference']})
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
 CLASS_ID_FIELDNAME = "classname"
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
 # @note Wrapped methods are : LeObject.data() & LeObject.set_data()
36
 # @note Wrapped methods are : LeObject.data() & LeObject.set_data()
34
-
35
-
36
 class LeObjectValues(object):
37
 class LeObjectValues(object):
37
 
38
 
38
-    # @brief Construct a new LeObjectValues
39
     # @param fieldnames_callback method
39
     # @param fieldnames_callback method
40
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
40
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
41
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
41
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
43
         self._setter = set_callback
43
         self._setter = set_callback
44
         self._getter = get_callback
44
         self._getter = get_callback
45
 
45
 
46
-    # @brief Provide read access to datas values
46
+    ## @brief Provides read access to datas values
47
     # @note Read access should be provided for all fields
47
     # @note Read access should be provided for all fields
48
     # @param fname str : Field name
48
     # @param fname str : Field name
49
+    # @return method
49
     def __getattribute__(self, fname):
50
     def __getattribute__(self, fname):
50
         getter = super().__getattribute__('_getter')
51
         getter = super().__getattribute__('_getter')
51
         return getter(fname)
52
         return getter(fname)
52
 
53
 
53
-    # @brief Provide write access to datas values
54
+    ## @brief Provides write access to datas values
54
     # @note Write acces shouldn't be provided for internal or immutable fields
55
     # @note Write acces shouldn't be provided for internal or immutable fields
55
     # @param fname str : Field name
56
     # @param fname str : Field name
56
     # @param fval * : the field value
57
     # @param fval * : the field value
58
+    # @return method
57
     def __setattribute__(self, fname, fval):
59
     def __setattribute__(self, fname, fval):
58
         setter = super().__getattribute__('_setter')
60
         setter = super().__getattribute__('_setter')
59
         return setter(fname, fval)
61
         return setter(fname, fval)
60
 
62
 
61
 
63
 
64
+## @brief Represents a handled object in Lodel.
62
 class LeObject(object):
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
     _abstract = None
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
     _fields = None
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
     _uid = None
72
     _uid = None
70
-    # @brief Read only datasource ( see @ref lodel2_datasources )
73
+    ## @brief Read only datasource ( see @ref lodel2_datasources )
71
     _ro_datasource = None
74
     _ro_datasource = None
72
-    # @brief Read & write datasource ( see @ref lodel2_datasources )
75
+    ## @brief Read & write datasource ( see @ref lodel2_datasources )
73
     _rw_datasource = None
76
     _rw_datasource = None
74
-    # @brief Store the list of child classes
77
+    ## @brief Store the list of child classes
75
     _child_classes = None
78
     _child_classes = None
76
-    # @brief Name of the datasource plugin
79
+    ## @brief Name of the datasource plugin
77
     _datasource_name = None
80
     _datasource_name = None
78
 
81
 
79
     def __new__(cls, **kwargs):
82
     def __new__(cls, **kwargs):
80
-
81
         self = object.__new__(cls)
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
         self.__datas = {fname: None for fname in self._fields}
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
         self.__initialized = list()
87
         self.__initialized = list()
86
-        # @brief Datas accessor. Instance of @ref LeObjectValues
88
+        ## @brief Datas accessor. Instance of @ref LeObjectValues
87
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
89
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
88
         for fieldname, fieldval in kwargs.items():
90
         for fieldname, fieldval in kwargs.items():
89
             self.__datas[fieldname] = fieldval
91
             self.__datas[fieldname] = fieldval
92
         self.__set_initialized()
94
         self.__set_initialized()
93
         return self
95
         return self
94
 
96
 
95
-    # @brief Construct an object representing an Editorial component
96
     # @note Can be considered as EmClass instance
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
     def __init__(self, **kwargs):
101
     def __init__(self, **kwargs):
98
         if self._abstract:
102
         if self._abstract:
99
             raise NotImplementedError(
103
             raise NotImplementedError(
130
     #   Fields datas handling methods   #
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
     @property
139
     @property
135
     def initialized(self):
140
     def initialized(self):
136
         return self.__is_initialized
141
         return self.__is_initialized
137
 
142
 
138
-    # @return The uid field name
143
+    ## @brief Returns the uid field name
144
+    # @return str
139
     @classmethod
145
     @classmethod
140
     def uid_fieldname(cls):
146
     def uid_fieldname(cls):
141
         return cls._uid
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
     @classmethod
152
     @classmethod
147
     def fieldnames(cls, include_ro=False):
153
     def fieldnames(cls, include_ro=False):
148
         if not include_ro:
154
         if not include_ro:
150
         else:
156
         else:
151
             return list(cls._fields.keys())
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
     @classmethod
162
     @classmethod
154
     def name2objname(cls, name):
163
     def name2objname(cls, name):
155
         return name.title()
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
     # @return A data handler instance
168
     # @return A data handler instance
169
+    # @throw NameError when the given field name doesn't exist
160
     #@todo update class of exception raised
170
     #@todo update class of exception raised
161
     @classmethod
171
     @classmethod
162
     def data_handler(cls, fieldname):
172
     def data_handler(cls, fieldname):
164
             raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
174
             raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
165
         return cls._fields[fieldname]
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
     @classmethod
180
     @classmethod
171
     def reference_handlers(cls, with_backref=True):
181
     def reference_handlers(cls, with_backref=True):
172
         return {fname: fdh
182
         return {fname: fdh
174
                 if fdh.is_reference() and
184
                 if fdh.is_reference() and
175
                 (not with_backref or fdh.back_reference is not None)}
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
     # @warning This method has to be called from dynamically generated LeObjects
188
     # @warning This method has to be called from dynamically generated LeObjects
179
     # @param leobject_name str : LeObject name
189
     # @param leobject_name str : LeObject name
180
     # @return A LeObject child class
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
     @classmethod
193
     @classmethod
183
     def name2class(cls, leobject_name):
194
     def name2class(cls, leobject_name):
184
         if cls.__module__ == 'lodel.leapi.leobject':
195
         if cls.__module__ == 'lodel.leapi.leobject':
189
         except (AttributeError, TypeError):
200
         except (AttributeError, TypeError):
190
             raise LeApiError("No LeObject named '%s'" % leobject_name)
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
     @classmethod
205
     @classmethod
193
     def is_abstract(cls):
206
     def is_abstract(cls):
194
         return cls._abstract
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
     @classmethod
213
     @classmethod
201
     def field(cls, fieldname):
214
     def field(cls, fieldname):
202
         try:
215
         try:
204
         except KeyError:
217
         except KeyError:
205
             raise NameError("No field named '%s' in %s" % (fieldname,
218
             raise NameError("No field named '%s' in %s" % (fieldname,
206
                                                            cls.__name__))
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
     @classmethod
223
     @classmethod
210
     def fields(cls, include_ro=False):
224
     def fields(cls, include_ro=False):
211
         if include_ro:
225
         if include_ro:
214
             return {fname: cls._fields[fname] for fname in cls._fields\
228
             return {fname: cls._fields[fname] for fname in cls._fields\
215
                     if not cls._fields[fname].is_internal()}
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
     @classmethod
237
     @classmethod
226
     def hierarch(cls):
238
     def hierarch(cls):
227
         res = [cls]
239
         res = [cls]
234
                 res.append(cur)
246
                 res.append(cur)
235
         return res
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
     @classmethod
251
     @classmethod
240
     def child_classes(cls):
252
     def child_classes(cls):
241
         return copy.copy(cls._child_classes)
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
     @classmethod
258
     @classmethod
248
     def uid_source(cls):
259
     def uid_source(cls):
249
         if cls._uid is None or len(cls._uid) == 0:
260
         if cls._uid is None or len(cls._uid) == 0:
259
             prev = pcls
270
             prev = pcls
260
         return prev
271
         return prev
261
 
272
 
262
-    # @brief Initialise both datasources (ro and rw)
273
+    ## @brief Initialise both datasources (ro and rw)
263
     #
274
     #
264
     # This method is used once at dyncode load to replace the datasource string
275
     # This method is used once at dyncode load to replace the datasource string
265
     # by a datasource instance to avoid doing this operation for each query
276
     # by a datasource instance to avoid doing this operation for each query
266
-    #@see LeObject::_init_datasource()
277
+    # @see LeObject::_init_datasource()
267
     @classmethod
278
     @classmethod
268
     def _init_datasources(cls):
279
     def _init_datasources(cls):
269
         if isinstance(cls._datasource_name, str):
280
         if isinstance(cls._datasource_name, str):
291
             log_msg %= (ro_ds, cls.__name__)
302
             log_msg %= (ro_ds, cls.__name__)
292
             logger.debug(log_msg)
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
     def uid(self):
308
     def uid(self):
298
         return self.data(self._uid[0])
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
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
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
     # @throw RuntimeError if the field is not initialized yet
315
     # @throw RuntimeError if the field is not initialized yet
305
     # @throw NameError if name is not an existing field name
316
     # @throw NameError if name is not an existing field name
306
     def data(self, field_name):
317
     def data(self, field_name):
311
                 "The field %s is not initialized yet (and have no value)" % field_name)
322
                 "The field %s is not initialized yet (and have no value)" % field_name)
312
         return self.__datas[field_name]
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
     def datas(self, internal=False):
327
     def datas(self, internal=False):
317
         return {fname: self.data(fname) for fname in self.fieldnames(internal)}
328
         return {fname: self.data(fname) for fname in self.fieldnames(internal)}
318
 
329
 
319
-    # @brief Datas setter
330
+    ## @brief Datas setter
320
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
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
     # @param fval * : field value
333
     # @param fval * : field value
323
     # @return the value that is really set
334
     # @return the value that is really set
324
     # @throw NameError if fname is not valid
335
     # @throw NameError if fname is not valid
325
     # @throw AttributeError if the field is not writtable
336
     # @throw AttributeError if the field is not writtable
337
+    # @throw LeApiErrors if the data check generates an error
326
     def set_data(self, fname, fval):
338
     def set_data(self, fname, fval):
327
         if fname not in self.fieldnames(include_ro=False):
339
         if fname not in self.fieldnames(include_ro=False):
328
             if fname not in self._fields.keys():
340
             if fname not in self._fields.keys():
353
             else:
365
             else:
354
                 self.__datas[fname] = val
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
     def __set_initialized(self):
371
     def __set_initialized(self):
360
         if isinstance(self.__initialized, list):
372
         if isinstance(self.__initialized, list):
361
             expected_fields = self.fieldnames(include_ro=False) + self._uid
373
             expected_fields = self.fieldnames(include_ro=False) + self._uid
362
             if set(expected_fields) == set(self.__initialized):
374
             if set(expected_fields) == set(self.__initialized):
363
                 self.__is_initialized = True
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
     # @return None if checks succeded else return an exception list
380
     # @return None if checks succeded else return an exception list
369
     def __check_modified_values(self):
381
     def __check_modified_values(self):
370
         err_list = dict()
382
         err_list = dict()
409
     #   Other methods    #
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
     # This method is used in the generated dynamic code to set the _fields attribute
426
     # This method is used in the generated dynamic code to set the _fields attribute
415
     # at the end of the dyncode parse
427
     # at the end of the dyncode parse
416
     # @warning This method is deleted once the dynamic code loaded
428
     # @warning This method is deleted once the dynamic code loaded
417
-    # @param field_list list : list of EmField instance
418
     # @param cls
429
     # @param cls
430
+    # @param field_list list : list of EmField instance
419
     @classmethod
431
     @classmethod
420
     def _set__fields(cls, field_list):
432
     def _set__fields(cls, field_list):
421
         cls._fields = field_list
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
     # @param datas dict : key == field name value are field values
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
     # @param cls
439
     # @param cls
428
     # @return Checked datas
440
     # @return Checked datas
429
-    # @throw LeApiDataCheckError if errors reported during check
441
+    # @throw LeApiDataCheckErrors if errors are reported during check
430
     @classmethod
442
     @classmethod
431
     def check_datas_value(cls, datas, complete=False, allow_internal=True):
443
     def check_datas_value(cls, datas, complete=False, allow_internal=True):
432
         err_l = dict()  # Error storing
444
         err_l = dict()  # Error storing
459
             raise LeApiDataCheckErrors("Error while checking datas", err_l)
471
             raise LeApiDataCheckErrors("Error while checking datas", err_l)
460
         return checked_datas
472
         return checked_datas
461
 
473
 
462
-    # @brief Check and prepare datas
474
+    ## @brief Checks and prepares all the data
463
     #
475
     #
464
     # @warning when complete = False we are not able to make construct_datas() and _check_data_consistency()
476
     # @warning when complete = False we are not able to make construct_datas() and _check_data_consistency()
465
     #
477
     #
466
     # @param datas dict : {fieldname : fieldvalue, ...}
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
     # @return Datas ready for use
481
     # @return Datas ready for use
471
     # @todo: complete is very unsafe, find a way to get rid of it
482
     # @todo: complete is very unsafe, find a way to get rid of it
472
     @classmethod
483
     @classmethod
482
             cls._check_datas_consistency(ret_datas)
493
             cls._check_datas_consistency(ret_datas)
483
         return ret_datas
494
         return ret_datas
484
 
495
 
485
-    # @brief Construct datas values
496
+    ## @brief Constructs datas values
486
     #
497
     #
487
-    # @param cls
488
     # @param datas dict : Datas that have been returned by LeCrud.check_datas_value() methods
498
     # @param datas dict : Datas that have been returned by LeCrud.check_datas_value() methods
489
     # @return A new dict of datas
499
     # @return A new dict of datas
490
     # @todo IMPLEMENTATION
500
     # @todo IMPLEMENTATION
498
         }
508
         }
499
         return ret
509
         return ret
500
 
510
 
501
-    # @brief Check datas consistency
511
+    ## @brief Checks datas consistency
502
512
503
     # @warning assert that datas is complete
513
     # @warning assert that datas is complete
504
     # @param cls
514
     # @param cls
505
     # @param datas dict : Datas that have been returned by LeCrud._construct_datas() method
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
     @classmethod
517
     @classmethod
508
     def _check_datas_consistency(cls, datas):
518
     def _check_datas_consistency(cls, datas):
509
         err_l = []
519
         err_l = []
516
         if len(err_l) > 0:
526
         if len(err_l) > 0:
517
             raise LeApiDataCheckError("Datas consistency checks fails", err_l)
527
             raise LeApiDataCheckError("Datas consistency checks fails", err_l)
518
 
528
 
519
-    # @brief Check datas consistency
529
+    ## @brief Checks data consistency
520
530
521
     # @warning assert that datas is complete
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
     # @param type_query str : Type of query to be performed , default value : insert
533
     # @param type_query str : Type of query to be performed , default value : insert
525
     @classmethod
534
     @classmethod
526
     def make_consistency(cls, datas, type_query='insert'):
535
     def make_consistency(cls, datas, type_query='insert'):
527
         for fname, dh in cls._fields.items():
536
         for fname, dh in cls._fields.items():
528
             ret = dh.make_consistency(fname, datas, type_query)
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
     @classmethod
542
     @classmethod
533
     def insert(cls, datas):
543
     def insert(cls, datas):
534
         query = LeInsertQuery(cls)
544
         query = LeInsertQuery(cls)
535
         return query.execute(datas)
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
     def update(self, datas=None):
551
     def update(self, datas=None):
541
         datas = self.datas(internal=False) if datas is None else datas
552
         datas = self.datas(internal=False) if datas is None else datas
542
         uids = self._uid
553
         uids = self._uid
555
 
566
 
556
         return result
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
     def delete(self):
572
     def delete(self):
562
         uids = self._uid
573
         uids = self._uid
563
         query_filter = list()
574
         query_filter = list()
570
 
581
 
571
         return result
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
     @classmethod
587
     @classmethod
577
     def delete_bundle(cls, query_filters):
588
     def delete_bundle(cls, query_filters):
578
         deleted = 0
589
         deleted = 0
589
             deleted += result
600
             deleted += result
590
         return deleted
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
     @classmethod
613
     @classmethod
603
     def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
614
     def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
604
         if field_list is not None:
615
         if field_list is not None:
628
 
639
 
629
         return objects
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
     #@todo broken multiple UID
646
     #@todo broken multiple UID
633
     @classmethod
647
     @classmethod
634
     def get_from_uid(cls, uid):
648
     def get_from_uid(cls, uid):
645
             while len(res_cp) > 0:
659
             while len(res_cp) > 0:
646
                 cur_res = res_cp.pop()
660
                 cur_res = res_cp.pop()
647
                 if cur_res.uid() in [r.uid() for r in res_cp]:
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
                 else:
663
                 else:
650
                     res.append(cur_res)
664
                     res.append(cur_res)
651
         if len(res) > 1:
665
         if len(res) > 1:

+ 194
- 188
lodel/leapi/query.py 查看文件

8
 from lodel.context import LodelContext
8
 from lodel.context import LodelContext
9
 LodelContext.expose_modules(globals(), {
9
 LodelContext.expose_modules(globals(), {
10
     'lodel.leapi.exceptions': ['LeApiError', 'LeApiErrors',
10
     'lodel.leapi.exceptions': ['LeApiError', 'LeApiErrors',
11
-        'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError',
12
-        'LeApiQueryErrors'],
11
+                               'LeApiDataCheckError', 'LeApiDataCheckErrors', 'LeApiQueryError',
12
+                               'LeApiQueryErrors'],
13
     'lodel.plugin.hooks': ['LodelHook'],
13
     'lodel.plugin.hooks': ['LodelHook'],
14
     'lodel.logger': ['logger']})
14
     'lodel.logger': ['logger']})
15
 
15
 
16
-##@todo check datas when running query
16
+# @todo check data when running query
17
+
18
+
17
 class LeQuery(object):
19
 class LeQuery(object):
18
 
20
 
19
-    ##@brief Hookname prefix
21
+    # @brief Hookname prefix
20
     _hook_prefix = None
22
     _hook_prefix = None
21
-    ##@brief arguments for the LeObject.check_data_value()
23
+    # @brief arguments for the LeObject.check_data_value()
22
     _data_check_args = {'complete': False, 'allow_internal': False}
24
     _data_check_args = {'complete': False, 'allow_internal': False}
23
 
25
 
24
-    ##@brief Abstract constructor
26
+    # @brief Abstract constructor
25
     # @param target_class LeObject : class of object the query is about
27
     # @param target_class LeObject : class of object the query is about
26
     def __init__(self, target_class):
28
     def __init__(self, target_class):
27
         from .leobject import LeObject
29
         from .leobject import LeObject
29
             raise NotImplementedError("Abstract class")
31
             raise NotImplementedError("Abstract class")
30
         if not inspect.isclass(target_class) or \
32
         if not inspect.isclass(target_class) or \
31
            not issubclass(target_class, LeObject):
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
         self._target_class = target_class
36
         self._target_class = target_class
34
         self._ro_datasource = target_class._ro_datasource
37
         self._ro_datasource = target_class._ro_datasource
35
         self._rw_datasource = target_class._rw_datasource
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
     #@return the query result
42
     #@return the query result
40
     #@see LeQuery._query()
43
     #@see LeQuery._query()
41
     #@todo check that the check_datas_value is not duplicated/useless
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
             self._target_class.check_datas_value(
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
         if self._hook_prefix is None:
51
         if self._hook_prefix is None:
49
             raise NotImplementedError("Abstract method")
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
         return ret
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
     #@return query result
64
     #@return query result
62
-    def _query(self, **datas):
65
+    def _query(self, **data):
63
         raise NotImplementedError("Asbtract method")
66
         raise NotImplementedError("Asbtract method")
64
 
67
 
65
-    ##@return a dict with query infos
68
+    # @return a dict with query infos
66
     def dump_infos(self):
69
     def dump_infos(self):
67
         return {'target_class': self._target_class}
70
         return {'target_class': self._target_class}
68
 
71
 
69
     def __repr__(self):
72
     def __repr__(self):
70
         ret = "<{classname} target={target_class}>"
73
         ret = "<{classname} target={target_class}>"
71
         return ret.format(
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
 class LeFilteredQuery(LeQuery):
81
 class LeFilteredQuery(LeQuery):
77
-    ##@brief The available operators used in query definitions
82
+    # @brief The available operators used in query definitions
78
     _query_operators = [
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
     _query_re = None
96
     _query_re = None
92
 
97
 
93
-    ##@brief Abtract constructor for queries with filter
98
+    # @brief Abtract constructor for queries with filter
94
     #@param target_class LeObject : class of object the query is about
99
     #@param target_class LeObject : class of object the query is about
95
     #@param query_filters list : with a tuple (only one filter) or a list of
100
     #@param query_filters list : with a tuple (only one filter) or a list of
96
     # tuple or a dict: {OP,list(filters)} with OP = 'OR' or 'AND for tuple
101
     # tuple or a dict: {OP,list(filters)} with OP = 'OR' or 'AND for tuple
97
     # (FIELD,OPERATOR,VALUE)
102
     # (FIELD,OPERATOR,VALUE)
98
     def __init__(self, target_class, query_filters=None):
103
     def __init__(self, target_class, query_filters=None):
99
         super().__init__(target_class)
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
         self._query_filter = None
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
         # more than one datasource.
108
         # more than one datasource.
104
         #
109
         #
105
         # Subqueries are tuple(target_class_ref_field, LeGetQuery)
110
         # Subqueries are tuple(target_class_ref_field, LeGetQuery)
107
         query_filters = [] if query_filters is None else query_filters
112
         query_filters = [] if query_filters is None else query_filters
108
         self.set_query_filter(query_filters)
113
         self.set_query_filter(query_filters)
109
 
114
 
110
-    ##@brief Abstract FilteredQuery execution method
115
+    # @brief Abstract FilteredQuery execution method
111
     #
116
     #
112
     # This method takes care to execute subqueries before calling super execute
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
         orig_filters = copy.copy(self._query_filter)
120
         orig_filters = copy.copy(self._query_filter)
116
         std_filters, rel_filters = self._query_filter
121
         std_filters, rel_filters = self._query_filter
117
 
122
 
123
         try:
128
         try:
124
 
129
 
125
             filters, rel_filters = self._query_filter
130
             filters, rel_filters = self._query_filter
126
-            res = super().execute(datas)
131
+            res = super().execute(data)
127
         except Exception as e:
132
         except Exception as e:
128
-            #restoring filters even if an exception is raised
133
+            # restoring filters even if an exception is raised
129
             self.__query_filter = orig_filters
134
             self.__query_filter = orig_filters
130
 
135
 
131
-            raise e #reraise
132
-        #restoring filters
136
+            raise e  # reraise
137
+        # restoring filters
133
         self._query_filter = orig_filters
138
         self._query_filter = orig_filters
134
         return res
139
         return res
135
 
140
 
136
-    ##@brief Add filter(s) to the query
141
+    # @brief Add filter(s) to the query
137
     #
142
     #
138
     # This method is also able to slice query if different datasources are
143
     # This method is also able to slice query if different datasources are
139
     # implied in the request
144
     # implied in the request
144
     def set_query_filter(self, query_filter):
149
     def set_query_filter(self, query_filter):
145
         if isinstance(query_filter, str):
150
         if isinstance(query_filter, str):
146
             query_filter = [query_filter]
151
             query_filter = [query_filter]
147
-        #Query filter prepration
152
+        # Query filter prepration
148
         filters_orig, rel_filters = self._prepare_filters(query_filter)
153
         filters_orig, rel_filters = self._prepare_filters(query_filter)
149
         # Here we now that each relational filter concern only one datasource
154
         # Here we now that each relational filter concern only one datasource
150
         # thank's to _prepare_relational_fields
155
         # thank's to _prepare_relational_fields
151
 
156
 
152
-        #Multiple datasources detection
157
+        # Multiple datasources detection
153
         self_ds_name = self._target_class._datasource_name
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
         other_ds_filters = dict()
160
         other_ds_filters = dict()
156
         for rfilter in rel_filters:
161
         for rfilter in rel_filters:
157
             (rfield, ref_dict), op, value = rfilter
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
             # First step : simplification
165
             # First step : simplification
161
             # Trying to delete relational filters done on referenced class uid
166
             # Trying to delete relational filters done on referenced class uid
162
             for tclass, tfield in copy.copy(ref_dict).items():
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
                 #   !!!WARNING!!!
171
                 #   !!!WARNING!!!
167
                 # The line below brake multi UID support
172
                 # The line below brake multi UID support
168
                 #
173
                 #
169
                 if tfield == tclass.uid_fieldname()[0]:
174
                 if tfield == tclass.uid_fieldname()[0]:
170
-                    #This relational filter can be simplified as
175
+                    # This relational filter can be simplified as
171
                     # ref_field, op, value
176
                     # ref_field, op, value
172
                     # Note : we will have to dedup filters_orig
177
                     # Note : we will have to dedup filters_orig
173
                     filters_orig.append((rfield, op, value))
178
                     filters_orig.append((rfield, op, value))
174
                     del(ref_dict[tclass])
179
                     del(ref_dict[tclass])
175
             if len(ref_dict) == 0:
180
             if len(ref_dict) == 0:
176
                 continue
181
                 continue
177
-            #Determine what to do with other relational filters given
182
+            # Determine what to do with other relational filters given
178
             # referenced class datasource
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
             # datasource
185
             # datasource
181
             tclass = list(ref_dict.keys())[0]
186
             tclass = list(ref_dict.keys())[0]
182
             cur_ds = tclass._datasource_name
187
             cur_ds = tclass._datasource_name
189
                     other_ds_filters[cur_ds] = list()
194
                     other_ds_filters[cur_ds] = list()
190
                 other_ds_filters[cur_ds].append(
195
                 other_ds_filters[cur_ds].append(
191
                     ((rfield, ref_dict), op, value))
196
                     ((rfield, ref_dict), op, value))
192
-        #deduplication of std filters
197
+        # deduplication of std filters
193
         filters_cp = set()
198
         filters_cp = set()
194
         if not isinstance(filters_orig, set):
199
         if not isinstance(filters_orig, set):
195
             for i, cfilt in enumerate(filters_orig):
200
             for i, cfilt in enumerate(filters_orig):
196
                 a, b, c = cfilt
201
                 a, b, c = cfilt
197
-                if isinstance(c, list): #list are not hashable
202
+                if isinstance(c, list):  # list are not hashable
198
                     newc = tuple(c)
203
                     newc = tuple(c)
199
                 else:
204
                 else:
200
                     newc = c
205
                     newc = c
201
                 old_len = len(filters_cp)
206
                 old_len = len(filters_cp)
202
-                filters_cp |= set((a,b,newc))
207
+                filters_cp |= set((a, b, newc))
203
                 if len(filters_cp) == old_len:
208
                 if len(filters_cp) == old_len:
204
                     del(filters_orig[i])
209
                     del(filters_orig[i])
205
         # Sets _query_filter attribute of self query
210
         # Sets _query_filter attribute of self query
206
         self._query_filter = (filters_orig, result_rel_filters)
211
         self._query_filter = (filters_orig, result_rel_filters)
207
 
212
 
208
-        #Sub queries creation
213
+        # Sub queries creation
209
         subq = list()
214
         subq = list()
210
         for ds, rfilters in other_ds_filters.items():
215
         for ds, rfilters in other_ds_filters.items():
211
             for rfilter in rfilters:
216
             for rfilter in rfilters:
218
                     subq.append((rfield, query))
223
                     subq.append((rfield, query))
219
         self.subqueries = subq
224
         self.subqueries = subq
220
 
225
 
221
-    ##@return informations
226
+    # @return informations
222
     def dump_infos(self):
227
     def dump_infos(self):
223
         ret = super().dump_infos()
228
         ret = super().dump_infos()
224
         ret['query_filter'] = self._query_filter
229
         ret['query_filter'] = self._query_filter
238
         res += '>'
243
         res += '>'
239
         return res
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
     #@par Simple filters
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
     # VALUE . Where :
256
     # VALUE . Where :
252
     #- FIELDNAME is the name of the field
257
     #- FIELDNAME is the name of the field
253
     #- OP is one of the authorized comparison operands (see
258
     #- OP is one of the authorized comparison operands (see
256
     #
261
     #
257
     #@par Relational filters
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
     # FIELDNAME[.REF_FIELD] OP VALUE . Where :
267
     # FIELDNAME[.REF_FIELD] OP VALUE . Where :
263
     #- FIELDNAME is the name of the reference field
268
     #- FIELDNAME is the name of the reference field
264
     #- REF_FIELD is an optionnal addon to the base field. It indicate on wich
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
     #@param cls
273
     #@param cls
269
     #@param filters_l list : This list of str or tuple (or both)
274
     #@param filters_l list : This list of str or tuple (or both)
271
     #@todo move this doc in another place (a dedicated page ?)
276
     #@todo move this doc in another place (a dedicated page ?)
272
     #@warning Does not supports multiple UID for an EmClass
277
     #@warning Does not supports multiple UID for an EmClass
273
     def _prepare_filters(self, filters_l):
278
     def _prepare_filters(self, filters_l):
274
-        filters=list()
279
+        filters = list()
275
         res_filters = list()
280
         res_filters = list()
276
         rel_filters = list()
281
         rel_filters = list()
277
         err_l = dict()
282
         err_l = dict()
278
-        #Splitting in tuple if necessary
283
+        # Splitting in tuple if necessary
279
         for i, fil in enumerate(filters_l):
284
         for i, fil in enumerate(filters_l):
280
             if len(fil) == 3 and not isinstance(fil, str):
285
             if len(fil) == 3 and not isinstance(fil, str):
281
                 filters.append(tuple(fil))
286
                 filters.append(tuple(fil))
286
                     err_l["filter %d" % i] = e
291
                     err_l["filter %d" % i] = e
287
 
292
 
288
         for field, operator, value in filters:
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
             # Spliting field name to be able to detect a relational field
295
             # Spliting field name to be able to detect a relational field
291
             field_spl = field.split('.')
296
             field_spl = field.split('.')
292
             if len(field_spl) == 2:
297
             if len(field_spl) == 2:
310
                 # inconsistency
315
                 # inconsistency
311
                 err_l[field] = NameError("The field '%s' in %s is not \
316
                 err_l[field] = NameError("The field '%s' in %s is not \
312
 a relational field, but %s.%s was present in the filter"
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
             if field_datahandler.is_reference():
322
             if field_datahandler.is_reference():
318
-                #Relationnal field
323
+                # Relationnal field
319
                 if ref_field is None:
324
                 if ref_field is None:
320
                     # ref_field default value
325
                     # ref_field default value
321
                     #
326
                     #
350
                 value, error = field_datahandler.check_data_value(value)
355
                 value, error = field_datahandler.check_data_value(value)
351
                 if isinstance(error, Exception):
356
                 if isinstance(error, Exception):
352
                     value = value_orig
357
                     value = value_orig
353
-                res_filters.append((field,operator, value))
358
+                res_filters.append((field, operator, value))
354
         if len(err_l) > 0:
359
         if len(err_l) > 0:
355
             raise LeApiDataCheckErrors(
360
             raise LeApiDataCheckErrors(
356
-                                        "Error while preparing filters : ",
357
-                                        err_l)
361
+                "Error while preparing filters : ",
362
+                err_l)
358
         return (res_filters, rel_filters)
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
     # @note The query_filter format is "FIELD OPERATOR VALUE"
366
     # @note The query_filter format is "FIELD OPERATOR VALUE"
362
     # @param query_filter str : A query_filter string
367
     # @param query_filter str : A query_filter string
363
     # @param cls
368
     # @param cls
382
                 raise ValueError(msg % query_filter)
387
                 raise ValueError(msg % query_filter)
383
         return result
388
         return result
384
 
389
 
385
-    ## @brief Compile the regex for query_filter processing
390
+    # @brief Compile the regex for query_filter processing
386
     # @note Set _LeObject._query_re
391
     # @note Set _LeObject._query_re
387
     @classmethod
392
     @classmethod
388
     def __compile_query_re(cls):
393
     def __compile_query_re(cls):
389
         op_re_piece = '(?P<operator>(%s)'
394
         op_re_piece = '(?P<operator>(%s)'
390
         op_re_piece %= cls._query_operators[0].replace(' ', '\s')
395
         op_re_piece %= cls._query_operators[0].replace(' ', '\s')
391
         for operator in cls._query_operators[1:]:
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
         op_re_piece += ')'
398
         op_re_piece += ')'
394
 
399
 
395
         re_full = '^\s*(?P<field>([a-z_][a-z0-9\-_]*\.)?[a-z_][a-z0-9\-_]*)\s*'
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
         cls._query_re = re.compile(re_full, flags=re.IGNORECASE)
403
         cls._query_re = re.compile(re_full, flags=re.IGNORECASE)
399
         pass
404
         pass
407
             msg %= (fieldname, target_class.__name__)
412
             msg %= (fieldname, target_class.__name__)
408
             return NameError(msg)
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
     #<code>((FIELDNAME, {REF_CLASS: REF_FIELD}), OP, VALUE)</code>
420
     #<code>((FIELDNAME, {REF_CLASS: REF_FIELD}), OP, VALUE)</code>
416
     # Where :
421
     # Where :
419
     # - REF_CLASS as key. It's a LeObject child class
424
     # - REF_CLASS as key. It's a LeObject child class
420
     # - REF_FIELD as value. The name of the referenced field in the REF_CLASS
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
     #@par String notation examples
431
     #@par String notation examples
427
     #<pre>contributeur IN (1,2,3,5)</pre> will be transformed into :
432
     #<pre>contributeur IN (1,2,3,5)</pre> will be transformed into :
439
     #
444
     #
440
     #@param fieldname str : The relational field name
445
     #@param fieldname str : The relational field name
441
     #@param ref_field str|None : The referenced field name (if None use
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
     #@return a well formed relational filter tuple or an Exception instance
448
     #@return a well formed relational filter tuple or an Exception instance
444
     def _prepare_relational_fields(self, fieldname, ref_field=None):
449
     def _prepare_relational_fields(self, fieldname, ref_field=None):
445
         datahandler = self._target_class.field(fieldname)
450
         datahandler = self._target_class.field(fieldname)
467
                     logger.debug(msg)
472
                     logger.debug(msg)
468
         if len(ref_dict) == 0:
473
         if len(ref_dict) == 0:
469
             return NameError("No field named '%s' in referenced objects [%s]"
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
         return (fieldname, ref_dict)
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
 class LeInsertQuery(LeQuery):
481
 class LeInsertQuery(LeQuery):
477
     _hook_prefix = 'leapi_insert_'
482
     _hook_prefix = 'leapi_insert_'
478
     _data_check_args = {'complete': True, 'allow_internal': False}
483
     _data_check_args = {'complete': True, 'allow_internal': False}
483
 abstract LeObject : %s" % target_class)
488
 abstract LeObject : %s" % target_class)
484
         super().__init__(target_class)
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
         return id_inserted
496
         return id_inserted
492
     """
497
     """
493
     ## @brief Implements an insert query operation, with multiple insertions
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
         nb_inserted = self._datasource.insert_multi(
501
         nb_inserted = self._datasource.insert_multi(
497
-            self._target_class,datas_list)
502
+            self._target_class,data_list)
498
         if nb_inserted < 0:
503
         if nb_inserted < 0:
499
             raise LeApiQueryError("Multiple insertions error")
504
             raise LeApiQueryError("Multiple insertions error")
500
         return nb_inserted
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
 #@todo Change behavior, Huge optimization problem when updating using filters
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
 class LeUpdateQuery(LeFilteredQuery):
517
 class LeUpdateQuery(LeFilteredQuery):
513
     _hook_prefix = 'leapi_update_'
518
     _hook_prefix = 'leapi_update_'
514
     _data_check_args = {'complete': False, 'allow_internal': False}
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
     #@param target LeObject clas or instance
527
     #@param target LeObject clas or instance
523
     #@param query_filters list|None
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
     def __init__(self, target, query_filters=None):
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
         self.__leobject_instance_datas = None
534
         self.__leobject_instance_datas = None
530
         target_class = target
535
         target_class = target
531
 
536
 
542
 
547
 
543
         super().__init__(target_class, query_filters)
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
     #@returns the number of updated items
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
         uid_name = self._target_class._uid[0]
556
         uid_name = self._target_class._uid[0]
552
         if self.__leobject_instance_datas is not None:
557
         if self.__leobject_instance_datas is not None:
553
-            #Instance update
554
-            #Building query_filter
558
+            # Instance update
559
+            # Building query_filter
555
             filters = [(
560
             filters = [(
556
                 uid_name,
561
                 uid_name,
557
                 '=',
562
                 '=',
560
                 self._target_class, filters, [],
565
                 self._target_class, filters, [],
561
                 self.__leobject_instance_datas)
566
                 self.__leobject_instance_datas)
562
         else:
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
             res = self._ro_datasource.select(
569
             res = self._ro_datasource.select(
565
                 self._target_class, self._target_class.fieldnames(True),
570
                 self._target_class, self._target_class.fieldnames(True),
566
                 self._query_filter[0],
571
                 self._query_filter[0],
567
                 self._query_filter[1])
572
                 self._query_filter[1])
568
-            #Checking and constructing datas
569
-            upd_datas = dict()
573
+            # Checking and constructing data
574
+            upd_data = dict()
570
             for res_data in res:
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
                     res_data, True, True)
578
                     res_data, True, True)
574
                 filters = [(uid_name, '=', res_data[uid_name])]
579
                 filters = [(uid_name, '=', res_data[uid_name])]
575
                 res = self._rw_datasource.update(
580
                 res = self._rw_datasource.update(
576
                     self._target_class, filters, [],
581
                     self._target_class, filters, [],
577
-                    res_datas)
582
+                    res_data)
578
         return res
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
 query on an instance")
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
 query on a class with filters")
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
 class LeDeleteQuery(LeFilteredQuery):
597
 class LeDeleteQuery(LeFilteredQuery):
593
     _hook_prefix = 'leapi_delete_'
598
     _hook_prefix = 'leapi_delete_'
594
 
599
 
595
     def __init__(self, target_class, query_filter):
600
     def __init__(self, target_class, query_filter):
596
         super().__init__(target_class, query_filter)
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
         return super().execute()
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
     #@returns the number of deleted items
610
     #@returns the number of deleted items
606
-    def _query(self, datas=None):
611
+    def _query(self, data=None):
607
         filters, rel_filters = self._query_filter
612
         filters, rel_filters = self._query_filter
608
         nb_deleted = self._rw_datasource.delete(
613
         nb_deleted = self._rw_datasource.delete(
609
             self._target_class, filters, rel_filters)
614
             self._target_class, filters, rel_filters)
610
         return nb_deleted
615
         return nb_deleted
611
 
616
 
617
+
612
 class LeGetQuery(LeFilteredQuery):
618
 class LeGetQuery(LeFilteredQuery):
613
     _hook_prefix = 'leapi_get_'
619
     _hook_prefix = 'leapi_get_'
614
 
620
 
615
-    ##@brief Instanciate a new get query
621
+    # @brief Instanciate a new get query
616
     #@param target_class LeObject : class of object the query is about
622
     #@param target_class LeObject : class of object the query is about
617
     #@param query_filters dict : {OP, list of query filters}
623
     #@param query_filters dict : {OP, list of query filters}
618
     # or tuple (FIELD, OPERATOR, VALUE) )
624
     # or tuple (FIELD, OPERATOR, VALUE) )
624
     #   - offset int : offset
630
     #   - offset int : offset
625
     def __init__(self, target_class, query_filters, **kwargs):
631
     def __init__(self, target_class, query_filters, **kwargs):
626
         super().__init__(target_class, query_filters)
632
         super().__init__(target_class, query_filters)
627
-        ##@brief The fields to get
633
+        # @brief The fields to get
628
         self._field_list = None
634
         self._field_list = None
629
-        ##@brief An equivalent to the SQL ORDER BY
635
+        # @brief An equivalent to the SQL ORDER BY
630
         self._order = None
636
         self._order = None
631
-        ##@brief An equivalent to the SQL GROUP BY
637
+        # @brief An equivalent to the SQL GROUP BY
632
         self._group = None
638
         self._group = None
633
-        ##@brief An equivalent to the SQL LIMIT x
639
+        # @brief An equivalent to the SQL LIMIT x
634
         self._limit = None
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
         self._offset = 0
642
         self._offset = 0
637
 
643
 
638
         # Checking kwargs and assigning default values if there is some
644
         # Checking kwargs and assigning default values if there is some
639
         for argname in kwargs:
645
         for argname in kwargs:
640
             if argname not in (
646
             if argname not in (
641
-                'field_list', 'order', 'group', 'limit', 'offset'):
647
+                    'field_list', 'order', 'group', 'limit', 'offset'):
642
                 raise TypeError("Unexpected argument '%s'" % argname)
648
                 raise TypeError("Unexpected argument '%s'" % argname)
643
 
649
 
644
         if 'field_list' not in kwargs:
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
         else:
652
         else:
647
             self.set_field_list(kwargs['field_list'])
653
             self.set_field_list(kwargs['field_list'])
648
 
654
 
649
         if 'order' in kwargs:
655
         if 'order' in kwargs:
650
-            #check kwargs['order']
656
+            # check kwargs['order']
651
             self._order = kwargs['order']
657
             self._order = kwargs['order']
652
         if 'group' in kwargs:
658
         if 'group' in kwargs:
653
-            #check kwargs['group']
659
+            # check kwargs['group']
654
             self._group = kwargs['group']
660
             self._group = kwargs['group']
655
         if 'limit' in kwargs and kwargs['limit'] is not None:
661
         if 'limit' in kwargs and kwargs['limit'] is not None:
656
             try:
662
             try:
669
                 msg = "offset argument expected to be an integer >= 0"
675
                 msg = "offset argument expected to be an integer >= 0"
670
                 raise ValueError(msg)
676
                 raise ValueError(msg)
671
 
677
 
672
-    ##@brief Set the field list
678
+    # @brief Set the field list
673
     # @param field_list list | None : If None use all fields
679
     # @param field_list list | None : If None use all fields
674
     # @return None
680
     # @return None
675
     # @throw LeApiQueryError if unknown field given
681
     # @throw LeApiQueryError if unknown field given
682
                     msg = "No field named '%s' in %s"
688
                     msg = "No field named '%s' in %s"
683
                     msg %= (fieldname, self._target_class.__name__)
689
                     msg %= (fieldname, self._target_class.__name__)
684
                     expt = NameError(msg)
690
                     expt = NameError(msg)
685
-                    err_l[fieldname] =  expt
691
+                    err_l[fieldname] = expt
686
             if len(err_l) > 0:
692
             if len(err_l) > 0:
687
                 msg = "Error while setting field_list in a get query"
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
             self._field_list = list(set(field_list))
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
         return super().execute()
699
         return super().execute()
694
 
700
 
695
-    ##@brief Implements select query operations
701
+    # @brief Implements select query operations
696
     # @returns a list containing the item(s)
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
         fl = list(self._field_list) if self._field_list is not None else None
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
     def dump_infos(self):
718
     def dump_infos(self):
713
         ret = super().dump_infos()
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
         return ret
726
         return ret
721
 
727
 
722
     def __repr__(self):
728
     def __repr__(self):
725
 offset={offset}"
731
 offset={offset}"
726
         res = res.format(**self.dump_infos())
732
         res = res.format(**self.dump_infos())
727
         if len(self.subqueries) > 0:
733
         if len(self.subqueries) > 0:
728
-            for n,subq in enumerate(self.subqueries):
734
+            for n, subq in enumerate(self.subqueries):
729
                 res += "\n\tSubquerie %d : %s"
735
                 res += "\n\tSubquerie %d : %s"
730
                 res %= (n, subq)
736
                 res %= (n, subq)
731
         res += ">"
737
         res += ">"

+ 5
- 0
lodel/mlnamedobject/__init__.py 查看文件

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 查看文件

4
 LodelContext.expose_modules(globals(), {
4
 LodelContext.expose_modules(globals(), {
5
     'lodel.utils.mlstring': ['MlString']})
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
 # Display name and Description of a lodel2 object
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
 class MlNamedObject(object):
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
     def __init__(self, display_name=None, help_text=None):
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
         self.display_name = None if display_name is None else MlString(display_name)
19
         self.display_name = None if display_name is None else MlString(display_name)
20
+        ## @brief Description text for this object
18
         self.help_text = None if help_text is None else MlString(help_text)
21
         self.help_text = None if help_text is None else MlString(help_text)

+ 15
- 7
lodel/plugin/core_hooks.py 查看文件

6
     'lodel.settings': ['Settings'],
6
     'lodel.settings': ['Settings'],
7
     'lodel.logger': 'logger'})
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
 @LodelHook('lodel2_bootstraped')
18
 @LodelHook('lodel2_bootstraped')
15
 def datasources_bootstrap_hook(hook_name, caller, payload):
19
 def datasources_bootstrap_hook(hook_name, caller, payload):
16
     for ds_name in Settings.datasources._fields:
20
     for ds_name in Settings.datasources._fields:
32
             msg %= identifier
36
             msg %= identifier
33
             raise NameError(msg)
37
             raise NameError(msg)
34
 
38
 
35
-
36
         log_msg = "Found a datasource named '%s' identified by '%s'"
39
         log_msg = "Found a datasource named '%s' identified by '%s'"
37
         log_msg %= (ds_name, identifier)
40
         log_msg %= (ds_name, identifier)
38
         logger.debug(log_msg)
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
 @LodelHook('lodel2_bootstraped')
47
 @LodelHook('lodel2_bootstraped')
42
 def list_hook_debug_hook(name, caller, payload):
48
 def list_hook_debug_hook(name, caller, payload):
43
     LodelContext.expose_modules(globals(), {
49
     LodelContext.expose_modules(globals(), {
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
 @LodelHook("lodel2_dyncode_loaded")
67
 @LodelHook("lodel2_dyncode_loaded")
60
 def lodel2_plugins_custom_methods(self, caller, dynclasses):
68
 def lodel2_plugins_custom_methods(self, caller, dynclasses):
61
     LodelContext.expose_modules(globals(), {
69
     LodelContext.expose_modules(globals(), {

+ 105
- 98
lodel/plugin/datasource_plugin.py 查看文件

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
 from lodel.context import LodelContext
6
 from lodel.context import LodelContext
2
 LodelContext.expose_modules(globals(), {
7
 LodelContext.expose_modules(globals(), {
3
     'lodel.plugin.plugins': ['Plugin'],
8
     'lodel.plugin.plugins': ['Plugin'],
7
     'lodel.exceptions': ['LodelException', 'LodelExceptions',
12
     'lodel.exceptions': ['LodelException', 'LodelExceptions',
8
         'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
13
         'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
9
 
14
 
15
+## @brief The plugin type that is used in the global settings of Lodel
10
 _glob_typename = 'datasource'
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
 class AbstractDatasource(object):
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
     @staticmethod
24
     @staticmethod
18
     def _abs_err():
25
     def _abs_err():
19
         raise LodelFatalError("This method is abstract and HAVE TO be \
26
         raise LodelFatalError("This method is abstract and HAVE TO be \
20
 reimplemented by plugin datasource child class")
27
 reimplemented by plugin datasource child class")
21
     
28
     
22
-    ##@brief The constructor
29
+    ##
30
+    # @param *conn_args
31
+    # @param **conn_kwargs
23
     def __init__(self, *conn_args, **conn_kwargs):
32
     def __init__(self, *conn_args, **conn_kwargs):
24
         self._abs_err()
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
     def new_numeric_id(self, emcomp):
38
     def new_numeric_id(self, emcomp):
31
         self._abs_err()
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
     def select(self, target, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
52
     def select(self, target, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
45
                instanciate=True):
53
                instanciate=True):
46
         self._abs_err()
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
     def delete(self, target, filters, relational_filters):
61
     def delete(self, target, filters, relational_filters):
54
         self._abs_err()
62
         self._abs_err()
55
 
63
 
56
     ## @brief updates records according to given filters
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
     def update(self, target, filters, relational_filters, upd_datas):
70
     def update(self, target, filters, relational_filters, upd_datas):
63
         self._abs_err()
71
         self._abs_err()
64
 
72
 
77
         self._abs_err()
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
 class DatasourcePlugin(Plugin):
98
 class DatasourcePlugin(Plugin):
93
     
99
     
94
     _type_conf_name = _glob_typename
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
     _plist_confspecs = {
103
     _plist_confspecs = {
97
         'section': 'lodel2',
104
         'section': 'lodel2',
98
         'key': 'datasource_connectors',
105
         'key': 'datasource_connectors',
104
                 'none_is_valid': False})
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
     def __init__(self, name):
117
     def __init__(self, name):
111
         super().__init__(name)
118
         super().__init__(name)
112
         self.__datasource_cls = None
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
     def datasource_cls(self):
125
     def datasource_cls(self):
117
         if self.__datasource_cls is None:
126
         if self.__datasource_cls is None:
118
             self.__datasource_cls = self.loader_module().Datasource
127
             self.__datasource_cls = self.loader_module().Datasource
122
 lodel.plugin.datasource_plugin.AbstractDatasource" % (self.name))
131
 lodel.plugin.datasource_plugin.AbstractDatasource" % (self.name))
123
         return self.__datasource_cls
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
     def migration_handler_cls(self):
136
     def migration_handler_cls(self):
128
         return self.loader_module().migration_handler_class()
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
     @classmethod
143
     @classmethod
137
     def init_datasource(cls, ds_name, ro):
144
     def init_datasource(cls, ds_name, ro):
138
         plugin_name, ds_identifier = cls.plugin_name(ds_name, ro)
145
         plugin_name, ds_identifier = cls.plugin_name(ds_name, ro)
140
         ds_cls = cls.get_datasource(plugin_name)
147
         ds_cls = cls.get_datasource(plugin_name)
141
         return ds_cls(**ds_conf)
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
     @classmethod
154
     @classmethod
147
     def init_migration_handler(cls, ds_name):
155
     def init_migration_handler(cls, ds_name):
148
         plugin_name, ds_identifier = cls.plugin_name(ds_name, False)
156
         plugin_name, ds_identifier = cls.plugin_name(ds_name, False)
156
         return mh_cls(**ds_conf)
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
     @staticmethod
173
     @staticmethod
167
     def plugin_name(ds_name, ro):
174
     def plugin_name(ds_name, ro):
168
         LodelContext.expose_modules(globals(), {
175
         LodelContext.expose_modules(globals(), {
195
 DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
202
 DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
196
         return res
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
     @staticmethod
210
     @staticmethod
204
     def _get_ds_connection_conf(ds_identifier,ds_plugin_name):
211
     def _get_ds_connection_conf(ds_identifier,ds_plugin_name):
205
         LodelContext.expose_modules(globals(), {
212
         LodelContext.expose_modules(globals(), {
216
         ds_conf = getattr(ds_conf, ds_identifier)
223
         ds_conf = getattr(ds_conf, ds_identifier)
217
         return {k: getattr(ds_conf,k) for k in ds_conf._fields }
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
     @classmethod
231
     @classmethod
226
     def get(cls, ds_name):
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
         if not isinstance(pinstance, DatasourcePlugin):
234
         if not isinstance(pinstance, DatasourcePlugin):
229
            raise PluginTypeErrror("A name of a DatasourcePlugin was excepted \
235
            raise PluginTypeErrror("A name of a DatasourcePlugin was excepted \
230
 but %s is a %s" % (ds_name, pinstance.__class__.__name__))
236
 but %s is a %s" % (ds_name, pinstance.__class__.__name__))
231
         return pinstance
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
     @classmethod
242
     @classmethod
238
     def get_datasource(cls, ds_plugin_name):
243
     def get_datasource(cls, ds_plugin_name):
239
         return cls.get(ds_plugin_name).datasource_cls()
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
     @classmethod
249
     @classmethod
244
     def get_migration_handler(cls, ds_plugin_name):
250
     def get_migration_handler(cls, ds_plugin_name):
245
         return cls.get(ds_plugin_name).migration_handler_cls()
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
 # A single lodel2 website can interact with multiple datasources. This page
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
 # Each object is attached to a datasource. This association is done in the
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
 #<pre>
275
 #<pre>
270
 #CONFSPEC = {
276
 #CONFSPEC = {
271
 #                'lodel2.datasource.example.*' : {
277
 #                'lodel2.datasource.example.*' : {
275
 #                }
281
 #                }
276
 #}
282
 #}
277
 #</pre>
283
 #</pre>
278
-#MySQL example
284
+# 
285
+#MySQL example \n
279
 #<pre>
286
 #<pre>
280
 #CONFSPEC = {
287
 #CONFSPEC = {
281
 #                'lodel2.datasource.mysql.*' : {
288
 #                'lodel2.datasource.mysql.*' : {
291
 #}
298
 #}
292
 #</pre>
299
 #</pre>
293
 #
300
 #
294
-#@par Configuration example
295
-#<pre>
301
+# @par Configuration example
302
+# <pre>
296
 # [lodel2.datasources.main]
303
 # [lodel2.datasources.main]
297
 # identifier = mysql.Core
304
 # identifier = mysql.Core
298
 # [lodel2.datasources.revues_write]
305
 # [lodel2.datasources.revues_write]

+ 3
- 0
lodel/plugin/exceptions.py 查看文件

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

+ 10
- 2
lodel/plugin/extensions.py 查看文件

1
+## @package lodel.plugin.extensions A package to manage the Extension plugins
2
+
3
+
1
 from lodel.context import LodelContext
4
 from lodel.context import LodelContext
2
 LodelContext.expose_modules(globals(), {
5
 LodelContext.expose_modules(globals(), {
3
     'lodel.plugin.plugins': ['Plugin'],
6
     'lodel.plugin.plugins': ['Plugin'],
7
 
10
 
8
 _glob_typename = 'extension'
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
 class Extension(Plugin):
16
 class Extension(Plugin):
12
     
17
     
18
+    ## @brief Specifies the settings linked to this plugin
13
     _plist_confspecs = {
19
     _plist_confspecs = {
14
         'section': 'lodel2',
20
         'section': 'lodel2',
15
         'key': 'extensions',
21
         'key': 'extensions',
20
                 'ptype': _glob_typename,
26
                 'ptype': _glob_typename,
21
                 'none_is_valid': False})
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
     _type_conf_name = _glob_typename
32
     _type_conf_name = _glob_typename
25
 
33
 

+ 18
- 17
lodel/plugin/hooks.py 查看文件

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

+ 12
- 4
lodel/plugin/interface.py 查看文件

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

+ 27
- 37
lodel/plugins/mongodb_datasource/datasource.py 查看文件

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

23
         grp1.add_components((cls1, c1f1))
23
         grp1.add_components((cls1, c1f1))
24
         grp2 = model.new_group('testgroup2')
24
         grp2 = model.new_group('testgroup2')
25
         grp2.add_components((cls2, c1f2, c2f1, c2f2))
25
         grp2.add_components((cls2, c1f2, c2f1, c2f2))
26
-        grp2.add_dependencie(grp1)
26
+        grp2.add_dependency(grp1)
27
         e_hash = 0x250eab75e782e51bbf212f47c6159571
27
         e_hash = 0x250eab75e782e51bbf212f47c6159571
28
         self.assertEqual(model.d_hash(), e_hash)
28
         self.assertEqual(model.d_hash(), e_hash)
29
 
29
 
181
         grp3 = EmGroup('grp3')
181
         grp3 = EmGroup('grp3')
182
         grp4 = EmGroup('grp4')
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
         self.assertEqual(set(grp1.dependencies().values()), set())
189
         self.assertEqual(set(grp1.dependencies().values()), set())
190
         self.assertEqual(set(grp2.dependencies().values()), set([grp1]))
190
         self.assertEqual(set(grp2.dependencies().values()), set([grp1]))
261
     def test_deps_complex(self):
261
     def test_deps_complex(self):
262
         """ More complex dependencies handling test """
262
         """ More complex dependencies handling test """
263
         grps = [ EmGroup('group%d' % i) for i in range(6) ]
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
         self.assertEqual(
268
         self.assertEqual(
269
                             set(grps[5].dependencies(True).values()),
269
                             set(grps[5].dependencies(True).values()),
270
                             set( grps[i] for i in range(5))
270
                             set( grps[i] for i in range(5))
273
                             set(grps[4].dependencies(True).values()),
273
                             set(grps[4].dependencies(True).values()),
274
                             set( grps[i] for i in range(4))
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
         self.assertEqual(
277
         self.assertEqual(
278
                             set(grps[5].dependencies(True).values()),
278
                             set(grps[5].dependencies(True).values()),
279
                             set( grps[i] for i in range(5))
279
                             set( grps[i] for i in range(5))
284
         )
284
         )
285
         # Inserting circular deps
285
         # Inserting circular deps
286
         with self.assertRaises(EditorialModelError):
286
         with self.assertRaises(EditorialModelError):
287
-            grps[0].add_dependencie(grps[5])
287
+            grps[0].add_dependency(grps[5])
288
 
288
 
289
     def test_circular_dep(self):
289
     def test_circular_dep(self):
290
         """ Test circular dependencies detection """
290
         """ Test circular dependencies detection """
291
         grps = [ EmGroup('group%d' % i) for i in range(10) ]
291
         grps = [ EmGroup('group%d' % i) for i in range(10) ]
292
         for i in range(1,10):
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
         for i in range(1,10):
295
         for i in range(1,10):
296
             for j in range(i+1,10):
296
             for j in range(i+1,10):
297
                 with self.assertRaises(EditorialModelError):
297
                 with self.assertRaises(EditorialModelError):
298
-                    grps[i].add_dependencie(grps[j])
298
+                    grps[i].add_dependency(grps[j])
299
 
299
 
300
     def test_d_hash(self):
300
     def test_d_hash(self):
301
         """ Test the deterministic hash method """
301
         """ Test the deterministic hash method """

+ 1
- 1
tests/editorial_model/test_translator_picklefile.py 查看文件

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

+ 1
- 1
tests/editorial_model/test_translator_xmlfile.py 查看文件

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

+ 3
- 3
tests/leapi/query/test_datasource.py 查看文件

105
             [(('alias', {cls: 'firstname'}), '=', 'foo')])
105
             [(('alias', {cls: 'firstname'}), '=', 'foo')])
106
         self.check_nocall(read = False, exclude = ['delete'])
106
         self.check_nocall(read = False, exclude = ['delete'])
107
         self.check_nocall(read = True)
107
         self.check_nocall(read = True)
108
-    
108
+
109
     @unittest.skip("Waiting references checks stack implementation")
109
     @unittest.skip("Waiting references checks stack implementation")
110
     def test_insert(self):
110
     def test_insert(self):
111
         """ Testing LeInsertQuery mocking datasource """
111
         """ Testing LeInsertQuery mocking datasource """
145
         query = LeUpdateQuery(inst)
145
         query = LeUpdateQuery(inst)
146
 
146
 
147
         with self.assertRaises(LeApiQueryError):
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
         query.execute()
151
         query.execute()
152
         self.mockwrite.update.assert_called_once_with(
152
         self.mockwrite.update.assert_called_once_with(

Loading…
取消
儲存