Browse Source

Documentation of the lodel.leapi.leobject module

Roland Haroutiounian 7 years ago
parent
commit
acc50423c0
1 changed files with 121 additions and 105 deletions
  1. 121
    105
      lodel/leapi/leobject.py

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

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

Loading…
Cancel
Save