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,308 @@
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 http.cookiejar import CookieJar
13
+ from typing import Union, Optional, List
14
+ from urllib.parse import urlencode
15
+
16
+ import pytz
17
+ from twisted.internet import reactor, task
18
+ from twisted.internet.defer import succeed, Deferred, inlineCallbacks
19
+ from twisted.python.failure import Failure
20
+ from twisted.web.client import Agent, CookieAgent
21
+ from twisted.web.http_headers import Headers
22
+ from twisted.web.iweb import IBodyProducer
23
+ from zope.interface.declarations import implementer
24
+
25
+ from vortex.DeferUtil import isMainThread
26
+ from vortex.PayloadEnvelope import PayloadEnvelope, VortexMsgList
27
+ from vortex.PayloadPriority import DEFAULT_PRIORITY
28
+ from vortex.VortexABC import VortexABC, VortexInfo
29
+ from vortex.VortexPayloadProtocol import VortexPayloadProtocol
30
+ from vortex.VortexServer import HEART_BEAT_PERIOD
31
+ from vortex.VortexUtil import logLargeMessages
32
+
33
+ logger = logging.getLogger(name=__name__)
34
+
35
+
36
+ @implementer(IBodyProducer)
37
+ class _VortexClientPayloadProducer(object):
38
+ def __init__(self, vortexMsgs) -> None:
39
+ self.vortexMsgs = b""
40
+
41
+ for vortexMsg in vortexMsgs:
42
+ self.vortexMsgs += vortexMsg + b"."
43
+
44
+ self.length = len(self.vortexMsgs)
45
+
46
+ def startProducing(self, consumer):
47
+ consumer.write(self.vortexMsgs)
48
+ return succeed(None)
49
+
50
+ def pauseProducing(self):
51
+ pass
52
+
53
+ def stopProducing(self):
54
+ pass
55
+
56
+
57
+ class VortexPayloadHttpClientProtocol(VortexPayloadProtocol):
58
+ def __init__(self, logger, vortexClient=None):
59
+ VortexPayloadProtocol.__init__(self, logger)
60
+ self._vortexClient = vortexClient
61
+
62
+ def _beat(self):
63
+ if self._vortexClient:
64
+ self._vortexClient._beat()
65
+
66
+ def _nameAndUuidReceived(self, name, uuid):
67
+ if self._vortexClient:
68
+ self._vortexClient._setNameAndUuid(
69
+ name=self._serverVortexName, uuid=self._serverVortexUuid
70
+ )
71
+
72
+ def _createResponseSenderCallable(self):
73
+ raise NotImplementedError()
74
+
75
+
76
+ class VortexClientHttp(VortexABC):
77
+ """VortexServer Client
78
+ Connects to a votex server
79
+ """
80
+
81
+ RETRY_DELAY = 1.5 # Seconds
82
+
83
+ # The time it takes after recieving a response from the server to receive the
84
+ INFO_PAYLOAD_TIMEOUT = 5 # Seconds
85
+
86
+ def __init__(self, name: str) -> None:
87
+ self._vortexName = name
88
+ self._vortexUuid = str(uuid.uuid4())
89
+
90
+ self._server = None
91
+ self._port = None
92
+
93
+ self._retrying = False
94
+
95
+ self._serverVortexUuid = None
96
+ self._serverVortexName = None
97
+
98
+ self._cookieJar = CookieJar()
99
+
100
+ self._beatTime = None
101
+ self._beatTimeout = 15.0 # Server beats at 5 seconds
102
+
103
+ # Start our heart beat checker
104
+ self._beatLoopingCall = task.LoopingCall(self._checkBeat)
105
+
106
+ self._reconnectVortexMsgs = [PayloadEnvelope().toVortexMsg()]
107
+
108
+ self.__protocol = None
109
+
110
+ @property
111
+ def localVortexInfo(self) -> VortexInfo:
112
+ return VortexInfo(name=self._vortexName, uuid=self._vortexUuid)
113
+
114
+ @property
115
+ def remoteVortexInfo(self) -> List[VortexInfo]:
116
+ if not self.__protocol:
117
+ return []
118
+
119
+ if not self._serverVortexUuid:
120
+ return []
121
+
122
+ return [
123
+ VortexInfo(name=self._serverVortexName, uuid=self._serverVortexUuid)
124
+ ]
125
+
126
+ @property
127
+ def requiresBase64Encoding(self) -> bool:
128
+ raise NotImplementedError()
129
+
130
+ @property
131
+ def name(self):
132
+ return self._vortexName
133
+
134
+ def connect(self, server, port):
135
+ self._server = server
136
+ self._port = port
137
+
138
+ if self._serverVortexName:
139
+ raise Exception("Reconnecting is not implemented")
140
+
141
+ self._beat()
142
+ self._beatLoopingCall.start(HEART_BEAT_PERIOD)
143
+
144
+ deferred = Deferred()
145
+
146
+ def checkUuid():
147
+ if self._serverVortexName:
148
+ deferred.callback(True)
149
+ else:
150
+ reactor.callLater(0.1, checkUuid)
151
+
152
+ checkUuid()
153
+ self.sendVortexMsg()
154
+
155
+ return deferred
156
+
157
+ def disconnect(self):
158
+ if self._beatLoopingCall:
159
+ if self._beatLoopingCall.running:
160
+ self._beatLoopingCall.stop()
161
+ self._beatLoopingCall = None
162
+
163
+ self.__protocol.transport.loseConnection()
164
+
165
+ def addReconnectVortexMsg(self, vortexMsg: bytes):
166
+ """Add Reconnect Payload
167
+ :param vortexMsg: An encoded PayloadEnvelope to send when the connection reconnects
168
+ :return:
169
+ """
170
+ self._reconnectVortexMsgs.append(vortexMsg)
171
+
172
+ def sendVortexMsg(
173
+ self,
174
+ vortexMsgs: Union[VortexMsgList, bytes, None] = None,
175
+ vortexUuid: Optional[str] = None,
176
+ priority: int = DEFAULT_PRIORITY,
177
+ ) -> Deferred:
178
+ """Send Vortex Msg
179
+
180
+ NOTE: Priority ins't supported as there is no buffer for this class.
181
+
182
+ """
183
+
184
+ if vortexMsgs is None:
185
+ vortexMsgs = self._reconnectVortexMsgs
186
+
187
+ if not isinstance(vortexMsgs, list):
188
+ vortexMsgs = [vortexMsgs]
189
+
190
+ logLargeMessages(logger, vortexMsgs, self._serverVortexUuid)
191
+
192
+ # Check if the vortexUuid matches the destination uuid
193
+ #
194
+ # if vortexUuid and vortexUuid != self._
195
+ #
196
+ # if not self.__protocol.serverVortexUuid:
197
+ # return []
198
+
199
+ if isMainThread():
200
+ return self._sendVortexMsgLater(vortexMsgs)
201
+
202
+ return task.deferLater(reactor, 0, self._sendVortexMsgLater, vortexMsgs)
203
+
204
+ @inlineCallbacks
205
+ def _sendVortexMsgLater(self, vortexMsgs: VortexMsgList):
206
+ yield None
207
+ assert self._server
208
+ assert vortexMsgs
209
+
210
+ # This transport requires base64 encoding
211
+ for index, vortexMsg in enumerate(vortexMsgs):
212
+ if vortexMsg.startswith(b"{"):
213
+ vortexMsgs[index] = yield PayloadEnvelope.base64EncodeDefer(
214
+ vortexMsg
215
+ )
216
+
217
+ def ebSendAgain(failure):
218
+ self._retrying = True
219
+ logger.debug(
220
+ "Retrying send of %s messages : %s",
221
+ len(vortexMsgs),
222
+ failure.value,
223
+ )
224
+
225
+ return task.deferLater(
226
+ reactor, self.RETRY_DELAY, self._sendVortexMsgLater, vortexMsgs
227
+ )
228
+
229
+ def cbRequest(response):
230
+ if response.code != 200:
231
+ msg = "Connection to vortex %s:%s failed" % (
232
+ self._server,
233
+ self._port,
234
+ )
235
+ logger.error(msg)
236
+ return Failure(Exception(msg))
237
+
238
+ elif self._retrying:
239
+ logger.info(
240
+ "VortexServer client %s:%s reconnected",
241
+ self._server,
242
+ self._port,
243
+ )
244
+
245
+ self._retrying = False
246
+ self.__protocol = VortexPayloadHttpClientProtocol(
247
+ logger, vortexClient=self
248
+ )
249
+ response.deliverBody(self.__protocol)
250
+ return True
251
+
252
+ bodyProducer = _VortexClientPayloadProducer(vortexMsgs)
253
+
254
+ agent = CookieAgent(Agent(reactor), self._cookieJar)
255
+
256
+ args = {"vortexUuid": self._vortexUuid, "vortexName": self._vortexName}
257
+
258
+ uri = (
259
+ "http://%s:%s/vortex?%s"
260
+ % (self._server, self._port, urlencode(args))
261
+ ).encode("UTF-8")
262
+
263
+ d = agent.request(
264
+ b"POST",
265
+ uri,
266
+ Headers(
267
+ {
268
+ b"User-Agent": [b"Synerty VortexServer Client"],
269
+ b"Content-Type": [b"text/plain"],
270
+ }
271
+ ),
272
+ bodyProducer,
273
+ )
274
+
275
+ d.addCallback(cbRequest)
276
+ d.addErrback(ebSendAgain) # Must be after cbRequest
277
+ return d
278
+
279
+ def _beat(self):
280
+ """Beat, Called by protocol"""
281
+ self._beatTime = datetime.now(pytz.utc)
282
+
283
+ def _setNameAndUuid(self, name, uuid):
284
+ """Set Name And Uuid, Called by protocol"""
285
+ self._serverVortexName = name
286
+ self._serverVortexUuid = uuid
287
+
288
+ def _checkBeat(self):
289
+ if (
290
+ not (datetime.now(pytz.utc) - self._beatTime).seconds
291
+ > self._beatTimeout
292
+ ):
293
+ return
294
+
295
+ if self._retrying:
296
+ return
297
+
298
+ self._retrying = True
299
+
300
+ logger.info(
301
+ "VortexServer client dead, reconnecting %s:%s"
302
+ % (self._server, self._port)
303
+ )
304
+
305
+ d = self.sendVortexMsg()
306
+
307
+ # Add a errback that handles the failure.
308
+ d.addErrback(lambda _: None)
@@ -0,0 +1,71 @@
1
+ import unittest
2
+
3
+ from twisted.internet import reactor
4
+ from twisted.internet.defer import DeferredList
5
+ from twisted.internet.endpoints import TCP4ServerEndpoint
6
+ from twisted.internet.protocol import Factory, Protocol, connectionDone
7
+
8
+ from vortex.Payload import Payload
9
+ from vortex.VortexClientHttp import VortexClientHttp
10
+
11
+
12
+ class DummyListenerProtocol(Protocol):
13
+ def connectionMade(self):
14
+ pass
15
+
16
+ def dataReceived(self, data):
17
+ pass
18
+
19
+ def connectionLost(self, reason=connectionDone):
20
+ pass
21
+
22
+
23
+ class DummyListenerFactory(Factory):
24
+ def buildProtocol(self, addr):
25
+ return DummyListenerProtocol()
26
+
27
+
28
+ class VortexClientTest(unittest.TestCase):
29
+ def setUp(self):
30
+ port = 9009
31
+ host = "localhost"
32
+
33
+ def listenSuccess(listeningPort):
34
+ self.listeningPort = listeningPort
35
+
36
+ self.vortexClient = VortexClientHttp()
37
+ return self.vortexClient.connect(host, port)
38
+
39
+ endpoint = TCP4ServerEndpoint(reactor, port)
40
+ d = endpoint.listen(DummyListenerFactory())
41
+ d.addCallback(listenSuccess)
42
+
43
+ return d
44
+
45
+ def tearDown(self):
46
+ try:
47
+ self.vortexClient.close()
48
+ except:
49
+ pass
50
+
51
+ self.listeningPort.stopListening()
52
+
53
+ def testSend(self):
54
+ deferreds = []
55
+
56
+ # Just test functionality.
57
+ d = self.vortexClient.send(Payload())
58
+ deferreds.append(d)
59
+
60
+ d = self.vortexClient.send([Payload(), Payload()])
61
+ deferreds.append(d)
62
+
63
+ d = self.vortexClient.sendVortexMsg(Payload().toVortexMsg())
64
+ deferreds.append(d)
65
+
66
+ d = self.vortexClient.sendVortexMsg(
67
+ [Payload().toVortexMsg(), Payload().toVortexMsg()]
68
+ )
69
+ deferreds.append(d)
70
+
71
+ return DeferredList(deferreds)