rabaDB2 2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rabaDB/Raba.py +992 -0
- rabaDB/__init__.py +1 -0
- rabaDB/examples/BasicExample.py +33 -0
- rabaDB/examples/QueryExample.py +37 -0
- rabaDB/examples/__init__.py +1 -0
- rabaDB/fields.py +101 -0
- rabaDB/filters.py +306 -0
- rabaDB/rabaSetup.py +417 -0
- rabadb2-2.0.dist-info/METADATA +35 -0
- rabadb2-2.0.dist-info/RECORD +14 -0
- rabadb2-2.0.dist-info/WHEEL +5 -0
- rabadb2-2.0.dist-info/entry_points.txt +2 -0
- rabadb2-2.0.dist-info/licenses/LICENCE +201 -0
- rabadb2-2.0.dist-info/top_level.txt +1 -0
rabaDB/Raba.py
ADDED
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
import sqlite3 as sq
|
|
2
|
+
import os, copy, pickle, random, json, abc, sys
|
|
3
|
+
from collections.abc import MutableSequence
|
|
4
|
+
|
|
5
|
+
from .rabaSetup import RabaConnection, RabaConfiguration
|
|
6
|
+
from . import fields as RabaFields
|
|
7
|
+
|
|
8
|
+
def _recClassCheck(v, cls) :
|
|
9
|
+
if v is cls : return True
|
|
10
|
+
|
|
11
|
+
res = False
|
|
12
|
+
for a in v.__bases__ :
|
|
13
|
+
if a is cls :
|
|
14
|
+
return True
|
|
15
|
+
res = res or _recClassCheck(a, cls)
|
|
16
|
+
return res
|
|
17
|
+
|
|
18
|
+
def isRabaClass(v) :
|
|
19
|
+
return _recClassCheck(v, Raba)
|
|
20
|
+
|
|
21
|
+
def isRabaList(v) :
|
|
22
|
+
return _recClassCheck(v.__class__, RabaList) or isRabaListPupa(v)
|
|
23
|
+
|
|
24
|
+
def isRabaListPupa(v) :
|
|
25
|
+
return _recClassCheck(v.__class__, RabaListPupa)
|
|
26
|
+
|
|
27
|
+
def isRabaObject(v) :
|
|
28
|
+
return isRabaClass(v.__class__) or isRabaObjectPupa(v)
|
|
29
|
+
|
|
30
|
+
def isRabaObjectPupa(v) :
|
|
31
|
+
return _recClassCheck(v.__class__, RabaPupa)
|
|
32
|
+
|
|
33
|
+
def isPythonPrimitive(v) :
|
|
34
|
+
primTypes = [int, int, float, bytes, str, memoryview, type(None)]
|
|
35
|
+
for t in primTypes :
|
|
36
|
+
if isinstance(v, t) :
|
|
37
|
+
return True
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
_RabaList_instances = {}
|
|
41
|
+
def _registerRabaListInstance(lst, anchorObj, relationName) :
|
|
42
|
+
global _RabaList_instances
|
|
43
|
+
key = (anchorObj._runtimeId, relationName)
|
|
44
|
+
_RabaList_instances[key] = lst
|
|
45
|
+
|
|
46
|
+
def _unregisterRabaListInstance(lst) :
|
|
47
|
+
global _RabaList_instances
|
|
48
|
+
key = (lst.anchorObj._runtimeId, lst.relationName)
|
|
49
|
+
try :
|
|
50
|
+
del(_RabaList_instances[key])
|
|
51
|
+
except KeyError :
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def _getRabaListInstance(anchorObj, relationName) :
|
|
55
|
+
global _RabaList_instances
|
|
56
|
+
key = (anchorObj._runtimeId, relationName)
|
|
57
|
+
return _RabaList_instances[key]
|
|
58
|
+
|
|
59
|
+
_RabaObject_instances = {}
|
|
60
|
+
def _registerRabaObjectInstance(obj) :
|
|
61
|
+
global _RabaObject_instances
|
|
62
|
+
key = (obj.__class__, obj._raba_namespace, obj.raba_id)
|
|
63
|
+
_RabaObject_instances[key] = obj
|
|
64
|
+
|
|
65
|
+
def _unregisterRabaObjectInstance(obj) :
|
|
66
|
+
global _RabaObject_instances
|
|
67
|
+
key = (obj.__class__, obj._raba_namespace, obj.raba_id)
|
|
68
|
+
|
|
69
|
+
if not isRabaObjectPupa(obj) :
|
|
70
|
+
for l in obj.rabaLists :
|
|
71
|
+
_unregisterRabaListInstance(l)
|
|
72
|
+
|
|
73
|
+
try :
|
|
74
|
+
del(_RabaObject_instances[key])
|
|
75
|
+
except KeyError :
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
def _getRabaObjectInstance(cls, namespace, raba_id) :
|
|
79
|
+
global _RabaObject_instances
|
|
80
|
+
key = (cls, namespace, raba_id)
|
|
81
|
+
return _RabaObject_instances[key]
|
|
82
|
+
|
|
83
|
+
class _RabaListPupaSingleton_Metaclass(abc.ABCMeta):
|
|
84
|
+
|
|
85
|
+
def __call__(clsObj, *args, **kwargs):
|
|
86
|
+
anchorObj = kwargs['anchorObj']
|
|
87
|
+
relationName = kwargs['relationName']
|
|
88
|
+
length = kwargs['length']
|
|
89
|
+
|
|
90
|
+
try :
|
|
91
|
+
return _getRabaListInstance(anchorObj, relationName)
|
|
92
|
+
except KeyError:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
connection = RabaConnection(anchorObj._raba_namespace)
|
|
96
|
+
|
|
97
|
+
obj = super(_RabaListPupaSingleton_Metaclass, clsObj).__call__(*args, anchorObj = anchorObj, relationName = relationName, length = length)
|
|
98
|
+
|
|
99
|
+
_registerRabaListInstance(obj, anchorObj, relationName)
|
|
100
|
+
|
|
101
|
+
return obj
|
|
102
|
+
|
|
103
|
+
class _RabaListSingleton_Metaclass(abc.ABCMeta):
|
|
104
|
+
def __call__(clsObj, *args, **kwargs):
|
|
105
|
+
|
|
106
|
+
if 'anchorObj' in kwargs and 'relationName' in kwargs :
|
|
107
|
+
anchorObj = kwargs['anchorObj']
|
|
108
|
+
relationName = kwargs['relationName']
|
|
109
|
+
|
|
110
|
+
try :
|
|
111
|
+
return _getRabaListInstance(anchorObj, relationName)
|
|
112
|
+
except KeyError:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
obj = super(_RabaListSingleton_Metaclass, clsObj).__call__(*args, **kwargs)
|
|
116
|
+
|
|
117
|
+
_registerRabaListInstance(obj, anchorObj, relationName)
|
|
118
|
+
|
|
119
|
+
return super(_RabaListSingleton_Metaclass, clsObj).__call__(*args, **kwargs)
|
|
120
|
+
|
|
121
|
+
class _RabaPupaSingleton_Metaclass(type):
|
|
122
|
+
def __call__(clsObj, *args, **kwargs):
|
|
123
|
+
|
|
124
|
+
if 'classObj' in kwargs :
|
|
125
|
+
cls = kwargs['classObj']
|
|
126
|
+
else :
|
|
127
|
+
cls = args[0]
|
|
128
|
+
|
|
129
|
+
if 'raba_id' in kwargs :
|
|
130
|
+
raba_id = kwargs['raba_id']
|
|
131
|
+
else :
|
|
132
|
+
raba_id = args[1]
|
|
133
|
+
|
|
134
|
+
try :
|
|
135
|
+
return _getRabaObjectInstance(cls, cls._raba_namespace, raba_id)
|
|
136
|
+
except KeyError :
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
obj = super(_RabaPupaSingleton_Metaclass, clsObj).__call__(*args, **kwargs)
|
|
140
|
+
_registerRabaObjectInstance(obj)
|
|
141
|
+
|
|
142
|
+
return obj
|
|
143
|
+
|
|
144
|
+
class _RabaSingleton_MetaClass(type) :
|
|
145
|
+
|
|
146
|
+
_instances = {}
|
|
147
|
+
|
|
148
|
+
def __new__(cls, name, bases, dct) :
|
|
149
|
+
if '_raba_abstract' not in dct or not dct['_raba_abstract'] :
|
|
150
|
+
def getFields_rec(name, sqlFields, columns, columnsToLowerCase, dct, bases) :
|
|
151
|
+
i = 0
|
|
152
|
+
if name != "Raba" :
|
|
153
|
+
for base in bases :
|
|
154
|
+
i += getFields_rec(base.__name__, sqlFields, columns, columnsToLowerCase, base.__dict__, base.__bases__)
|
|
155
|
+
|
|
156
|
+
for k, v in dct.items() :
|
|
157
|
+
if RabaFields.isField(v) :
|
|
158
|
+
sk = str(k)
|
|
159
|
+
if k.lower() != 'raba_id' and k.lower() != 'json' :
|
|
160
|
+
sqlFields.append(sk)
|
|
161
|
+
|
|
162
|
+
columns[sk] = i
|
|
163
|
+
columnsToLowerCase[sk.lower()] = sk
|
|
164
|
+
i += 1
|
|
165
|
+
return i
|
|
166
|
+
|
|
167
|
+
sqlFields = []
|
|
168
|
+
columns = {}
|
|
169
|
+
columnsToLowerCase = {}
|
|
170
|
+
getFields_rec(name, sqlFields, columns, columnsToLowerCase, dct, bases)
|
|
171
|
+
columns["raba_id"] = 0
|
|
172
|
+
columns['json'] = 1
|
|
173
|
+
|
|
174
|
+
try :
|
|
175
|
+
con = RabaConnection(dct['_raba_namespace'])
|
|
176
|
+
except KeyError :
|
|
177
|
+
raise ValueError("The class %s has no defined namespace, please add a valid '_raba_namespace' to class attributes" % name)
|
|
178
|
+
|
|
179
|
+
uniqueStr = ''
|
|
180
|
+
if '_raba_uniques' in dct :
|
|
181
|
+
for c in dct['_raba_uniques'] :
|
|
182
|
+
if type(c) is str :
|
|
183
|
+
uniqueStr += 'UNIQUE (%s) ON CONFLICT REPLACE, ' % c
|
|
184
|
+
elif len(c) == 1 :
|
|
185
|
+
uniqueStr += 'UNIQUE (%s) ON CONFLICT REPLACE, ' % c[0]
|
|
186
|
+
else :
|
|
187
|
+
uniqueStr += 'UNIQUE %s ON CONFLICT REPLACE, ' % str(c)
|
|
188
|
+
uniqueStr = uniqueStr[:-2]
|
|
189
|
+
|
|
190
|
+
if not con.tableExits(name) :
|
|
191
|
+
idJsonStr = 'raba_id INTEGER PRIMARY KEY, json '
|
|
192
|
+
if len(sqlFields) > 0 :
|
|
193
|
+
if len(uniqueStr) > 0 :
|
|
194
|
+
con.createTable(name, '%s, %s, %s' % (idJsonStr, ', '.join(list(sqlFields)), uniqueStr))
|
|
195
|
+
else :
|
|
196
|
+
con.createTable(name, '%s, %s' % (idJsonStr, ', '.join(list(sqlFields))))
|
|
197
|
+
else :
|
|
198
|
+
con.createTable(name, '%s' % idJsonStr)
|
|
199
|
+
|
|
200
|
+
sqlCons = 'INSERT INTO raba_tables_constraints (table_name, constraints) VALUES (?, ?)'
|
|
201
|
+
con.execute(sqlCons, (name, uniqueStr))
|
|
202
|
+
else :
|
|
203
|
+
sql = 'SELECT constraints FROM raba_tables_constraints WHERE table_name = ?'
|
|
204
|
+
|
|
205
|
+
cur = con.execute(sql, (name,))
|
|
206
|
+
res = cur.fetchone()
|
|
207
|
+
|
|
208
|
+
if res != None and res[0]!= '' and res[0] != uniqueStr :
|
|
209
|
+
sys.stderr.write('Warning: The unique contraints have changed from:\n\t%s\n\nto:\n\t%s.\n-Unique constraints modification is not supported yet-\n' %(res[0], uniqueStr))
|
|
210
|
+
#raise FutureWarning('Warning: The unique contraints have changed from:\n\t%s\n\nto:\n\t%s.\n-Unique constraints modification is not supported yet-\n' %(res[0], uniqueStr))
|
|
211
|
+
|
|
212
|
+
cur = con.execute('PRAGMA table_info("%s")' % name)
|
|
213
|
+
tableColumns = set()
|
|
214
|
+
tableColumnsToKeep = []
|
|
215
|
+
tableColumnsToDrop = []
|
|
216
|
+
|
|
217
|
+
mustClean = False
|
|
218
|
+
columns = {}
|
|
219
|
+
columns['raba_id'] = 0
|
|
220
|
+
columns['json'] = 1
|
|
221
|
+
i = len(columns)
|
|
222
|
+
for c in cur :
|
|
223
|
+
if c[1] != 'raba_id' and c[1] != 'json' :
|
|
224
|
+
if c[1].lower() not in columnsToLowerCase :
|
|
225
|
+
mustClean = True
|
|
226
|
+
con.dropRabalist(name, c[1])
|
|
227
|
+
tableColumnsToDrop.append(c[1])
|
|
228
|
+
else :
|
|
229
|
+
tableColumnsToKeep.append(c[1])
|
|
230
|
+
columns[columnsToLowerCase[c[1].lower()]] = i
|
|
231
|
+
i += 1
|
|
232
|
+
tableColumns.add(c[1].lower())
|
|
233
|
+
|
|
234
|
+
if mustClean :
|
|
235
|
+
con.dropColumnsFromRabaObjTable(name, tableColumnsToKeep)
|
|
236
|
+
|
|
237
|
+
mustAlter = False
|
|
238
|
+
for k in columnsToLowerCase :
|
|
239
|
+
if k not in tableColumns :
|
|
240
|
+
con.execute('ALTER TABLE %s ADD COLUMN %s' % (name, columnsToLowerCase[k]))
|
|
241
|
+
mustAlter = True
|
|
242
|
+
|
|
243
|
+
if mustClean or mustAlter :
|
|
244
|
+
con.forceCommit()
|
|
245
|
+
|
|
246
|
+
dct['columns'] = columns
|
|
247
|
+
dct['columnsToLowerCase'] = columnsToLowerCase
|
|
248
|
+
|
|
249
|
+
clsObj = type.__new__(cls, name, bases, dct)
|
|
250
|
+
con.registerRabaClass(clsObj)
|
|
251
|
+
return clsObj
|
|
252
|
+
|
|
253
|
+
return type.__new__(cls, name, bases, dct)
|
|
254
|
+
|
|
255
|
+
def __call__(cls, *args, **fieldsDct) :
|
|
256
|
+
if cls == Raba :
|
|
257
|
+
return super(_RabaSingleton_MetaClass, cls).__call__(**fieldsDct)
|
|
258
|
+
|
|
259
|
+
if 'raba_id' in fieldsDct :
|
|
260
|
+
try :
|
|
261
|
+
return _getRabaObjectInstance(cls, cls._raba_namespace, fieldsDct['raba_id'])
|
|
262
|
+
except KeyError :
|
|
263
|
+
pass
|
|
264
|
+
else :
|
|
265
|
+
key = None
|
|
266
|
+
|
|
267
|
+
params = copy.copy(fieldsDct)
|
|
268
|
+
nonRabaParams = {}
|
|
269
|
+
for p, v in list(params.items()) :
|
|
270
|
+
if p in cls.columns :
|
|
271
|
+
if isRabaObject(v) :
|
|
272
|
+
params[p] = v.getJsonEncoding()
|
|
273
|
+
else :
|
|
274
|
+
nonRabaParams[p] = v
|
|
275
|
+
del(params[p])
|
|
276
|
+
|
|
277
|
+
connection = RabaConnection(cls._raba_namespace)
|
|
278
|
+
ret = connection.getRabaObjectInfos(cls.__name__, params)
|
|
279
|
+
|
|
280
|
+
if ret != None :
|
|
281
|
+
dbLine = ret.fetchone()
|
|
282
|
+
else :
|
|
283
|
+
dbLine = None
|
|
284
|
+
|
|
285
|
+
if dbLine != None :
|
|
286
|
+
if ret.fetchone() != None :
|
|
287
|
+
raise ValueError("More than one object fit the arguments you've provided to the constructor")
|
|
288
|
+
|
|
289
|
+
raba_id = dbLine[0]
|
|
290
|
+
try :
|
|
291
|
+
return _getRabaObjectInstance(cls, cls._raba_namespace, raba_id)
|
|
292
|
+
except KeyError :
|
|
293
|
+
pass
|
|
294
|
+
|
|
295
|
+
obj = Raba.__new__(cls, *args, **nonRabaParams)
|
|
296
|
+
obj._raba__init__(initDbLine = dbLine)
|
|
297
|
+
obj.__init__(*args, **nonRabaParams)
|
|
298
|
+
|
|
299
|
+
if not hasattr(cls, '_raba_not_a_singleton') or not getattr(cls, '_raba_not_a_singleton') :
|
|
300
|
+
_registerRabaObjectInstance(obj)
|
|
301
|
+
|
|
302
|
+
elif len(params) > 0 : # params provided but no result
|
|
303
|
+
raise KeyError("Couldn't find any object that fit the arguments you've provided to the constructor")
|
|
304
|
+
else :
|
|
305
|
+
obj = type.__call__(cls, *args, **fieldsDct)
|
|
306
|
+
obj._raba__init__(**fieldsDct)
|
|
307
|
+
|
|
308
|
+
return obj
|
|
309
|
+
|
|
310
|
+
def freeRegistery() :
|
|
311
|
+
"""Empties all registeries. This is useful if you want to allow the garbage collector to free the memory
|
|
312
|
+
taken by the objects you've already loaded. Be careful might cause some discrepenties in your scripts"""
|
|
313
|
+
freeListRegistery()
|
|
314
|
+
freeObjectRegistery()
|
|
315
|
+
|
|
316
|
+
def freeListRegistery() :
|
|
317
|
+
"""same as freeRegistery() bu only for lists"""
|
|
318
|
+
global _RabaList_instances
|
|
319
|
+
_RabaList_instances = {}
|
|
320
|
+
|
|
321
|
+
def freeObjectRegistery() :
|
|
322
|
+
"""same as freeRegistery() bu only for objects"""
|
|
323
|
+
global _RabaObject_instances
|
|
324
|
+
_RabaObject_instances = {}
|
|
325
|
+
|
|
326
|
+
def removeFromRegistery(obj) :
|
|
327
|
+
"""Removes an object/rabalist from registery. This is useful if you want to allow the garbage collector to free the memory
|
|
328
|
+
taken by the objects you've already loaded. Be careful might cause some discrepenties in your scripts. For objects,
|
|
329
|
+
cascades to free the registeries of related rabalists also"""
|
|
330
|
+
|
|
331
|
+
if isRabaObject(obj) :
|
|
332
|
+
_unregisterRabaObjectInstance(obj)
|
|
333
|
+
elif isRabaList(obj) :
|
|
334
|
+
_unregisterRabaListInstance(obj)
|
|
335
|
+
|
|
336
|
+
class RabaPupa(object, metaclass=_RabaPupaSingleton_Metaclass) :
|
|
337
|
+
"""One of the founding principles of RabaDB is to separate the storage from the code. Fields are stored in the DB while the processing only depends
|
|
338
|
+
on your python code. This approach ensures a higher degree of stability by preventing old objects from lurking inside the DB before popping out of nowhere several decades afterwards.
|
|
339
|
+
According to this apparoach, raba objects are not serialised but transformed into pupas before being stored. A pupa is a very light object that contains only a reference
|
|
340
|
+
to the raba object class, and it's unique raba_id. Upon asking for one of the attributes of a pupa, it magically transforms into a full fledged raba object. This process is completly transparent to the user. Pupas also have the advantage of being light weight and also ensure that the only raba objects loaded are those explicitely accessed, thus potentialy saving a lot of memory.
|
|
341
|
+
For a pupa self._rabaClass refers to the class of the object "inside" the pupa.
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
def __init__(self, classObj, raba_id) :
|
|
345
|
+
self._rabaClass = classObj
|
|
346
|
+
self.raba_id = raba_id
|
|
347
|
+
self._raba_namespace = classObj._raba_namespace
|
|
348
|
+
self.__doc__ = classObj.__doc__
|
|
349
|
+
|
|
350
|
+
def develop(self) :
|
|
351
|
+
def getAttr(name) :
|
|
352
|
+
return object.__getattribute__(self, name)
|
|
353
|
+
|
|
354
|
+
def setAttr(name, value) :
|
|
355
|
+
object.__setattr__(self, name, value)
|
|
356
|
+
|
|
357
|
+
rabaClass = getAttr('_rabaClass')
|
|
358
|
+
uniqueId = getAttr('raba_id')
|
|
359
|
+
connection = RabaConnection(getAttr('_raba_namespace'))
|
|
360
|
+
dbLine = connection.getRabaObjectInfos(getAttr('_rabaClass').__name__, {'raba_id' : uniqueId}).fetchone()
|
|
361
|
+
setAttr('__class__', rabaClass)
|
|
362
|
+
purge = getAttr('__dict__').keys()
|
|
363
|
+
for k in list(purge) :
|
|
364
|
+
delattr(self, k)
|
|
365
|
+
|
|
366
|
+
self._rabaClass = rabaClass
|
|
367
|
+
self.connection = connection
|
|
368
|
+
rabaClass._raba__init__(self, initDbLine = dbLine)
|
|
369
|
+
rabaClass.__init__(self)
|
|
370
|
+
|
|
371
|
+
def getDctDescription(self) :
|
|
372
|
+
"returns a dict describing the object"
|
|
373
|
+
return {'type' : RabaFields.RABA_FIELD_TYPE_IS_RABA_OBJECT, 'className' : self._rabaClass.__name__, 'raba_id' : self.raba_id, 'raba_namespace' : self._raba_namespace}
|
|
374
|
+
|
|
375
|
+
def getJsonEncoding(self) :
|
|
376
|
+
"returns a json encoding of self.getDctDescription()"
|
|
377
|
+
return json.dumps(self.getDctDescription(), sort_keys=True) # sort_keys added during migration to python3
|
|
378
|
+
|
|
379
|
+
def __getattr__(self, name) :
|
|
380
|
+
develop = object.__getattribute__(self, "develop")
|
|
381
|
+
develop()
|
|
382
|
+
|
|
383
|
+
return self.__getattribute__(name)
|
|
384
|
+
|
|
385
|
+
def __str__(self) :
|
|
386
|
+
self.develop()
|
|
387
|
+
return str(self)
|
|
388
|
+
|
|
389
|
+
def __repr__(self) :
|
|
390
|
+
return "<RabaObj pupa: %s, raba_id %s>" % (self._rabaClass.__name__, self.raba_id)
|
|
391
|
+
|
|
392
|
+
class Raba(object, metaclass=_RabaSingleton_MetaClass):
|
|
393
|
+
"All raba object inherit from this class"
|
|
394
|
+
raba_id = RabaFields.Primitive()
|
|
395
|
+
json = RabaFields.Primitive()
|
|
396
|
+
_raba_abstract = True
|
|
397
|
+
|
|
398
|
+
def __init__(self, *a, **b) :
|
|
399
|
+
pass
|
|
400
|
+
|
|
401
|
+
def unreference(self) :
|
|
402
|
+
"explicit deletes the object from the singleton reference dictionary. This is mandatory to be able to delete the object using del(). Also, any attempt to reload an object with the same parameters will result un a new instance being created"
|
|
403
|
+
_unregisterRabaObjectInstance(self)
|
|
404
|
+
|
|
405
|
+
def _initDbLine(self, dbLine) :
|
|
406
|
+
self.raba_id = dbLine[self.__class__.columns['raba_id']]
|
|
407
|
+
self.json = dbLine[self.__class__.columns['json']]
|
|
408
|
+
|
|
409
|
+
lists = []
|
|
410
|
+
for kk, i in self.columns.items() :
|
|
411
|
+
k = self.columnsToLowerCase[kk.lower()]
|
|
412
|
+
elmt = getattr(self._rabaClass, k)
|
|
413
|
+
if RabaFields.isPrimitiveField(elmt) :
|
|
414
|
+
try :
|
|
415
|
+
self.__setattr__(k, pickle.loads(bytes(dbLine[i])))
|
|
416
|
+
except :
|
|
417
|
+
self.__setattr__(k, dbLine[i])
|
|
418
|
+
|
|
419
|
+
elif RabaFields.isRabaObjectField(elmt) :
|
|
420
|
+
if dbLine[i] != None :
|
|
421
|
+
val = json.loads(dbLine[i])
|
|
422
|
+
objClass = RabaConnection(val["raba_namespace"]).getClass(val["className"])
|
|
423
|
+
self.__setattr__(k, RabaPupa(objClass, val["raba_id"]))
|
|
424
|
+
elif RabaFields.isRabaListField(elmt) :
|
|
425
|
+
if dbLine[i] == None :
|
|
426
|
+
lists.append((k, 0))
|
|
427
|
+
else :
|
|
428
|
+
lists.append((k, int(dbLine[i])))
|
|
429
|
+
else :
|
|
430
|
+
raise ValueError("Unable to set field %s to %s in Raba object %s" %(k, dbLine[i], self._rabaClass.__name__))
|
|
431
|
+
|
|
432
|
+
#~ self.rabaLists = []
|
|
433
|
+
for k, leng in lists :
|
|
434
|
+
rlp = RabaListPupa(anchorObj = self, relationName = k, length = leng)
|
|
435
|
+
self.__setattr__(k, rlp)
|
|
436
|
+
self.rabaLists.append(rlp)
|
|
437
|
+
|
|
438
|
+
def _raba__init__(self, **fieldsSet) :
|
|
439
|
+
|
|
440
|
+
self.sqlSave = {}
|
|
441
|
+
self.sqlSaveQMarks = {}
|
|
442
|
+
self.listsToSave = {}
|
|
443
|
+
self.rabaLists = []
|
|
444
|
+
|
|
445
|
+
if self.__class__ is Raba :
|
|
446
|
+
raise TypeError('Raba class should never be instanciated, use inheritance')
|
|
447
|
+
|
|
448
|
+
self._runtimeId = (self.__class__.__name__, random.random()) #this is used only during runtime ex, to avoid circular calls
|
|
449
|
+
self._rabaClass = self.__class__
|
|
450
|
+
|
|
451
|
+
self.connection = RabaConnection(self._rabaClass._raba_namespace)
|
|
452
|
+
self.rabaConfiguration = RabaConfiguration(self._rabaClass._raba_namespace)
|
|
453
|
+
|
|
454
|
+
self._saved = False #True if present in the database
|
|
455
|
+
|
|
456
|
+
if 'initDbLine' in fieldsSet and 'initDbLine' != None :
|
|
457
|
+
self._initDbLine(fieldsSet['initDbLine'])
|
|
458
|
+
self._saved = True
|
|
459
|
+
|
|
460
|
+
if self.raba_id == None :
|
|
461
|
+
self.raba_id = self.connection.getNextRabaId(self)
|
|
462
|
+
|
|
463
|
+
def pupa(self) :
|
|
464
|
+
"""returns a pupa version of self"""
|
|
465
|
+
return RabaPupa(self.__class__, self.raba_id)
|
|
466
|
+
|
|
467
|
+
def develop(self) :
|
|
468
|
+
"Dummy fct, so when you call develop on a full developed object you don't get nasty exceptions"
|
|
469
|
+
pass
|
|
470
|
+
|
|
471
|
+
@classmethod
|
|
472
|
+
def _parseIndex(cls, fields) :
|
|
473
|
+
con = RabaConnection(cls._raba_namespace)
|
|
474
|
+
ff = []
|
|
475
|
+
rlf = []
|
|
476
|
+
tmpf = []
|
|
477
|
+
if type(fields) is str :
|
|
478
|
+
tmpf.append(fields)
|
|
479
|
+
else :
|
|
480
|
+
tmpf = fields
|
|
481
|
+
|
|
482
|
+
for field in tmpf :
|
|
483
|
+
if RabaFields.isRabaListField(getattr(cls, field)) :
|
|
484
|
+
lname = con.makeRabaListTableName(cls.__name__, field)
|
|
485
|
+
rlf.append(lname, )
|
|
486
|
+
else :
|
|
487
|
+
ff.append(field)
|
|
488
|
+
|
|
489
|
+
return rlf, ff
|
|
490
|
+
|
|
491
|
+
@classmethod
|
|
492
|
+
def ensureIndex(cls, fields, where = '', whereValues = []) :
|
|
493
|
+
"""Add an index for field, indexes take place and slow down saves and deletes but they speed up a lot everything else. If you are going to do a lot of saves/deletes drop the indexes first re-add them afterwards
|
|
494
|
+
Fields can be a list of fields for Multi-Column Indices or simply the name of a single field. But as RabaList are basicaly in separate tables you cannot create a multicolumn indice on them. A single index will
|
|
495
|
+
be create for the RabaList alone"""
|
|
496
|
+
con = RabaConnection(cls._raba_namespace)
|
|
497
|
+
rlf, ff = cls._parseIndex(fields)
|
|
498
|
+
ww = []
|
|
499
|
+
for i in range(len(whereValues)) :
|
|
500
|
+
if isRabaObject(whereValues[i]) :
|
|
501
|
+
ww.append(whereValues[i].getJsonEncoding())
|
|
502
|
+
|
|
503
|
+
for name in rlf :
|
|
504
|
+
con.createIndex(name, 'anchor_raba_id')
|
|
505
|
+
|
|
506
|
+
if len(ff) > 0 :
|
|
507
|
+
con.createIndex(cls.__name__, ff, where = where, whereValues = ww)
|
|
508
|
+
con.commit()
|
|
509
|
+
|
|
510
|
+
@classmethod
|
|
511
|
+
def dropIndex(cls, fields) :
|
|
512
|
+
"removes an index created with ensureIndex "
|
|
513
|
+
con = RabaConnection(cls._raba_namespace)
|
|
514
|
+
rlf, ff = cls._parseIndex(fields)
|
|
515
|
+
|
|
516
|
+
for name in rlf :
|
|
517
|
+
con.dropIndex(name, 'anchor_raba_id')
|
|
518
|
+
|
|
519
|
+
con.dropIndex(cls.__name__, ff)
|
|
520
|
+
con.commit()
|
|
521
|
+
|
|
522
|
+
@classmethod
|
|
523
|
+
def getIndexes(cls) :
|
|
524
|
+
"returns a list of the indexes of a class"
|
|
525
|
+
con = RabaConnection(cls._raba_namespace)
|
|
526
|
+
idxs = []
|
|
527
|
+
for idx in con.getIndexes(rabaOnly = True) :
|
|
528
|
+
if idx[2] == cls.__name__ :
|
|
529
|
+
idxs.append(idx)
|
|
530
|
+
else :
|
|
531
|
+
for k in cls.columns :
|
|
532
|
+
if RabaFields.isRabaListField(getattr(cls, k)) and idx[2] == con.makeRabaListTableName(cls.__name__, k) :
|
|
533
|
+
idxs.append(idx)
|
|
534
|
+
return idxs
|
|
535
|
+
|
|
536
|
+
@classmethod
|
|
537
|
+
def flushIndexes(cls) :
|
|
538
|
+
"drops all indexes for a class"
|
|
539
|
+
con = RabaConnection(cls._raba_namespace)
|
|
540
|
+
for idx in cls.getIndexes() :
|
|
541
|
+
con.dropIndexByName(idx[1])
|
|
542
|
+
|
|
543
|
+
def mutated(self) :
|
|
544
|
+
'returns True if the object has changed since the last save'
|
|
545
|
+
return len(self.sqlSave) > 0 or len(self.listsToSave) > 0
|
|
546
|
+
|
|
547
|
+
def save(self) :
|
|
548
|
+
if self.mutated() :
|
|
549
|
+
# Reset the savedObject set so this save session starts fresh.
|
|
550
|
+
# Without this, lists saved in a previous save() call would
|
|
551
|
+
# be skipped by registerSave() and never re-saved.
|
|
552
|
+
self.connection.savedObject = set()
|
|
553
|
+
if not self.raba_id :
|
|
554
|
+
raise ValueError("Field raba_id of self has the not int value %s therefore i cannot save the object, sorry" % (self, ))
|
|
555
|
+
|
|
556
|
+
for k, v in self.listsToSave.items() :
|
|
557
|
+
v._save()
|
|
558
|
+
self.sqlSave[k] = len(v)
|
|
559
|
+
if not self._saved : #this dict is only for optimisation purpose for generating the insert sql
|
|
560
|
+
self.sqlSaveQMarks[k] = '?'
|
|
561
|
+
|
|
562
|
+
self.sqlSave['json'] = self.getJsonEncoding()
|
|
563
|
+
if not self._saved : #this dict is only for optimisation purpose for generating the insert sql
|
|
564
|
+
self.sqlSaveQMarks['json'] = '?'
|
|
565
|
+
|
|
566
|
+
if not self._saved :
|
|
567
|
+
values = list(self.sqlSave.values())
|
|
568
|
+
sql = 'INSERT INTO %s (%s) VALUES (%s)' % (self.__class__.__name__, ', '.join(list(self.sqlSave.keys())), ', '.join(list(self.sqlSaveQMarks.values())))
|
|
569
|
+
else :
|
|
570
|
+
values = list(self.sqlSave.values())
|
|
571
|
+
sql = 'UPDATE %s SET %s = ? WHERE raba_id = ?' % (self.__class__.__name__, ' = ?, '.join(list(self.sqlSave.keys())))
|
|
572
|
+
values.append(self.raba_id)
|
|
573
|
+
|
|
574
|
+
self.connection.execute(sql, values)
|
|
575
|
+
self.connection.commit()
|
|
576
|
+
self._saved = True
|
|
577
|
+
self.sqlSave = {}
|
|
578
|
+
self.sqlSaveQMarks = {}
|
|
579
|
+
self.listsToSave = {}
|
|
580
|
+
|
|
581
|
+
def delete(self) :
|
|
582
|
+
if self._saved :
|
|
583
|
+
for c in self.columnsToLowerCase.values() :
|
|
584
|
+
if isRabaList(getattr(self, c)) :
|
|
585
|
+
getattr(self, c).empty()
|
|
586
|
+
self.connection.delete(table = self.__class__.__name__, where = 'raba_id = ?', values = (self.raba_id, ))
|
|
587
|
+
self.connection.commit()
|
|
588
|
+
|
|
589
|
+
def copy(self) :
|
|
590
|
+
v = copy.copy(self)
|
|
591
|
+
v.raba_id = None
|
|
592
|
+
return v
|
|
593
|
+
|
|
594
|
+
def getDctDescription(self) :
|
|
595
|
+
"returns a dict describing the object"
|
|
596
|
+
return {'type' : RabaFields.RABA_FIELD_TYPE_IS_RABA_OBJECT, 'className' : self._rabaClass.__name__, 'raba_id' : self.raba_id, 'raba_namespace' : self._raba_namespace}
|
|
597
|
+
|
|
598
|
+
def getJsonEncoding(self) :
|
|
599
|
+
"returns a json encoding of self.getDctDescription()"
|
|
600
|
+
return json.dumps(self.getDctDescription(), sort_keys=True) # sort_keys added during migration to python3
|
|
601
|
+
|
|
602
|
+
def set(self, **args) :
|
|
603
|
+
"set multiple values quickly, ex : name = woopy"
|
|
604
|
+
for k, v in args.items() :
|
|
605
|
+
setattr(self, k, v)
|
|
606
|
+
|
|
607
|
+
def __setattr__(self, k, v) :
|
|
608
|
+
"This also keeps track of wich fields have been updated."
|
|
609
|
+
vv = v
|
|
610
|
+
if hasattr(self.__class__, k) and RabaFields.isField(getattr(self.__class__, k)) :
|
|
611
|
+
vSQL = None
|
|
612
|
+
if not RabaFields.isRabaListField(getattr(self.__class__, k)) :
|
|
613
|
+
classType = getattr(self.__class__, k)
|
|
614
|
+
if not classType.check(vv) :
|
|
615
|
+
raise ValueError("Unable to set '%s' to value '%s'. Constrain function violation" % (k, vv))
|
|
616
|
+
if isRabaObject(vv) :
|
|
617
|
+
vSQL = vv.getJsonEncoding()
|
|
618
|
+
elif isPythonPrimitive(vv):
|
|
619
|
+
vSQL = vv
|
|
620
|
+
else :
|
|
621
|
+
vSQL = memoryview(pickle.dumps(vv))
|
|
622
|
+
|
|
623
|
+
self.sqlSave[k] = vSQL
|
|
624
|
+
|
|
625
|
+
if not self._saved : #this dict is only for optimisation purpose for generating the insert sql
|
|
626
|
+
self.sqlSaveQMarks[k] = '?'
|
|
627
|
+
else :
|
|
628
|
+
if not isRabaList(vv) and not isRabaListPupa(vv) :
|
|
629
|
+
try :
|
|
630
|
+
vv = RabaList(v)
|
|
631
|
+
except :
|
|
632
|
+
raise ValueError("Unable to set '%s' to value '%s'. Value is not a valid RabaList" % (k, vv))
|
|
633
|
+
|
|
634
|
+
currList = object.__getattribute__(self, k)
|
|
635
|
+
if not RabaFields.isRabaListField(currList) and vv is not currList and len(currList) > 0 :
|
|
636
|
+
currList.empty()
|
|
637
|
+
self.connection.unregisterRabalist(anchor_class_name = self.__class__.__name__, anchor_raba_id = self.raba_id, relation_name = k)
|
|
638
|
+
|
|
639
|
+
vv._attachToObject(self, k)
|
|
640
|
+
self.listsToSave[k] = vv #self.sqlSave[k] and self.sqlSaveQMarks[k] are updated in self.save() for lists
|
|
641
|
+
|
|
642
|
+
object.__setattr__(self, k, vv)
|
|
643
|
+
|
|
644
|
+
def __getattribute__(self, k) :
|
|
645
|
+
try :
|
|
646
|
+
elmt = object.__getattribute__(self, k)
|
|
647
|
+
if RabaFields.isRabaListField(elmt) : #if empty
|
|
648
|
+
elmt = RabaListPupa(anchorObj = self, relationName = k, length = -1)
|
|
649
|
+
object.__setattr__(self, k, elmt)
|
|
650
|
+
elif RabaFields.isField(elmt) :
|
|
651
|
+
elmt = elmt.default
|
|
652
|
+
|
|
653
|
+
return elmt
|
|
654
|
+
except AttributeError :
|
|
655
|
+
return self.__getattr__(k)
|
|
656
|
+
|
|
657
|
+
def __getattr__(self, k) :
|
|
658
|
+
raise AttributeError('%s instance has no attribute "%s"' %(self.__class__.__name__, k))
|
|
659
|
+
|
|
660
|
+
def __getitem__(self, k) :
|
|
661
|
+
return self.__getattribute__(k)
|
|
662
|
+
|
|
663
|
+
def __setitem__(self, k, v) :
|
|
664
|
+
self. __setattr__(k, v)
|
|
665
|
+
|
|
666
|
+
def __repr__(self) :
|
|
667
|
+
return "<Raba obj: %s, raba_id: %s>" % (self._runtimeId, self.raba_id)
|
|
668
|
+
|
|
669
|
+
@classmethod
|
|
670
|
+
def getFields(cls) :
|
|
671
|
+
"""returns a set of the available fields. In order to be able ti securely loop of the fields, "raba_id" and "json" are not included in the set"""
|
|
672
|
+
s = set(cls.columns.keys())
|
|
673
|
+
s.remove('json')
|
|
674
|
+
s.remove('raba_id')
|
|
675
|
+
return s
|
|
676
|
+
|
|
677
|
+
@classmethod
|
|
678
|
+
def help(cls) :
|
|
679
|
+
"returns a string of lisinting available fields"
|
|
680
|
+
return 'Available fields for %s: %s' %(cls.__name__, ', '.join(cls.getFields()))
|
|
681
|
+
|
|
682
|
+
class RabaListPupa(MutableSequence, metaclass=_RabaListPupaSingleton_Metaclass) :
|
|
683
|
+
|
|
684
|
+
_isRabaList = True
|
|
685
|
+
|
|
686
|
+
def __init__(self, **kwargs) :
|
|
687
|
+
self._runtimeId = (self.__class__.__name__, random.random()) #this is using only during runtime ex, to avoid circular calls
|
|
688
|
+
self.anchorObj = kwargs['anchorObj']
|
|
689
|
+
self.relationName = kwargs['relationName']
|
|
690
|
+
self.length = kwargs['length']
|
|
691
|
+
self._raba_namespace = self.anchorObj._raba_namespace
|
|
692
|
+
|
|
693
|
+
self.connection = RabaConnection(self._raba_namespace)
|
|
694
|
+
|
|
695
|
+
self.tableName = self.connection.makeRabaListTableName(self.anchorObj._rabaClass.__name__, self.relationName)
|
|
696
|
+
|
|
697
|
+
def develop(self) :
|
|
698
|
+
MutableSequence.__setattr__(self, '__class__', RabaList)
|
|
699
|
+
|
|
700
|
+
initFromPupa = {}
|
|
701
|
+
|
|
702
|
+
initFromPupa['relationName'] = MutableSequence.__getattribute__(self, 'relationName')
|
|
703
|
+
initFromPupa['anchorObj'] = MutableSequence.__getattribute__(self, 'anchorObj')
|
|
704
|
+
initFromPupa['_raba_namespace'] = MutableSequence.__getattribute__(self, '_raba_namespace')
|
|
705
|
+
initFromPupa['tableName'] = MutableSequence.__getattribute__(self, 'tableName')
|
|
706
|
+
initFromPupa['length'] = MutableSequence.__getattribute__(self, 'length')
|
|
707
|
+
#initFromPupa['raba_id'] = MutableSequence.__getattribute__(self, 'raba_id')
|
|
708
|
+
|
|
709
|
+
purge = MutableSequence.__getattribute__(self, '__dict__').keys()
|
|
710
|
+
for k in list(purge) :
|
|
711
|
+
delattr(self, k)
|
|
712
|
+
|
|
713
|
+
RabaList.__init__(self, initFromPupa = initFromPupa)
|
|
714
|
+
RabaList._attachToObject(self, initFromPupa['anchorObj'], initFromPupa['relationName'])
|
|
715
|
+
|
|
716
|
+
def __getitem__(self, i) :
|
|
717
|
+
self.develop()
|
|
718
|
+
return self[i]
|
|
719
|
+
|
|
720
|
+
def __delitem__(self, i) :
|
|
721
|
+
self.develop()
|
|
722
|
+
self.__delitem__(i)
|
|
723
|
+
|
|
724
|
+
def __setitem__(self, k, v) :
|
|
725
|
+
self.develop()
|
|
726
|
+
self.__setitem__(k, v)
|
|
727
|
+
|
|
728
|
+
def insert(self, i, v) :
|
|
729
|
+
self.develop()
|
|
730
|
+
self.insert(i, v)
|
|
731
|
+
|
|
732
|
+
def append(self, k) :
|
|
733
|
+
self.develop()
|
|
734
|
+
self.append(k)
|
|
735
|
+
|
|
736
|
+
def _attachToObject(self, anchorObj, relationName) :
|
|
737
|
+
"dummy fct for compatibility reasons, a RabaListPupa is attached by default"
|
|
738
|
+
#MutableSequence.__getattribute__(self, "develop")()
|
|
739
|
+
self.develop()
|
|
740
|
+
self._attachToObject(anchorObj, relationName)
|
|
741
|
+
|
|
742
|
+
def _save(self, *args, **kwargs) :
|
|
743
|
+
"dummy fct for compatibility reasons, a RabaListPupa represents an unmodified list so there's nothing to save"
|
|
744
|
+
self.develop()
|
|
745
|
+
RabaList._save(self)
|
|
746
|
+
|
|
747
|
+
def __getattr__(self, name) :
|
|
748
|
+
self.develop()
|
|
749
|
+
return MutableSequence.__getattribute__(self, name)
|
|
750
|
+
|
|
751
|
+
def __repr__(self) :
|
|
752
|
+
return "[%s length: %d, relationName: %s, anchorObj: %s, raba_id: %s]" % (self._runtimeId, self.length, self.relationName, self.anchorObj, self.raba_id)
|
|
753
|
+
|
|
754
|
+
def __len__(self) :
|
|
755
|
+
if self.length < 0 :
|
|
756
|
+
self.develop()
|
|
757
|
+
return self.length
|
|
758
|
+
|
|
759
|
+
class RabaList(MutableSequence, metaclass=_RabaListSingleton_Metaclass) :
|
|
760
|
+
"""A RabaList is a list that can only contain Raba objects of the same class or (Pupas of the same class). They represent one to many relations and are stored in separate
|
|
761
|
+
tables that contain only one single line"""
|
|
762
|
+
|
|
763
|
+
_isRabaList = True
|
|
764
|
+
|
|
765
|
+
def _checkElmt(self, v) :
|
|
766
|
+
if self.anchorObj != None :
|
|
767
|
+
return getattr(self.anchorObj._rabaClass, self.relationName).check(v)
|
|
768
|
+
else :
|
|
769
|
+
return True
|
|
770
|
+
|
|
771
|
+
def _checkSelf(self) :
|
|
772
|
+
"""Checks the entire list, returns (faultyElmt, list namespace), if the list passes the check, faultyElmt = None"""
|
|
773
|
+
for e in self :
|
|
774
|
+
if not self._checkElmt(e) :
|
|
775
|
+
return e
|
|
776
|
+
return None
|
|
777
|
+
|
|
778
|
+
def _dieInvalidRaba(self, v) :
|
|
779
|
+
st = """The element %s can't be added to the list, possible causes:
|
|
780
|
+
-The element is a RabaObject wich namespace is different from list's namespace
|
|
781
|
+
-The element violates the constraint function""" % v
|
|
782
|
+
|
|
783
|
+
raise TypeError(st)
|
|
784
|
+
|
|
785
|
+
def __init__(self, *listElements, **listArguments) :
|
|
786
|
+
"""To avoid the check of all elements of listElements during initialisation pass : noInitCheck = True as argument
|
|
787
|
+
It is also possible to define both the anchor object and the namespace durint initalisation using argument keywords: anchorObj and namespace. But only do it
|
|
788
|
+
if you really now what you are doing."""
|
|
789
|
+
self._runtimeId = (self.__class__.__name__, random.random())#this is used only during runtime ex, to avoid circular calls
|
|
790
|
+
|
|
791
|
+
self.raba_id = None
|
|
792
|
+
self.relationName = None
|
|
793
|
+
self.tableName = None
|
|
794
|
+
self.anchorObj = None
|
|
795
|
+
|
|
796
|
+
self._raba_namespace = None
|
|
797
|
+
self.connection = None
|
|
798
|
+
self.rabaConfiguration = None
|
|
799
|
+
self._saved = False
|
|
800
|
+
self._mutated = True
|
|
801
|
+
|
|
802
|
+
if len(listElements) > 0 :
|
|
803
|
+
self.data = list(listElements[0])
|
|
804
|
+
else :
|
|
805
|
+
self.data = []
|
|
806
|
+
|
|
807
|
+
if 'initFromPupa' in listArguments :
|
|
808
|
+
pupaInit = listArguments['initFromPupa']
|
|
809
|
+
self.anchorObj = pupaInit['anchorObj']
|
|
810
|
+
self.relationName = pupaInit['relationName']
|
|
811
|
+
self._raba_namespace = self.anchorObj._raba_namespace
|
|
812
|
+
#self.raba_id = pupaInit['raba_id']
|
|
813
|
+
self.tableName = pupaInit['tableName']
|
|
814
|
+
length = pupaInit['length']
|
|
815
|
+
|
|
816
|
+
self._setNamespaceConAndConf(self.anchorObj._raba_namespace)
|
|
817
|
+
self.connection.createRabaListTable(self.tableName)
|
|
818
|
+
#if self.raba_id == None :
|
|
819
|
+
# self.raba_id, self.tableName = self.connection.registerRabalist(self.anchorObj._rabaClass.__name__, self.anchorObj.raba_id, self.relationName)
|
|
820
|
+
|
|
821
|
+
if length > 0 :
|
|
822
|
+
sql, values = 'SELECT * FROM %s WHERE anchor_raba_id = ?' % self.tableName, (self.anchorObj.raba_id, )
|
|
823
|
+
cur = self.connection.execute(sql, values)
|
|
824
|
+
for aidi in cur :
|
|
825
|
+
self._saved = True
|
|
826
|
+
value = aidi[2]
|
|
827
|
+
typ = aidi[3]
|
|
828
|
+
className = aidi[4]
|
|
829
|
+
raba_id = aidi[5]
|
|
830
|
+
raba_namespace = aidi[6]
|
|
831
|
+
if typ == RabaFields.RABA_FIELD_TYPE_IS_PRIMITIVE :
|
|
832
|
+
# Try to unpickle if value is a memoryview/bytes
|
|
833
|
+
# (non-primitive values are pickled before storage)
|
|
834
|
+
if isinstance(value, (memoryview, bytes, bytearray)) :
|
|
835
|
+
try :
|
|
836
|
+
self.append(pickle.loads(bytes(value)))
|
|
837
|
+
except Exception :
|
|
838
|
+
self.append(value)
|
|
839
|
+
else :
|
|
840
|
+
self.append(value)
|
|
841
|
+
elif typ == RabaFields.RABA_FIELD_TYPE_IS_RABA_LIST :
|
|
842
|
+
raise FutureWarning('RabaList in RabaList not supported')
|
|
843
|
+
elif typ == RabaFields.RABA_FIELD_TYPE_IS_RABA_OBJECT :
|
|
844
|
+
classObj = RabaConnection(raba_namespace).getClass(className)
|
|
845
|
+
self.append(RabaPupa(classObj, raba_id))
|
|
846
|
+
else :
|
|
847
|
+
raise ValueError("Unknown type: %s in rabalist %s" % (typ, self))
|
|
848
|
+
|
|
849
|
+
def mutated(self) :
|
|
850
|
+
'returns True if the object has changed since the last save'
|
|
851
|
+
return self._mutated
|
|
852
|
+
|
|
853
|
+
def pupatizeElements(self) :
|
|
854
|
+
"""Transform all raba object into pupas"""
|
|
855
|
+
for i in range(len(self)) :
|
|
856
|
+
self[i] = self[i].pupa()
|
|
857
|
+
|
|
858
|
+
def empty(self) :
|
|
859
|
+
if self.tableName != None and self.anchorObj != None :
|
|
860
|
+
sql = 'DELETE FROM %s WHERE anchor_raba_id = ?' % self.tableName
|
|
861
|
+
self.connection.execute(sql, (self.anchorObj.raba_id,))
|
|
862
|
+
|
|
863
|
+
def _save(self) :
|
|
864
|
+
"""saves the RabaList into it's own table. This a private function that should be called directly
|
|
865
|
+
Before saving the entire list corresponding to the anchorObj is wiped out before being rewritten. The
|
|
866
|
+
alternative would be to keep the sync between the list and the table in real time (remove in both).
|
|
867
|
+
If the current solution proves to be to slow, i'll consider the alternative"""
|
|
868
|
+
|
|
869
|
+
if self.connection.registerSave(self) :
|
|
870
|
+
if len(self) == 0 :
|
|
871
|
+
self.connection.updateRabaListLength(self.raba_id, len(self))
|
|
872
|
+
return True
|
|
873
|
+
else :
|
|
874
|
+
if self.relationName == None or self.anchorObj == None :
|
|
875
|
+
raise ValueError('%s has not been attached to any object, impossible to save it' % (self, ))
|
|
876
|
+
|
|
877
|
+
#if self.raba_id == None :
|
|
878
|
+
# self.raba_id, self.tableName = self.connection.registerRabalist(self.anchorObj._rabaClass.__name__, self.anchorObj.raba_id, self.relationName)
|
|
879
|
+
|
|
880
|
+
if self._saved :
|
|
881
|
+
self.empty()
|
|
882
|
+
|
|
883
|
+
values = []
|
|
884
|
+
for e in self.data :
|
|
885
|
+
if isRabaObject(e) :
|
|
886
|
+
e.save()
|
|
887
|
+
objDct = e.getDctDescription()
|
|
888
|
+
values.append((self.anchorObj.raba_id, None, RabaFields.RABA_FIELD_TYPE_IS_RABA_OBJECT, e._rabaClass.__name__, e.raba_id, e._raba_namespace))
|
|
889
|
+
elif isPythonPrimitive(e) :
|
|
890
|
+
values.append((self.anchorObj.raba_id, e, RabaFields.RABA_FIELD_TYPE_IS_PRIMITIVE, None, None, None))
|
|
891
|
+
else :
|
|
892
|
+
values.append((self.anchorObj.raba_id, memoryview(pickle.dumps(e)), RabaFields.RABA_FIELD_TYPE_IS_PRIMITIVE, None, None, None))
|
|
893
|
+
|
|
894
|
+
self.connection.executeMany('INSERT INTO %s (anchor_raba_id, value, type, obj_raba_class_name, obj_raba_id, obj_raba_namespace) VALUES (?, ?, ?, ?, ?, ?)' % self.tableName, values)
|
|
895
|
+
|
|
896
|
+
#self.connection.updateRabaListLength(self.raba_id, len(self))
|
|
897
|
+
self._saved = True
|
|
898
|
+
self._mutated = False
|
|
899
|
+
return True
|
|
900
|
+
else :
|
|
901
|
+
return False
|
|
902
|
+
|
|
903
|
+
def _attachToObject(self, anchorObj, relationName) :
|
|
904
|
+
"Attaches the rabalist to a raba object. Only attached rabalists can be saved"
|
|
905
|
+
if self.anchorObj == None :
|
|
906
|
+
self.relationName = relationName
|
|
907
|
+
self.anchorObj = anchorObj
|
|
908
|
+
self._setNamespaceConAndConf(anchorObj._rabaClass._raba_namespace)
|
|
909
|
+
self.tableName = self.connection.makeRabaListTableName(self.anchorObj._rabaClass.__name__, self.relationName)
|
|
910
|
+
faultyElmt = self._checkSelf()
|
|
911
|
+
if faultyElmt != None :
|
|
912
|
+
raise ValueError("Element %s violates specified list or relation constraints" % faultyElmt)
|
|
913
|
+
elif self.anchorObj is not anchorObj :
|
|
914
|
+
raise ValueError("Ouch: attempt to steal rabalist, use RabaLict.copy() instead.\nthief: %s\nvictim: %s\nlist: %s" % (anchorObj, self.anchorObj, self))
|
|
915
|
+
|
|
916
|
+
def pupa(self) :
|
|
917
|
+
return RabaListPupa(self.namespace, self.anchorObj, self.relationName)
|
|
918
|
+
|
|
919
|
+
def _mutateNotifyAnchor(self) :
|
|
920
|
+
self._mutated = True
|
|
921
|
+
if self.anchorObj is not None and self.relationName is not None :
|
|
922
|
+
self.anchorObj.listsToSave[self.relationName] = self
|
|
923
|
+
|
|
924
|
+
def _setNamespaceConAndConf(self, namespace) :
|
|
925
|
+
self._raba_namespace = namespace
|
|
926
|
+
self.connection = RabaConnection(self._raba_namespace)
|
|
927
|
+
self.rabaConfiguration = RabaConfiguration(self._raba_namespace)
|
|
928
|
+
|
|
929
|
+
def extend(self, v) :
|
|
930
|
+
# Check each element against the list's constraint
|
|
931
|
+
for el in v :
|
|
932
|
+
if not self._checkElmt(el) :
|
|
933
|
+
self._dieInvalidRaba(el)
|
|
934
|
+
|
|
935
|
+
namespace = None
|
|
936
|
+
for el in v :
|
|
937
|
+
if isRabaObject(el) :
|
|
938
|
+
namespace = el._raba_namespace
|
|
939
|
+
break
|
|
940
|
+
|
|
941
|
+
self.data.extend(v)
|
|
942
|
+
if self._raba_namespace == None and namespace != None :
|
|
943
|
+
self._setNamespaceConAndConf(namespace)
|
|
944
|
+
|
|
945
|
+
#@profile
|
|
946
|
+
def append(self, v) :
|
|
947
|
+
if not self._checkElmt(v) :
|
|
948
|
+
self._dieInvalidRaba(v)
|
|
949
|
+
|
|
950
|
+
if self._raba_namespace == None and isRabaObject(v) :
|
|
951
|
+
self._setNamespaceConAndConf(v._raba_namespace)
|
|
952
|
+
|
|
953
|
+
self.data.append(v)
|
|
954
|
+
self._mutateNotifyAnchor()
|
|
955
|
+
|
|
956
|
+
def insert(self, k, v) :
|
|
957
|
+
if not self._checkElmt(v) :
|
|
958
|
+
self._dieInvalidRaba(v)
|
|
959
|
+
|
|
960
|
+
if self._raba_namespace == None and isRabaObject(v) :
|
|
961
|
+
self._setNamespaceConAndConf(v._raba_namespace)
|
|
962
|
+
|
|
963
|
+
self.data.insert(k, v)
|
|
964
|
+
self._mutateNotifyAnchor()
|
|
965
|
+
|
|
966
|
+
def set(self, lst) :
|
|
967
|
+
self.data = list(lst)
|
|
968
|
+
self._mutateNotifyAnchor()
|
|
969
|
+
|
|
970
|
+
def __setitem__(self, k, v) :
|
|
971
|
+
if not self._checkElmt(v) :
|
|
972
|
+
self._dieInvalidRaba(v)
|
|
973
|
+
|
|
974
|
+
if self._raba_namespace == None and isRabaObject(v) :
|
|
975
|
+
self._setNamespaceConAndConf(v._raba_namespace)
|
|
976
|
+
|
|
977
|
+
self.data[k] = v
|
|
978
|
+
self._mutateNotifyAnchor()
|
|
979
|
+
|
|
980
|
+
def __delitem__(self, i) :
|
|
981
|
+
del self.data[i]
|
|
982
|
+
self._mutateNotifyAnchor()
|
|
983
|
+
|
|
984
|
+
def __getitem__(self, i) :
|
|
985
|
+
return self.data[i]
|
|
986
|
+
self._mutateNotifyAnchor()
|
|
987
|
+
|
|
988
|
+
def __len__(self) :
|
|
989
|
+
return len(self.data)
|
|
990
|
+
|
|
991
|
+
def __repr__(self) :
|
|
992
|
+
return '[ %s, len: %d, anchor: %s, table: %s]' % (self._runtimeId, len(self), self.anchorObj, self.tableName)
|