vortexpy 4.0.23__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.
Files changed (87) hide show
  1. vortex/DataWrapTuple.py +27 -0
  2. vortex/DeferUtil.py +224 -0
  3. vortex/Jsonable.py +304 -0
  4. vortex/Payload.py +115 -0
  5. vortex/PayloadEndpoint.py +216 -0
  6. vortex/PayloadEndpointTest.py +164 -0
  7. vortex/PayloadEnvelope.py +166 -0
  8. vortex/PayloadFilterKeys.py +15 -0
  9. vortex/PayloadIO.py +152 -0
  10. vortex/PayloadPriority.py +11 -0
  11. vortex/PayloadResponse.py +173 -0
  12. vortex/PayloadResponseTest.py +39 -0
  13. vortex/PayloadTest.py +136 -0
  14. vortex/PerformTestActionTuple.py +30 -0
  15. vortex/SerialiseUtil.py +273 -0
  16. vortex/TestTuple.py +61 -0
  17. vortex/Tuple.py +968 -0
  18. vortex/TupleAction.py +72 -0
  19. vortex/TupleActionVortex.py +5 -0
  20. vortex/TupleDataForTest.py +25 -0
  21. vortex/TuplePerfTest.py +71 -0
  22. vortex/TuplePolymorphicTupleTypeFieldArgTest.py +74 -0
  23. vortex/TupleRestfulTest.py +140 -0
  24. vortex/TupleSelector.py +105 -0
  25. vortex/TupleTest.py +127 -0
  26. vortex/VortexABC.py +55 -0
  27. vortex/VortexClientHttp.py +308 -0
  28. vortex/VortexClientHttpTest.py +71 -0
  29. vortex/VortexClientTcp.py +355 -0
  30. vortex/VortexClientWebsocketFactory.py +383 -0
  31. vortex/VortexConnectionABC.py +88 -0
  32. vortex/VortexFactory.py +932 -0
  33. vortex/VortexPayloadProtocol.py +180 -0
  34. vortex/VortexServer.py +367 -0
  35. vortex/VortexServerConnection.py +127 -0
  36. vortex/VortexServerHttpResource.py +193 -0
  37. vortex/VortexServerTcp.py +104 -0
  38. vortex/VortexServerWebsocket.py +287 -0
  39. vortex/VortexTcpMemoryLeakPayloadTest.py +611 -0
  40. vortex/VortexTcpMemoryLeakRawTest.py +87 -0
  41. vortex/VortexTest.py +22 -0
  42. vortex/VortexUtil.py +314 -0
  43. vortex/VortexWritePushProducer.py +321 -0
  44. vortex/__init__.py +10 -0
  45. vortex/data_loader/TupleDataLiveLockManager.py +383 -0
  46. vortex/data_loader/TupleDataLoader.py +326 -0
  47. vortex/data_loader/TupleDataLoaderDelegate.py +90 -0
  48. vortex/data_loader/TupleDataLoaderTupleABC.py +10 -0
  49. vortex/data_loader/TupleDataLoaderTuples.py +53 -0
  50. vortex/data_loader/__init__.py +0 -0
  51. vortex/event_bus/TupleChangeEventABC.py +15 -0
  52. vortex/event_bus/TupleChangeEventBus.py +192 -0
  53. vortex/event_bus/TupleChangeEventBusObserverABC.py +11 -0
  54. vortex/event_bus/__init__.py +0 -0
  55. vortex/handler/TupleActionProcessor.py +270 -0
  56. vortex/handler/TupleActionProcessorProxy.py +218 -0
  57. vortex/handler/TupleActionProcessorTestHandler.py +27 -0
  58. vortex/handler/TupleDataActionClient.py +67 -0
  59. vortex/handler/TupleDataObservableCache.py +201 -0
  60. vortex/handler/TupleDataObservableHandler.py +371 -0
  61. vortex/handler/TupleDataObservableProxyHandler.py +340 -0
  62. vortex/handler/TupleDataObservableTestHandler.py +60 -0
  63. vortex/handler/TupleDataObserverClient.py +158 -0
  64. vortex/handler/VortexJSTupleLoaderTestHandler.py +42 -0
  65. vortex/handler/__init__.py +0 -0
  66. vortex/restful/GzippedDataHttpClient.py +170 -0
  67. vortex/restful/GzippedDataHttpClientTest.py +103 -0
  68. vortex/restful/GzippedPayloadHttpClient.py +181 -0
  69. vortex/restful/RestfulHttpClient.py +107 -0
  70. vortex/restful/RestfulHttpClientTest.py +60 -0
  71. vortex/restful/RestfulResource.py +153 -0
  72. vortex/restful/RestfulResourceTest.py +100 -0
  73. vortex/restful/__init__.py +0 -0
  74. vortex/rpc/RPC.py +446 -0
  75. vortex/rpc/RPCTest.py +12 -0
  76. vortex/rpc/__init__.py +0 -0
  77. vortex/sqla_orm/OrmCrudHandler.py +431 -0
  78. vortex/sqla_orm/TupleProviderForDb.py +42 -0
  79. vortex/sqla_orm/VortexTupleJson.py +24 -0
  80. vortex/sqla_orm/__init__.py +0 -0
  81. vortex/storage/TupleStorageSqlite.py +359 -0
  82. vortex/storage/__init__.py +0 -0
  83. vortexpy-4.0.23.dist-info/LICENSE +21 -0
  84. vortexpy-4.0.23.dist-info/METADATA +43 -0
  85. vortexpy-4.0.23.dist-info/RECORD +87 -0
  86. vortexpy-4.0.23.dist-info/WHEEL +5 -0
  87. vortexpy-4.0.23.dist-info/top_level.txt +1 -0
@@ -0,0 +1,431 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+ import logging
10
+ from copy import copy
11
+ from typing import Callable, List
12
+ from typing import Union
13
+
14
+ from sqlalchemy.orm.exc import NoResultFound
15
+ from sqlalchemy.orm.session import Session
16
+ from twisted.internet.defer import inlineCallbacks
17
+ from twisted.internet.threads import deferToThread
18
+
19
+ from vortex.DeferUtil import deferToThreadWrapWithLogger
20
+ from vortex.Payload import Payload
21
+ from vortex.PayloadEndpoint import PayloadEndpoint
22
+ from vortex.PayloadEnvelope import VortexMsgList, PayloadEnvelope
23
+ from vortex.PayloadFilterKeys import plIdKey, plDeleteKey
24
+ from vortex.Tuple import Tuple
25
+ from vortex.VortexFactory import VortexFactory
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class OrmCrudHandlerExtension(object):
31
+ def uiData(self, tuple_, tuples, session, payloadFilt):
32
+ return True
33
+
34
+ def afterCreate(self, tuple_, tuples, session, payloadFilt):
35
+ return True
36
+
37
+ def afterRetrieve(self, tuple_, tuples, session, payloadFilt):
38
+ return True
39
+
40
+ def beforeUpdate(self, tuple_, tuples, session, payloadFilt):
41
+ return True
42
+
43
+ def middleUpdate(self, tuple_, tuples, session, payloadFilt):
44
+ return True
45
+
46
+ def afterUpdate(self, tuple_, tuples, session, payloadFilt):
47
+ return True
48
+
49
+ def afterUpdateCommit(self, tuple_, tuples, session, payloadFilt):
50
+ return True
51
+
52
+ def beforeDelete(self, tuple_, tuples, session, payloadFilt):
53
+ return True
54
+
55
+ def afterDeleteCommit(self, tuple_, tuples, session, payloadFilt):
56
+ return True
57
+
58
+
59
+ class _OrmCrudExtensionProcessor(object):
60
+ def __init__(self):
61
+ self.extensions = {}
62
+
63
+ def addExtensionClassDecorator(self, Tuple):
64
+ def f(cls):
65
+ self.extensions[Tuple.tupleType()] = cls()
66
+ return cls
67
+
68
+ return f
69
+
70
+ def addExtensionObject(self, Tuple, ormCrudHandlerExtension):
71
+ self.extensions[Tuple.tupleType()] = ormCrudHandlerExtension
72
+
73
+ def uiData(self, tuples, session, payloadFilt):
74
+ if not self.extensions:
75
+ return
76
+
77
+ for tuple_ in tuples:
78
+ if not hasattr(tuple_, "uiData"):
79
+ continue
80
+ extension = self.extensions.get(tuple_.tupleType())
81
+ if extension:
82
+ extension.uiData(tuple_, tuples, session, payloadFilt)
83
+
84
+ def afterCreate(self, tuples, session, payloadFilt):
85
+ if not self.extensions:
86
+ return
87
+
88
+ for tuple_ in tuples:
89
+ extension = self.extensions.get(tuple_.tupleType())
90
+ if extension:
91
+ extension.afterCreate(tuple_, tuples, session, payloadFilt)
92
+
93
+ self.uiData(tuples, session, payloadFilt)
94
+
95
+ def afterRetrieve(self, tuples, session, payloadFilt):
96
+ if not self.extensions:
97
+ return
98
+
99
+ for tuple_ in tuples:
100
+ extension = self.extensions.get(tuple_.tupleType())
101
+ if extension:
102
+ extension.afterRetrieve(tuple_, tuples, session, payloadFilt)
103
+
104
+ self.uiData(tuples, session, payloadFilt)
105
+
106
+ def beforeUpdate(self, tuples, session, payloadFilt):
107
+ if not self.extensions:
108
+ return
109
+
110
+ for tuple_ in tuples:
111
+ extension = self.extensions.get(tuple_.tupleType())
112
+ if extension:
113
+ extension.beforeUpdate(tuple_, tuples, session, payloadFilt)
114
+
115
+ def middleUpdate(self, tuples, session, payloadFilt):
116
+ if not self.extensions:
117
+ return
118
+
119
+ for tuple_ in tuples:
120
+ extension = self.extensions.get(tuple_.tupleType())
121
+ if extension:
122
+ extension.middleUpdate(tuple_, tuples, session, payloadFilt)
123
+
124
+ def afterUpdate(self, tuples, session, payloadFilt):
125
+ if not self.extensions:
126
+ return
127
+
128
+ for tuple_ in tuples:
129
+ extension = self.extensions.get(tuple_.tupleType())
130
+ if extension:
131
+ extension.afterUpdate(tuple_, tuples, session, payloadFilt)
132
+
133
+ def afterUpdateCommit(self, tuples, session, payloadFilt):
134
+ if not self.extensions:
135
+ return
136
+
137
+ for tuple_ in tuples:
138
+ extension = self.extensions.get(tuple_.tupleType())
139
+ if extension:
140
+ extension.afterUpdateCommit(
141
+ tuple_, tuples, session, payloadFilt
142
+ )
143
+
144
+ self.uiData(tuples, session, payloadFilt)
145
+
146
+ def beforeDelete(self, tuples, session, payloadFilt):
147
+ if not self.extensions:
148
+ return
149
+
150
+ for tuple_ in tuples:
151
+ extension = self.extensions.get(tuple_.tupleType())
152
+ if extension:
153
+ extension.beforeDelete(tuple_, tuples, session, payloadFilt)
154
+
155
+ def afterDeleteCommit(self, tuples, session, payloadFilt):
156
+ if not self.extensions:
157
+ return
158
+
159
+ for tuple_ in tuples:
160
+ extension = self.extensions.get(tuple_.tupleType())
161
+ if extension:
162
+ extension.afterDeleteCommit(
163
+ tuple_, tuples, session, payloadFilt
164
+ )
165
+
166
+
167
+ class OrmCrudHandler(object):
168
+ UPDATE = 1
169
+ CREATE = 2
170
+ DELETE = 3
171
+ QUERY = 4
172
+
173
+ def __init__(
174
+ self, sessionFunctor, Declarative, payloadFilter, retreiveAll=False
175
+ ):
176
+ """Create CRUD Hanlder
177
+
178
+ This handler will perform crud operations for an SQLAlchemy Declarative
179
+
180
+ """
181
+ self._sessionFunctor = sessionFunctor
182
+ self._Declarative = Declarative
183
+ self._payloadFilter = (
184
+ payloadFilter
185
+ if isinstance(payloadFilter, dict)
186
+ else {"key": payloadFilter}
187
+ )
188
+
189
+ self._ep = PayloadEndpoint(self._payloadFilter, self._process)
190
+
191
+ self._ext = _OrmCrudExtensionProcessor()
192
+
193
+ self._retreiveAll = retreiveAll
194
+
195
+ def shutdown(self):
196
+ self._ep.shutdown()
197
+
198
+ def addExtension(self, Tuple, ormCrudHandlerExtension=None):
199
+ if ormCrudHandlerExtension:
200
+ self._ext.addExtensionObject(Tuple, ormCrudHandlerExtension)
201
+ return
202
+ return self._ext.addExtensionClassDecorator(Tuple)
203
+
204
+ @inlineCallbacks
205
+ def _process(self, *args, **kwargs):
206
+ val = yield self._processInThread(*args, **kwargs)
207
+ return val
208
+
209
+ @deferToThreadWrapWithLogger(logger)
210
+ def _processInThread(
211
+ self,
212
+ payloadEnvelope: PayloadEnvelope,
213
+ vortexUuid: str,
214
+ sendResponse: Callable[[Union[VortexMsgList, bytes]], None],
215
+ **kwargs
216
+ ):
217
+
218
+ # Execute preprocess functions
219
+ if self.preProcess(payloadEnvelope, vortexUuid, **kwargs) != None:
220
+ return
221
+
222
+ # Create reply payload replyFilt
223
+
224
+ replyFilt = copy(self._payloadFilter)
225
+ if payloadEnvelope.filt:
226
+ replyFilt.update(payloadEnvelope.filt)
227
+
228
+ # Get data from the payload
229
+ phId = payloadEnvelope.filt.get(plIdKey)
230
+ delete = payloadEnvelope.filt.get(plDeleteKey, False)
231
+
232
+ # Setup variables to populate
233
+ replyPayloadEnvelope: PayloadEnvelope = None
234
+ action = None
235
+
236
+ tuples = []
237
+ if payloadEnvelope.encodedPayload:
238
+ tuples = payloadEnvelope.decodePayload().tuples
239
+
240
+ session = self._getSession()
241
+
242
+ # Execute the action
243
+ try:
244
+ if delete == True:
245
+ action = self.DELETE
246
+ replyPayloadEnvelope = self._delete(
247
+ session, tuples, phId, payloadEnvelope.filt
248
+ )
249
+
250
+ elif len(tuples):
251
+ action = self.UPDATE
252
+ replyPayloadEnvelope = self._update(
253
+ session, tuples, payloadEnvelope.filt
254
+ )
255
+
256
+ elif phId != None:
257
+ action = self.QUERY
258
+ replyPayloadEnvelope = self._retrieve(
259
+ session, phId, payloadEnvelope.filt
260
+ )
261
+
262
+ elif len(tuples) == 0:
263
+ action = self.CREATE
264
+ replyPayloadEnvelope = self._create(
265
+ session, payloadEnvelope.filt
266
+ )
267
+
268
+ else:
269
+ session.close()
270
+ raise Exception("Invalid ORM CRUD parameter state")
271
+
272
+ except Exception as e:
273
+ replyPayloadEnvelope = PayloadEnvelope(
274
+ result=str(e), filt=replyFilt
275
+ )
276
+ sendResponse(replyPayloadEnvelope.toVortexMsg())
277
+ try:
278
+ session.rollback()
279
+ except:
280
+ pass
281
+ session.close()
282
+ raise
283
+
284
+ # Prefer reply filt, if not combine our accpt filt with the filt we were sent
285
+
286
+ # Ensure any delegates are playing nice with the result
287
+ if (
288
+ action in (self.DELETE, self.UPDATE)
289
+ and replyPayloadEnvelope.result is None
290
+ ):
291
+ replyPayloadEnvelope.result = True
292
+
293
+ replyPayloadEnvelope.filt = replyFilt
294
+ sendResponse(replyPayloadEnvelope.toVortexMsg())
295
+
296
+ # Execute the post process function
297
+ self.postProcess(action, payloadEnvelope.filt, vortexUuid)
298
+ session.commit()
299
+ session.close()
300
+
301
+ def _getSession(self):
302
+ if isinstance(self._sessionFunctor, Session):
303
+ return self._sessionFunctor
304
+
305
+ return self._sessionFunctor()
306
+
307
+ def _getDeclarativeById(self, session, id_):
308
+ qry = session.query(self._Declarative)
309
+ if self._retreiveAll and id_ is None:
310
+ return qry.all()
311
+
312
+ try:
313
+ return qry.filter(self._Declarative.id == id_).one()
314
+ except NoResultFound as e:
315
+ return None
316
+
317
+ def createDeclarative(self, session, payloadFilt):
318
+ if self._retreiveAll:
319
+ return session.query(self._Declarative).all()
320
+
321
+ return [self._Declarative()]
322
+
323
+ def _getDeclarativeByTuple(self, session, tuple_):
324
+ T = tuple_.__class__
325
+ return session.query(T).filter(T.id == tuple_.id).one()
326
+
327
+ def _create(self, session, payloadFilt) -> PayloadEnvelope:
328
+ tuples = self.createDeclarative(session, payloadFilt)
329
+ payload = Payload(tuples=tuples)
330
+ self._ext.afterCreate(payload.tuples, session, payloadFilt)
331
+ return payload.makePayloadEnvelope()
332
+
333
+ def _retrieve(
334
+ self, session, filtId, payloadFilt, obj=None, **kwargs
335
+ ) -> PayloadEnvelope:
336
+ ph = obj if obj else self._getDeclarativeById(session, filtId)
337
+ payload = Payload()
338
+ payload.tuples = [ph] if ph else []
339
+ self._ext.afterRetrieve(payload.tuples, session, payloadFilt)
340
+ return payload.makePayloadEnvelope()
341
+
342
+ def _update(self, session, tuples, payloadFilt) -> PayloadEnvelope:
343
+ self._ext.beforeUpdate(tuples, session, payloadFilt)
344
+
345
+ # Add everything first.
346
+ for tupleObj in tuples:
347
+ if tupleObj is None:
348
+ raise Exception("None/null was present in array of tuples")
349
+
350
+ # Make sure it's not ''
351
+ if tupleObj.id == "":
352
+ tupleObj.id = None
353
+
354
+ if tupleObj.id is None:
355
+ session.add(tupleObj)
356
+
357
+ self._ext.middleUpdate(tuples, session, payloadFilt)
358
+
359
+ # Now merge with the session
360
+ returnTuples = []
361
+ for tupleObj in tuples:
362
+ if tupleObj.id is None:
363
+ # If this was a create, then we can just return this tuple
364
+ returnTuples.append(tupleObj)
365
+
366
+ else:
367
+ # Otherwise use the merge method to perform the update magic.
368
+ # and add the resulting merged tuple to the return list
369
+ returnTuples.append(session.merge(tupleObj))
370
+
371
+ self._ext.afterUpdate(returnTuples, session, payloadFilt)
372
+
373
+ session.commit()
374
+
375
+ self._ext.afterUpdateCommit(returnTuples, session, payloadFilt)
376
+
377
+ return Payload(tuples=returnTuples).makePayloadEnvelope(result=True)
378
+
379
+ def _delete(self, session, tuples, filtId, payloadFilt) -> PayloadEnvelope:
380
+ self._ext.beforeDelete(tuples, session, payloadFilt)
381
+
382
+ if len(tuples):
383
+ phIds = [t.id for t in tuples]
384
+ else:
385
+ phIds = [filtId]
386
+
387
+ for phId in phIds:
388
+ ph = self._getDeclarativeById(session, phId)
389
+ try:
390
+ # Try to iterate it
391
+ for item in iter(ph):
392
+ session.delete(item)
393
+
394
+ except TypeError:
395
+ # If it's not an iterator
396
+ if ph is not None:
397
+ session.delete(ph)
398
+
399
+ session.commit()
400
+
401
+ returnTuples: List[Tuple] = []
402
+ if self._retreiveAll:
403
+ returnTuples = self.createDeclarative(session, payloadFilt)
404
+
405
+ self._ext.afterDeleteCommit(tuples, session, payloadFilt)
406
+ return Payload(tuples=returnTuples).makePayloadEnvelope(result=True)
407
+
408
+ def sendModelUpdate(
409
+ self, objId, vortexUuid=None, session=None, obj=None, **kwargs
410
+ ):
411
+ session = session if session else self._getSession()
412
+ pl = self._retrieve(session, objId, self._payloadFilter, obj=obj)
413
+ pl.filt.update(self._payloadFilter)
414
+ pl.filt[plIdKey] = objId
415
+ VortexFactory.sendVortexMsg(pl.toVortexMsg(), destVortexUuid=vortexUuid)
416
+
417
+ def preProcess(self, payload, vortextUuid, **kwargs):
418
+ pass
419
+
420
+ def postProcess(self, action, payloadFilt, vortextUuid):
421
+ pass
422
+
423
+
424
+ class OrmCrudHandlerInThread(OrmCrudHandler):
425
+ def _process(self, payload, **kwargs):
426
+ return deferToThread(OrmCrudHandler.process, self, payload, **kwargs)
427
+
428
+ def sendModelUpdate(self, objId, **kwargs):
429
+ return deferToThread(
430
+ OrmCrudHandler.sendModelUpdate, self, objId, **kwargs
431
+ )
@@ -0,0 +1,42 @@
1
+ import logging
2
+
3
+ from vortex.DeferUtil import deferToThreadWrapWithLogger
4
+ from vortex.Payload import Payload
5
+ from vortex.Tuple import tupleForTupleName
6
+ from vortex.TupleSelector import TupleSelector
7
+ from vortex.handler.TupleDataObservableHandler import TuplesProviderABC
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class TuplesProviderForDB(TuplesProviderABC):
13
+ def __init__(self, ormSessionCreatorFunc):
14
+ self._ormSessionCreatorFunc = ormSessionCreatorFunc
15
+
16
+ @deferToThreadWrapWithLogger(logger)
17
+ def makeVortexMsg(self, filt: dict, tupleSelector: TupleSelector) -> bytes:
18
+ """Make VortexMsg for DB
19
+
20
+ Considerations for this method.
21
+
22
+ #. It must return a vortexMsg (bytes), this ensures all the database access
23
+ and lazy loading is completed in this thread, in this session.
24
+
25
+ #. It must ensure the session is closed on/after exit.
26
+
27
+ """
28
+
29
+ ormSession = self._ormSessionCreatorFunc()
30
+ try:
31
+ TupleClass = tupleForTupleName(tupleSelector.name)
32
+ qry = ormSession.query(TupleClass)
33
+ for key, value in tupleSelector.selector.items():
34
+ qry = qry.filter(getattr(TupleClass, key) == value)
35
+
36
+ return (
37
+ Payload(filt=filt, tuples=qry.all())
38
+ .makePayloadEnvelope()
39
+ .toVortexMsg()
40
+ )
41
+ finally:
42
+ ormSession.close()
@@ -0,0 +1,24 @@
1
+ import json
2
+
3
+ from sqlalchemy import TypeDecorator, JSON
4
+ from typing import Any
5
+
6
+ from vortex.Tuple import Tuple
7
+
8
+
9
+ class VortexTupleJSON(TypeDecorator):
10
+ impl = JSON()
11
+
12
+ def __init__(self, TupleClass, *args: Any, **kwargs: Any):
13
+ super().__init__(self, *args, **kwargs)
14
+ self._TupleClass = TupleClass
15
+
16
+ def process_bind_param(self, value: Tuple, dialect):
17
+ return json.dumps(value.tupleToRestfulJsonDict())
18
+
19
+ def process_result_value(self, value, dialect):
20
+ valueDict = json.loads(value)
21
+
22
+ return Tuple.restfulJsonDictToTupleWithValidation(
23
+ valueDict, self._TupleClass
24
+ )
File without changes