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.
- vortex/DataWrapTuple.py +27 -0
- vortex/DeferUtil.py +224 -0
- vortex/Jsonable.py +304 -0
- vortex/Payload.py +115 -0
- vortex/PayloadEndpoint.py +216 -0
- vortex/PayloadEndpointTest.py +164 -0
- vortex/PayloadEnvelope.py +166 -0
- vortex/PayloadFilterKeys.py +15 -0
- vortex/PayloadIO.py +152 -0
- vortex/PayloadPriority.py +11 -0
- vortex/PayloadResponse.py +173 -0
- vortex/PayloadResponseTest.py +39 -0
- vortex/PayloadTest.py +136 -0
- vortex/PerformTestActionTuple.py +30 -0
- vortex/SerialiseUtil.py +273 -0
- vortex/TestTuple.py +61 -0
- vortex/Tuple.py +968 -0
- vortex/TupleAction.py +72 -0
- vortex/TupleActionVortex.py +5 -0
- vortex/TupleDataForTest.py +25 -0
- vortex/TuplePerfTest.py +71 -0
- vortex/TuplePolymorphicTupleTypeFieldArgTest.py +74 -0
- vortex/TupleRestfulTest.py +140 -0
- vortex/TupleSelector.py +105 -0
- vortex/TupleTest.py +127 -0
- vortex/VortexABC.py +55 -0
- vortex/VortexClientHttp.py +308 -0
- vortex/VortexClientHttpTest.py +71 -0
- vortex/VortexClientTcp.py +355 -0
- vortex/VortexClientWebsocketFactory.py +383 -0
- vortex/VortexConnectionABC.py +88 -0
- vortex/VortexFactory.py +932 -0
- vortex/VortexPayloadProtocol.py +180 -0
- vortex/VortexServer.py +367 -0
- vortex/VortexServerConnection.py +127 -0
- vortex/VortexServerHttpResource.py +193 -0
- vortex/VortexServerTcp.py +104 -0
- vortex/VortexServerWebsocket.py +287 -0
- vortex/VortexTcpMemoryLeakPayloadTest.py +611 -0
- vortex/VortexTcpMemoryLeakRawTest.py +87 -0
- vortex/VortexTest.py +22 -0
- vortex/VortexUtil.py +314 -0
- vortex/VortexWritePushProducer.py +321 -0
- vortex/__init__.py +10 -0
- vortex/data_loader/TupleDataLiveLockManager.py +383 -0
- vortex/data_loader/TupleDataLoader.py +326 -0
- vortex/data_loader/TupleDataLoaderDelegate.py +90 -0
- vortex/data_loader/TupleDataLoaderTupleABC.py +10 -0
- vortex/data_loader/TupleDataLoaderTuples.py +53 -0
- vortex/data_loader/__init__.py +0 -0
- vortex/event_bus/TupleChangeEventABC.py +15 -0
- vortex/event_bus/TupleChangeEventBus.py +192 -0
- vortex/event_bus/TupleChangeEventBusObserverABC.py +11 -0
- vortex/event_bus/__init__.py +0 -0
- vortex/handler/TupleActionProcessor.py +270 -0
- vortex/handler/TupleActionProcessorProxy.py +218 -0
- vortex/handler/TupleActionProcessorTestHandler.py +27 -0
- vortex/handler/TupleDataActionClient.py +67 -0
- vortex/handler/TupleDataObservableCache.py +201 -0
- vortex/handler/TupleDataObservableHandler.py +371 -0
- vortex/handler/TupleDataObservableProxyHandler.py +340 -0
- vortex/handler/TupleDataObservableTestHandler.py +60 -0
- vortex/handler/TupleDataObserverClient.py +158 -0
- vortex/handler/VortexJSTupleLoaderTestHandler.py +42 -0
- vortex/handler/__init__.py +0 -0
- vortex/restful/GzippedDataHttpClient.py +170 -0
- vortex/restful/GzippedDataHttpClientTest.py +103 -0
- vortex/restful/GzippedPayloadHttpClient.py +181 -0
- vortex/restful/RestfulHttpClient.py +107 -0
- vortex/restful/RestfulHttpClientTest.py +60 -0
- vortex/restful/RestfulResource.py +153 -0
- vortex/restful/RestfulResourceTest.py +100 -0
- vortex/restful/__init__.py +0 -0
- vortex/rpc/RPC.py +446 -0
- vortex/rpc/RPCTest.py +12 -0
- vortex/rpc/__init__.py +0 -0
- vortex/sqla_orm/OrmCrudHandler.py +431 -0
- vortex/sqla_orm/TupleProviderForDb.py +42 -0
- vortex/sqla_orm/VortexTupleJson.py +24 -0
- vortex/sqla_orm/__init__.py +0 -0
- vortex/storage/TupleStorageSqlite.py +359 -0
- vortex/storage/__init__.py +0 -0
- vortexpy-4.0.23.dist-info/LICENSE +21 -0
- vortexpy-4.0.23.dist-info/METADATA +43 -0
- vortexpy-4.0.23.dist-info/RECORD +87 -0
- vortexpy-4.0.23.dist-info/WHEEL +5 -0
- 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)
|