|
@@ -1,5 +1,7 @@
|
1
|
1
|
#-*- coding: utf-8 -*-
|
2
|
2
|
|
|
3
|
+import importlib
|
|
4
|
+
|
3
|
5
|
class LeApiErrors(Exception):
|
4
|
6
|
## @brief Instanciate a new exceptions handling multiple exceptions
|
5
|
7
|
# @param msg str : Exception message
|
|
@@ -64,26 +66,26 @@ class LeObjectValues(object):
|
64
|
66
|
class LeObject(object):
|
65
|
67
|
|
66
|
68
|
## @brief boolean that tells if an object is abtract or not
|
67
|
|
- __abtract = None
|
|
69
|
+ _abstract = None
|
68
|
70
|
## @brief A dict that stores DataHandler instances indexed by field name
|
69
|
|
- __fields = None
|
|
71
|
+ _fields = None
|
70
|
72
|
## @brief A tuple of fieldname (or a uniq fieldname) representing uid
|
71
|
|
- __uid = None
|
|
73
|
+ _uid = None
|
72
|
74
|
|
73
|
75
|
## @brief Construct an object representing an Editorial component
|
74
|
76
|
# @note Can be considered as EmClass instance
|
75
|
77
|
def __init__(self, **kwargs):
|
76
|
|
- if self.__abstract:
|
|
78
|
+ if self._abstract:
|
77
|
79
|
raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
|
78
|
80
|
## @brief A dict that stores fieldvalues indexed by fieldname
|
79
|
|
- self.__datas = { fname:None for fname in self.__fields }
|
|
81
|
+ self.__datas = { fname:None for fname in self._fields }
|
80
|
82
|
## @brief Store a list of initianilized fields when instanciation not complete else store True
|
81
|
83
|
self.__initialized = list()
|
82
|
84
|
## @brief Datas accessor. Instance of @ref LeObjectValues
|
83
|
85
|
self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
|
84
|
86
|
|
85
|
87
|
# Checks that uid is given
|
86
|
|
- for uid_name in self.__uid:
|
|
88
|
+ for uid_name in self._uid:
|
87
|
89
|
if uid_name not in kwargs:
|
88
|
90
|
raise AttributeError("Cannot instanciate a LeObject without it's identifier")
|
89
|
91
|
self.__datas[uid_name] = kwargs[uid_name]
|
|
@@ -95,7 +97,7 @@ class LeObject(object):
|
95
|
97
|
err_list = list()
|
96
|
98
|
for fieldname, fieldval in kwargs.items():
|
97
|
99
|
if fieldname not in allowed_fieldnames:
|
98
|
|
- if fieldname in self.__fields:
|
|
100
|
+ if fieldname in self._fields:
|
99
|
101
|
err_list.append(
|
100
|
102
|
AttributeError("Value given for internal field : '%s'" % fieldname)
|
101
|
103
|
)
|
|
@@ -123,9 +125,9 @@ class LeObject(object):
|
123
|
125
|
@classmethod
|
124
|
126
|
def fieldnames(cls, include_ro = False):
|
125
|
127
|
if not include_ro:
|
126
|
|
- return [ fname for fname in self.__fields if not self.__fields[fname].is_internal() ]
|
|
128
|
+ return [ fname for fname in self._fields if not self._fields[fname].is_internal() ]
|
127
|
129
|
else:
|
128
|
|
- return list(self.__fields.keys())
|
|
130
|
+ return list(self._fields.keys())
|
129
|
131
|
|
130
|
132
|
@classmethod
|
131
|
133
|
def name2objname(cls, name):
|
|
@@ -136,10 +138,43 @@ class LeObject(object):
|
136
|
138
|
# @return A data handler instance
|
137
|
139
|
@classmethod
|
138
|
140
|
def data_handler(cls, fieldname):
|
139
|
|
- if not fieldname in cls.__fields:
|
|
141
|
+ if not fieldname in cls._fields:
|
140
|
142
|
raise NameError("No field named '%s' in %s" % (fieldname, cls.__name__))
|
141
|
|
- return cls.__fields[fieldname]
|
142
|
|
-
|
|
143
|
+ return cls._fields[fieldname]
|
|
144
|
+
|
|
145
|
+ ## @brief Return a LeObject child class from a name
|
|
146
|
+ # @warning This method has to be called from dynamically generated LeObjects
|
|
147
|
+ # @param leobject_name str : LeObject name
|
|
148
|
+ # @return A LeObject child class
|
|
149
|
+ # @throw NameError if invalid name given
|
|
150
|
+ @classmethod
|
|
151
|
+ def name2class(cls, leobject_name):
|
|
152
|
+ if cls.__module__ == 'lodel.leapi.leobject':
|
|
153
|
+ raise NotImplementedError("Abstract method")
|
|
154
|
+ mod = importlib.import_module(cls.__module__)
|
|
155
|
+ try:
|
|
156
|
+ return getattr(mod, leobject_name)
|
|
157
|
+ except AttributeError:
|
|
158
|
+ raise NameError("No LeObject named '%s'" % leobject_name)
|
|
159
|
+
|
|
160
|
+ ## @brief Method designed to "bootstrap" fields by settings their back references
|
|
161
|
+ #
|
|
162
|
+ # @note called once at dyncode load
|
|
163
|
+ # @note doesn't do any consistency checks, it assume that checks has been done EM side
|
|
164
|
+ # @warning after dyncode load this method is deleted
|
|
165
|
+ @classmethod
|
|
166
|
+ def _backref_init(cls):
|
|
167
|
+ for fname,field in cls._fields.items():
|
|
168
|
+ if field.is_reference() and field.back_reference is not None:
|
|
169
|
+ cls_name, field_name = field.back_reference
|
|
170
|
+ bref_leobject = cls.name2class(cls.name2objname(cls_name))
|
|
171
|
+ if field_name not in bref_leobject._fields:
|
|
172
|
+ raise NameError("LeObject %s doesn't have a field named '%s'" % (cls_name, field_name))
|
|
173
|
+ field.set_backreference(bref_leobject._fields[field_name])
|
|
174
|
+
|
|
175
|
+ @classmethod
|
|
176
|
+ def is_abstract(cls):
|
|
177
|
+ return cls._abstract
|
143
|
178
|
|
144
|
179
|
## @brief Read only access to all datas
|
145
|
180
|
# @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
|
|
@@ -148,7 +183,7 @@ class LeObject(object):
|
148
|
183
|
# @throw RuntimeError if the field is not initialized yet
|
149
|
184
|
# @throw NameError if name is not an existing field name
|
150
|
185
|
def data(self, field_name):
|
151
|
|
- if field_name not in self.__fields.keys():
|
|
186
|
+ if field_name not in self._fields.keys():
|
152
|
187
|
raise NameError("No such field in %s : %s" % (self.__class__.__name__, name))
|
153
|
188
|
if not self.initialized and name not in self.__initialized:
|
154
|
189
|
raise RuntimeError("The field %s is not initialized yet (and have no value)" % name)
|
|
@@ -163,7 +198,7 @@ class LeObject(object):
|
163
|
198
|
# @throw AttributeError if the field is not writtable
|
164
|
199
|
def set_data(self, fname, fval):
|
165
|
200
|
if field_name not in self.fieldnames(include_ro = False):
|
166
|
|
- if field_name not in self.__fields.keys():
|
|
201
|
+ if field_name not in self._fields.keys():
|
167
|
202
|
raise NameError("No such field in %s : %s" % (self.__class__.__name__, name))
|
168
|
203
|
else:
|
169
|
204
|
raise AttributeError("The field %s is read only" % fname)
|
|
@@ -182,7 +217,7 @@ class LeObject(object):
|
182
|
217
|
else:
|
183
|
218
|
# Doing value check on modified field
|
184
|
219
|
# We skip full validation here because the LeObject is not fully initialized yet
|
185
|
|
- val, err = self.__fields[fname].check_data_value(fval)
|
|
220
|
+ val, err = self._fields[fname].check_data_value(fval)
|
186
|
221
|
if isinstance(err, Exception):
|
187
|
222
|
#Revert change to be in valid state
|
188
|
223
|
del(self.__datas[fname])
|
|
@@ -196,7 +231,7 @@ class LeObject(object):
|
196
|
231
|
# Check the list of initialized fields and set __initialized to True if all fields initialized
|
197
|
232
|
def __set_initialized(self):
|
198
|
233
|
if isinstance(self.__initialized, list):
|
199
|
|
- expected_fields = self.fieldnames(include_ro = False) + self.__uid
|
|
234
|
+ expected_fields = self.fieldnames(include_ro = False) + self._uid
|
200
|
235
|
if set(expected_fields) == set(self.__initialized):
|
201
|
236
|
self.__initialized = True
|
202
|
237
|
|
|
@@ -209,7 +244,7 @@ class LeObject(object):
|
209
|
244
|
if self.__initialized is True:
|
210
|
245
|
# Data value check
|
211
|
246
|
for fname in self.fieldnames(include_ro = False):
|
212
|
|
- val, err = self.__fields[fname].check_data_value(self.__datas[fname])
|
|
247
|
+ val, err = self._fields[fname].check_data_value(self.__datas[fname])
|
213
|
248
|
if err is not None:
|
214
|
249
|
err_list[fname] = err
|
215
|
250
|
else:
|
|
@@ -218,7 +253,7 @@ class LeObject(object):
|
218
|
253
|
if len(err_list) == 0:
|
219
|
254
|
for fname in self.fieldnames(include_ro = True):
|
220
|
255
|
try:
|
221
|
|
- field = self.__fields[fname]
|
|
256
|
+ field = self._fields[fname]
|
222
|
257
|
self.__datas[fname] = fields.construct_data( self,
|
223
|
258
|
fname,
|
224
|
259
|
self.__datas,
|
|
@@ -229,14 +264,14 @@ class LeObject(object):
|
229
|
264
|
# Datas consistency check
|
230
|
265
|
if len(err_list) == 0:
|
231
|
266
|
for fname in self.fieldnames(include_ro = True):
|
232
|
|
- field = self.__fields[fname]
|
|
267
|
+ field = self._fields[fname]
|
233
|
268
|
ret = field.check_data_consistency(self, fname, self.__datas)
|
234
|
269
|
if isinstance(ret, Exception):
|
235
|
270
|
err_list[fname] = ret
|
236
|
271
|
else:
|
237
|
272
|
# Data value check for initialized datas
|
238
|
273
|
for fname in self.__initialized:
|
239
|
|
- val, err = self.__fields[fname].check_data_value(self.__datas[fname])
|
|
274
|
+ val, err = self._fields[fname].check_data_value(self.__datas[fname])
|
240
|
275
|
if err is not None:
|
241
|
276
|
err_list[fname] = err
|
242
|
277
|
else:
|
|
@@ -249,13 +284,13 @@ class LeObject(object):
|
249
|
284
|
|
250
|
285
|
## @brief Temporary method to set private fields attribute at dynamic code generation
|
251
|
286
|
#
|
252
|
|
- # This method is used in the generated dynamic code to set the __fields attribute
|
|
287
|
+ # This method is used in the generated dynamic code to set the _fields attribute
|
253
|
288
|
# at the end of the dyncode parse
|
254
|
|
- # @warning This method is deleted once the dynamic code is parsed
|
|
289
|
+ # @warning This method is deleted once the dynamic code loaded
|
255
|
290
|
# @param field_list list : list of EmField instance
|
256
|
291
|
@classmethod
|
257
|
292
|
def _set__fields(cls, field_list):
|
258
|
|
- cls.__fields = field_list
|
|
293
|
+ cls._fields = field_list
|
259
|
294
|
|
260
|
295
|
|
261
|
296
|
|