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,355 @@
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
+ import uuid
11
+ from datetime import datetime
12
+ from typing import Union, Optional, List
13
+
14
+ import pytz
15
+ import twisted
16
+ from twisted.internet import reactor
17
+ from twisted.internet import task
18
+ from twisted.internet.defer import Deferred, inlineCallbacks
19
+ from twisted.internet.error import ConnectionDone, ConnectionRefusedError
20
+ from twisted.internet.protocol import connectionDone, ReconnectingClientFactory
21
+
22
+ from vortex.DeferUtil import vortexLogFailure, isMainThread
23
+ from vortex.PayloadEnvelope import PayloadEnvelope, VortexMsgList
24
+ from vortex.PayloadPriority import DEFAULT_PRIORITY
25
+ from vortex.VortexABC import VortexABC, VortexInfo
26
+ from vortex.VortexPayloadProtocol import VortexPayloadProtocol
27
+ from vortex.VortexServer import HEART_BEAT_PERIOD
28
+ from vortex.VortexServer import HEART_BEAT_TIMEOUT
29
+ from vortex.VortexUtil import logLargeMessages
30
+ from vortex.VortexWritePushProducer import VortexWritePushProducer
31
+
32
+ logger = logging.getLogger(name=__name__)
33
+
34
+
35
+ class VortexPayloadTcpClientProtocol(VortexPayloadProtocol):
36
+ def __init__(self, vortexClient=None) -> None:
37
+ VortexPayloadProtocol.__init__(self, logger)
38
+ self._vortexClient = vortexClient
39
+
40
+ self._closed = False
41
+
42
+ # Start our heart beat
43
+ self._sendBeatLoopingCall = task.LoopingCall(self._sendBeat)
44
+ d = self._sendBeatLoopingCall.start(HEART_BEAT_PERIOD, now=False)
45
+ d.addErrback(lambda f: logger.exception(f.value))
46
+
47
+ self._producer: Optional[VortexWritePushProducer] = None
48
+
49
+ def _beat(self):
50
+ if self._vortexClient:
51
+ self._vortexClient._beat()
52
+
53
+ def _nameAndUuidReceived(self, name, uuid):
54
+ from vortex.VortexFactory import VortexFactory
55
+
56
+ self._producer.setRemoteVortexName(name)
57
+
58
+ if self._vortexClient:
59
+ self._vortexClient._setNameAndUuid(name=name, uuid=uuid)
60
+
61
+ VortexFactory._notifyOfVortexStatusChange(name, online=True)
62
+
63
+ def _createResponseSenderCallable(self):
64
+ def sendResponse(
65
+ vortexMsgs: Union[VortexMsgList, bytes],
66
+ priority: int = DEFAULT_PRIORITY,
67
+ ):
68
+ return self._vortexClient.sendVortexMsg(
69
+ vortexMsgs=vortexMsgs, priority=priority
70
+ )
71
+
72
+ return sendResponse
73
+
74
+ def _sendBeat(self):
75
+ if self._closed:
76
+ return
77
+
78
+ # Send the heartbeats
79
+ self._producer.write(b".", DEFAULT_PRIORITY)
80
+
81
+ def write(self, payloadVortexStr: bytes, priority: int = DEFAULT_PRIORITY):
82
+ if not twisted.python.threadable.isInIOThread():
83
+ e = Exception("Write called from NON main thread")
84
+ logger.exception(str(e))
85
+ raise e
86
+
87
+ assert not self._closed
88
+ self._producer.write(payloadVortexStr, priority)
89
+
90
+ def connectionMade(self):
91
+ self._producer = VortexWritePushProducer(
92
+ self.transport, lambda: self.close()
93
+ )
94
+
95
+ # Register the producer if there isn't one already.
96
+ if not self.transport.producer:
97
+ self.transport.registerProducer(self._producer, True)
98
+
99
+ # Send a heart beat down the new connection, tell it who we are.
100
+ connectPayloadFilt = {
101
+ PayloadEnvelope.vortexUuidKey: self._vortexClient.uuid,
102
+ PayloadEnvelope.vortexNameKey: self._vortexClient.name,
103
+ }
104
+ self._producer.write(
105
+ PayloadEnvelope(filt=connectPayloadFilt).toVortexMsg(),
106
+ DEFAULT_PRIORITY,
107
+ )
108
+
109
+ def connectionLost(self, reason=connectionDone):
110
+ from vortex.VortexFactory import VortexFactory
111
+
112
+ VortexFactory._notifyOfVortexStatusChange(
113
+ self._serverVortexName, online=False
114
+ )
115
+
116
+ if self._sendBeatLoopingCall.running:
117
+ self._sendBeatLoopingCall.stop()
118
+ self._closed = False
119
+
120
+ def close(self):
121
+ self.transport.loseConnection()
122
+ if self._sendBeatLoopingCall.running:
123
+ self._sendBeatLoopingCall.stop()
124
+ self._closed = False
125
+
126
+
127
+ class VortexClientTcp(ReconnectingClientFactory, VortexABC):
128
+ """VortexServer Client
129
+ Connects to a votex server
130
+ """
131
+
132
+ RETRY_DELAY = 1.5 # Seconds
133
+
134
+ # The time it takes after recieving a response from the server to receive the
135
+ INFO_PAYLOAD_TIMEOUT = 5 # Seconds
136
+
137
+ # Set the ReconnectingClientFactory max delay
138
+ maxDelay = 1
139
+
140
+ def __init__(self, name: str) -> None:
141
+ self._vortexName = name
142
+ self._vortexUuid = str(uuid.uuid4())
143
+
144
+ self._server = None
145
+ self._port = None
146
+
147
+ self._retrying = False
148
+
149
+ self._serverVortexUuid = None
150
+ self._serverVortexName = None
151
+
152
+ self._lastBeatReceiveTime = None
153
+ self._lastHeartBeatCheckTime = datetime.now(pytz.utc)
154
+
155
+ # Start our heart beat checker
156
+ self._beatLoopingCall = task.LoopingCall(self._checkBeat)
157
+
158
+ self._reconnectVortexMsgs = [PayloadEnvelope().toVortexMsg()]
159
+
160
+ self.__protocol = None
161
+
162
+ #####################################################################################3
163
+
164
+ # class EchoClientFactory(ReconnectingClientFactory):
165
+ def startedConnecting(self, connector):
166
+ logger.debug("Started to connect.")
167
+
168
+ def buildProtocol(self, addr):
169
+
170
+ if self.__protocol:
171
+ self.__protocol.close()
172
+ self.__protocol = None
173
+
174
+ logger.debug("Connected.")
175
+ self.resetDelay()
176
+ self.__protocol = VortexPayloadTcpClientProtocol(self)
177
+ return self.__protocol
178
+
179
+ def clientConnectionLost(self, connector, reason):
180
+ if not reason.check(ConnectionDone):
181
+ logger.debug("Lost connection. Reason: %s", reason)
182
+ ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
183
+
184
+ def clientConnectionFailed(self, connector, reason):
185
+ if reason.check(ConnectionRefusedError):
186
+ logger.debug("Connection refused (We'll retry)")
187
+ else:
188
+ logger.debug("Connection failed. Reason: %s", reason)
189
+ ReconnectingClientFactory.clientConnectionFailed(
190
+ self, connector, reason
191
+ )
192
+
193
+ #####################################################################################3
194
+
195
+ @property
196
+ def localVortexInfo(self) -> VortexInfo:
197
+ return VortexInfo(name=self._vortexName, uuid=self._vortexUuid)
198
+
199
+ @property
200
+ def remoteVortexInfo(self) -> List[VortexInfo]:
201
+ if not self.__protocol:
202
+ return []
203
+
204
+ if not self._serverVortexUuid:
205
+ return []
206
+
207
+ return [
208
+ VortexInfo(name=self._serverVortexName, uuid=self._serverVortexUuid)
209
+ ]
210
+
211
+ @property
212
+ def name(self):
213
+ return self._vortexName
214
+
215
+ @property
216
+ def uuid(self):
217
+ return self._vortexUuid
218
+
219
+ @property
220
+ def requiresBase64Encoding(self) -> bool:
221
+ return True
222
+
223
+ def connect(self, server, port):
224
+ self._server = server
225
+ self._port = port
226
+
227
+ self._beat()
228
+ d = self._beatLoopingCall.start(HEART_BEAT_PERIOD, now=False)
229
+ d.addErrback(vortexLogFailure, logger, consumeError=True)
230
+
231
+ deferred = Deferred()
232
+
233
+ reactor.connectTCP(self._server, self._port, self)
234
+
235
+ def checkUuid():
236
+ if self._serverVortexName:
237
+ deferred.callback(True)
238
+ else:
239
+ reactor.callLater(0.1, checkUuid)
240
+
241
+ checkUuid()
242
+
243
+ return deferred
244
+
245
+ def close(self):
246
+ if self._beatLoopingCall and self._beatLoopingCall.running:
247
+ self._beatLoopingCall.stop()
248
+ self._beatLoopingCall = None
249
+
250
+ self.stopTrying()
251
+ self.__protocol.close()
252
+
253
+ def addReconnectVortexMsg(self, vortexMsg: bytes):
254
+ """Add Reconnect Payload
255
+ :param vortexMsg: An encoded PayloadEnvelope to send when the connection reconnects
256
+ :return:
257
+ """
258
+ self._reconnectVortexMsgs.append(vortexMsg)
259
+
260
+ def sendVortexMsg(
261
+ self,
262
+ vortexMsgs: Union[VortexMsgList, bytes, None] = None,
263
+ vortexUuid: Optional[str] = None,
264
+ priority: int = DEFAULT_PRIORITY,
265
+ ) -> Deferred:
266
+ """Send Vortex Msg
267
+
268
+ NOTE: Priority ins't supported as there is no buffer for this class.
269
+
270
+ """
271
+
272
+ if vortexMsgs is None:
273
+ vortexMsgs = self._reconnectVortexMsgs
274
+
275
+ if not isinstance(vortexMsgs, list):
276
+ vortexMsgs = [vortexMsgs]
277
+
278
+ logLargeMessages(logger, vortexMsgs, self._serverVortexUuid)
279
+
280
+ # Check if the vortexUuid matches the destination uuid
281
+ #
282
+ # if vortexUuid and vortexUuid != self._
283
+ #
284
+ # if not self.__protocol.serverVortexUuid:
285
+ # return []
286
+
287
+ if isMainThread():
288
+ return self._sendVortexMsgLater(vortexMsgs)
289
+
290
+ return task.deferLater(reactor, 0, self._sendVortexMsgLater, vortexMsgs)
291
+
292
+ @inlineCallbacks
293
+ def _sendVortexMsgLater(self, vortexMsgs: VortexMsgList):
294
+ yield None
295
+ assert self._server
296
+ assert vortexMsgs
297
+
298
+ # This transport requires base64 encoding
299
+ for index, vortexMsg in enumerate(vortexMsgs):
300
+ if vortexMsg.startswith(b"{"):
301
+ vortexMsgs[index] = yield PayloadEnvelope.base64EncodeDefer(
302
+ vortexMsg
303
+ )
304
+
305
+ for vortexMsg in vortexMsgs:
306
+ self.__protocol.write(vortexMsg)
307
+
308
+ return True
309
+
310
+ def _beat(self):
311
+ """Beat, Called by protocol"""
312
+ self._lastBeatReceiveTime = datetime.now(pytz.utc)
313
+
314
+ def _setNameAndUuid(self, name, uuid):
315
+ """Set Name And Uuid, Called by protocol"""
316
+ self._serverVortexName = name
317
+ self._serverVortexUuid = uuid
318
+
319
+ def _checkBeat(self):
320
+
321
+ # If we've been asleep, then make note of that (VM suspended)
322
+ checkTimout = (
323
+ datetime.now(pytz.utc) - self._lastHeartBeatCheckTime
324
+ ).seconds > HEART_BEAT_TIMEOUT
325
+
326
+ # Has the heart beat expired?
327
+ beatTimeout = (
328
+ datetime.now(pytz.utc) - self._lastBeatReceiveTime
329
+ ).seconds > HEART_BEAT_TIMEOUT
330
+
331
+ # Mark that we've just checked it
332
+ self._lastHeartBeatCheckTime = datetime.now(pytz.utc)
333
+
334
+ if checkTimout:
335
+ self._lastBeatReceiveTime = datetime.now(pytz.utc)
336
+ return
337
+
338
+ if beatTimeout:
339
+ self._reconnectAfterHeartBeatLost()
340
+ return
341
+
342
+ def _reconnectAfterHeartBeatLost(self):
343
+ if self._retrying:
344
+ return
345
+
346
+ self._retrying = True
347
+
348
+ logger.info(
349
+ "VortexServer client dead, reconnecting %s:%s",
350
+ self._server,
351
+ self._port,
352
+ )
353
+
354
+ if self.__protocol:
355
+ self.__protocol.close()