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
vortex/rpc/RPC.py ADDED
@@ -0,0 +1,446 @@
1
+ import logging
2
+ import sys
3
+ from copy import copy
4
+ from typing import Optional, Set, Union
5
+
6
+ from twisted.internet.defer import (
7
+ succeed,
8
+ fail,
9
+ Deferred,
10
+ TimeoutError,
11
+ inlineCallbacks,
12
+ )
13
+ from twisted.internet.threads import deferToThread
14
+ from twisted.python.failure import Failure
15
+
16
+ from vortex.DeferUtil import yesMainThread
17
+ from vortex.Payload import Payload
18
+ from vortex.PayloadEndpoint import PayloadEndpoint
19
+ from vortex.PayloadEnvelope import PayloadEnvelope
20
+ from vortex.PayloadPriority import RPC_PRIORITY
21
+ from vortex.PayloadResponse import PayloadResponse
22
+ from vortex.Tuple import Tuple, addTupleType, TupleField
23
+ from vortex.VortexFactory import VortexFactory
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @addTupleType
29
+ class _VortexRPCArgTuple(Tuple):
30
+ """Vortex RPC Arg Tuple
31
+
32
+ This tuple stores the arguments used to call the remote method.
33
+
34
+ """
35
+
36
+ __tupleType__ = __name__ + "_VortexRPCArgTuple"
37
+
38
+ args = TupleField(defaultValue=[])
39
+ kwargs = TupleField(defaultValue={})
40
+
41
+
42
+ @addTupleType
43
+ class _VortexRPCResultTuple(Tuple):
44
+ """Vortex RPC Result Tuple
45
+
46
+ This tuple stores the result from the remote procedure call
47
+
48
+ """
49
+
50
+ __tupleType__ = __name__ + "_VortexRPCResultTuple"
51
+
52
+ result = TupleField(defaultValue=None)
53
+
54
+
55
+ class _VortexRPC:
56
+ """Vortex RPC Wrapper Class
57
+
58
+ This wrapper class handles the mechanics of listening for the RPC calls (handler)
59
+ and sending calls (PayloadResponse)
60
+
61
+ Under the covers, This is what the class.
62
+
63
+ #. The caller will be given a Deferred
64
+ #. The args, kwargs for Tuples and primitive values will be serialised into payload.
65
+ #. The payload will be sent across the vortex
66
+ #. The payload will be deserialized and delivered to a PayloadEndpoint created
67
+ for the decorated method.
68
+ #. The handler will then call the method.
69
+ #. The result from the method will then be serialised and sent back to the calling
70
+ vortex.
71
+ #. The deferred will be called with the result from the remote method.
72
+
73
+ """
74
+
75
+ __registeredFuncNames: Set[str] = set()
76
+
77
+ def __init__(
78
+ self,
79
+ func,
80
+ listeningVortexName: str,
81
+ timeoutSeconds: float,
82
+ acceptOnlyFromVortex: Optional[Union[str, tuple]],
83
+ additionalFilt: dict,
84
+ deferToThread: bool,
85
+ inlineCallbacks: bool,
86
+ ) -> None:
87
+ """
88
+
89
+ :param listeningVortexName: If the local vortex name matches this name, then
90
+ a handler will be setup to listen for payloads for this RPC method.
91
+
92
+ :param timeoutSeconds: The seconds to wait for a response before calling the
93
+ deferreds errback with a TimeoutError
94
+
95
+ :param acceptOnlyFromVortex: Accept payloads (calls) only from this vortex.
96
+ The vortex can be str or tuple of str, or None to accept from any.
97
+
98
+ :param additionalFilt: If specified, the items from this dict will be added
99
+ to the filt that this RPCs handler listens on.
100
+
101
+ :param deferToThread: Should the function be called in a thread, or in the
102
+ reactors main loop.
103
+
104
+ :param inlineCallbacks: Should the function be wrapped in the twisted
105
+ @inlinecallbacks decorator before it's called?.
106
+
107
+ """
108
+
109
+ self.__func = func
110
+ self.__funcSelf = None
111
+ self.__listeningVortexName = listeningVortexName
112
+ self.__timeoutSeconds = timeoutSeconds
113
+ self.__acceptOnlyFromVortex = acceptOnlyFromVortex
114
+ if isinstance(self.__acceptOnlyFromVortex, str):
115
+ self.__acceptOnlyFromVortex = (self.__acceptOnlyFromVortex,)
116
+ self.__deferToThread = deferToThread
117
+ self.__inlineCallbacks = inlineCallbacks
118
+
119
+ self.__funcName = ""
120
+ if func.__globals__["__spec__"]:
121
+ self.__funcName += func.__globals__["__spec__"].name
122
+ self.__funcName += "." + func.__qualname__
123
+
124
+ if self.__funcName in self.__registeredFuncNames:
125
+ raise Exception(
126
+ "RPC function name %s is already registered" % self.__funcName
127
+ )
128
+ self.__registeredFuncNames.add(self.__funcName)
129
+
130
+ # Define the FILT
131
+ self._filt = {"_internal": "vortexRPC", "key": self.__funcName}
132
+ self._filt.update(additionalFilt)
133
+
134
+ # Define the Endpoint
135
+
136
+ def start(self, funcSelf=None):
137
+ """Start
138
+
139
+ If this is a class method, then bind the function to the
140
+ object passed in by bindToSelf.
141
+
142
+ :param funcSelf: The object to bind the class instance methods self to.
143
+
144
+ """
145
+ if VortexFactory.isVortexNameLocal(self.__listeningVortexName):
146
+ self.__ep = PayloadEndpoint(self._filt, self._processCall)
147
+ logger.debug("RPC %s listening", self.__funcName)
148
+
149
+ else:
150
+ logger.error(
151
+ "Ignoring request to start listening for RPC %s "
152
+ "as vortex name %s is not local",
153
+ self.__funcName,
154
+ self.__listeningVortexName,
155
+ )
156
+
157
+ self.__funcSelf = funcSelf
158
+
159
+ return self
160
+
161
+ def shutdown(self):
162
+ """Shutdown
163
+
164
+ Shuts down the RPC PayloadEndpoint
165
+ """
166
+ self.__ep.shutdown()
167
+ self.__func = None
168
+ self.__funcSelf = None
169
+
170
+ @inlineCallbacks
171
+ def _processCall(
172
+ self,
173
+ payloadEnvelope: PayloadEnvelope,
174
+ vortexName,
175
+ sendResponse,
176
+ *args,
177
+ **kwargs
178
+ ):
179
+ """Process
180
+
181
+ Process the incoming RPC call payloads.
182
+
183
+ """
184
+ # If the sending vortex, is local, then ignore it, RPC can not be called locally
185
+ if VortexFactory.isVortexNameLocal(vortexName):
186
+ logger.warning(
187
+ "Received RPC call to %s, from local vortex %s, ignoring it",
188
+ self.__funcName,
189
+ vortexName,
190
+ )
191
+ return
192
+
193
+ # Apply the "allow" logic
194
+ if (
195
+ self.__acceptOnlyFromVortex
196
+ and vortexName not in self.__acceptOnlyFromVortex
197
+ ):
198
+ logger.debug(
199
+ "Call from non-accepted vortex %s, allowing only from %s",
200
+ vortexName,
201
+ str(self.__acceptOnlyFromVortex),
202
+ )
203
+ return
204
+
205
+ # Get the args tuple
206
+ payload = yield payloadEnvelope.decodePayloadDefer()
207
+ argsTuple = payload.tuples[0]
208
+ assert isinstance(argsTuple, _VortexRPCArgTuple), (
209
+ "argsTuple is not an instance of %s" % _VortexRPCArgTuple
210
+ )
211
+
212
+ logger.debug("Received RPC call for %s", self.__funcName)
213
+
214
+ # Call the method and setup the callbacks
215
+ result = yield self.callLocally(argsTuple.args, argsTuple.kwargs)
216
+ yield self._processCallCallback(
217
+ result, sendResponse, payloadEnvelope.filt
218
+ )
219
+
220
+ @inlineCallbacks
221
+ def _processCallCallback(self, result, sendResponseCallable, filt):
222
+
223
+ payloadEnvelope = yield (
224
+ Payload(
225
+ filt=filt, tuples=[_VortexRPCResultTuple(result=result)]
226
+ ).makePayloadEnvelopeDefer()
227
+ )
228
+
229
+ vortexMsg = yield payloadEnvelope.toVortexMsgDefer()
230
+
231
+ yield sendResponseCallable(vortexMsg, RPC_PRIORITY)
232
+
233
+ @inlineCallbacks
234
+ def callForVortexUuid(self, vortexUuid: str, *args, **kwargs):
235
+ yesMainThread()
236
+ return (
237
+ yield self.__callForVortex(
238
+ (args, kwargs), vortexUuid=vortexUuid, **kwargs
239
+ )
240
+ )
241
+
242
+ @inlineCallbacks
243
+ def __call__(self, *args, **kwargs):
244
+ """Call"""
245
+ yesMainThread()
246
+ return (
247
+ yield self.__callForVortex(
248
+ (args, kwargs), vortexName=self.__listeningVortexName
249
+ )
250
+ )
251
+
252
+ @inlineCallbacks
253
+ def __callForVortex(
254
+ self,
255
+ argsKwArgs,
256
+ vortexName: Optional[str] = None,
257
+ vortexUuid: Optional[str] = None,
258
+ ):
259
+ (args, kwargs) = argsKwArgs
260
+ assert vortexName or vortexUuid
261
+
262
+ try:
263
+ # FAKE Exception so we can raise a better stack trace later
264
+ raise Exception()
265
+ except:
266
+ stack = sys.exc_info()[2]
267
+
268
+ logger.debug("Calling RPC for %s", self.__funcName)
269
+
270
+ payloadEnvelope = yield (
271
+ Payload(
272
+ filt=copy(self._filt),
273
+ tuples=[_VortexRPCArgTuple(args=args, kwargs=kwargs)],
274
+ ).makePayloadEnvelopeDefer(compressionLevel=4)
275
+ )
276
+
277
+ pr = PayloadResponse(
278
+ payloadEnvelope,
279
+ timeout=self.__timeoutSeconds,
280
+ resultCheck=False,
281
+ logTimeoutError=False,
282
+ destVortexName=vortexName,
283
+ destVortexUuid=vortexUuid,
284
+ )
285
+
286
+ # Delete the payload, we don't need to keep it in memory while we
287
+ # get the result.
288
+ del payloadEnvelope
289
+
290
+ pr.addCallback(self._processResponseCallback, stack)
291
+ pr.addErrback(self._processResponseErrback, stack)
292
+
293
+ val = yield pr
294
+ return val
295
+
296
+ @inlineCallbacks
297
+ def _processResponseCallback(self, payloadEnvelope: PayloadEnvelope, stack):
298
+ """Process Response Callback
299
+
300
+ Convert the PayloadResponse payload to the result from the remotely called
301
+ method.
302
+
303
+ """
304
+
305
+ if not payloadEnvelope.result in (None, True):
306
+ return Failure(
307
+ Exception(payloadEnvelope.result).with_traceback(stack),
308
+ exc_tb=stack,
309
+ )
310
+
311
+ # Get the Result from the payload
312
+ payload = yield payloadEnvelope.decodePayloadDefer()
313
+ resultTuple = payload.tuples[0]
314
+ assert isinstance(resultTuple, _VortexRPCResultTuple), (
315
+ "resultTuple is not an instance of %s" % _VortexRPCResultTuple
316
+ )
317
+
318
+ logger.debug("Received RPC result for %s", self.__funcName)
319
+
320
+ # Return the remote result
321
+ return resultTuple.result
322
+
323
+ def _processResponseErrback(self, failure, stack):
324
+ """Process Response Errback
325
+
326
+ Convert the PayloadResponse payload to the result from the remotely called
327
+ method.
328
+
329
+ """
330
+
331
+ if failure.check(TimeoutError):
332
+ logger.error("Received RPC timeout for %s", self.__funcName)
333
+
334
+ return Failure(
335
+ Exception(
336
+ "RPC call timed out for %s", self.__funcName
337
+ ).with_traceback(stack),
338
+ exc_tb=stack,
339
+ )
340
+
341
+ return failure
342
+
343
+ def callLocally(self, args, kwargs):
344
+ """Call Locally
345
+
346
+ This method calls the wrapped function locally, ensuring it returns a
347
+ deferred as it's result.
348
+
349
+ """
350
+ try:
351
+ if self.__funcSelf:
352
+ args = [self.__funcSelf] + args
353
+
354
+ if self.__inlineCallbacks:
355
+ result = inlineCallbacks(self.__func)(*args, **kwargs)
356
+
357
+ elif self.__deferToThread:
358
+ result = deferToThread(self.__func, *args, **kwargs)
359
+
360
+ else:
361
+ result = self.__func(*args, **kwargs)
362
+
363
+ except Exception as e:
364
+ return fail(Failure(e))
365
+
366
+ if isinstance(result, Deferred):
367
+ return result
368
+
369
+ elif isinstance(result, Failure):
370
+ return fail(result)
371
+
372
+ else:
373
+ return succeed(result)
374
+
375
+
376
+ def vortexRPC(
377
+ listeningVortexName: str,
378
+ timeoutSeconds: float = 30.0,
379
+ acceptOnlyFromVortex: Optional[Union[str, tuple]] = None,
380
+ additionalFilt: Optional[dict] = None,
381
+ deferToThread: bool = False,
382
+ inlineCallbacks: bool = False,
383
+ ):
384
+ """Vortex RPC Decorator
385
+
386
+ :param listeningVortexName: If the local vortex name matches this name, then
387
+ a handler will be setup to listen for payloads for this RPC method.
388
+
389
+ :param timeoutSeconds: The seconds to wait for a response before calling the
390
+ deferreds errback with a TimeoutError
391
+
392
+ :param acceptOnlyFromVortex: Accept payloads (calls) only from this vortex.
393
+ Or None to accept from any.
394
+
395
+ :param additionalFilt: If specified, the items from this dict will be added
396
+ to the filt that this RPCs handler listens on.
397
+
398
+ :param deferToThread: Should the function be called in a thread, or in the
399
+ reactors main loop.
400
+
401
+ :param inlineCallbacks: Should the function be wrapped in the twisted
402
+ @inlinecallbacks decorator before it's called?.
403
+
404
+ :return A wrapped function, that will now work as an RPC call.
405
+
406
+
407
+ EXAMPLE:
408
+
409
+ Declare the function and decorate it.
410
+ Because this will be imported on both sides of the vortex, we specify the name
411
+ so that it will only be listening on one side ::
412
+
413
+
414
+ @vortexRPC("listenVortexName")
415
+ def myRemoteAddMethod(arg1:int, kwarg1:int=0) -> int:
416
+ return arg1 + kwarg1
417
+
418
+
419
+ Call the method
420
+
421
+ .. note:: The method will ALWAYS return a deferred, regardless of if the method
422
+ wrapped returns one or not.
423
+
424
+ ::
425
+
426
+
427
+ deferred = myRemoteAddMethod(5, kwarg1=7)
428
+ print(deferred.addCallback(lambda v:print(v)))
429
+
430
+
431
+
432
+ """
433
+ additionalFilt = additionalFilt if additionalFilt else {}
434
+
435
+ def decorator(func):
436
+ return _VortexRPC(
437
+ func,
438
+ listeningVortexName,
439
+ timeoutSeconds,
440
+ acceptOnlyFromVortex,
441
+ additionalFilt,
442
+ deferToThread,
443
+ inlineCallbacks,
444
+ )
445
+
446
+ return decorator
vortex/rpc/RPCTest.py ADDED
@@ -0,0 +1,12 @@
1
+ from vortex.rpc.RPC import vortexRPC
2
+
3
+
4
+ @vortexRPC("listenVortexName", acceptOnlyFromVortex="sendVortexName")
5
+ def myRemoteExceptionMethod(arg1: int, kwarg1: int = 0) -> int:
6
+ raise ValueError("A is not B")
7
+
8
+
9
+ @vortexRPC("listenVortexName", acceptOnlyFromVortex="sendVortexName")
10
+ def myRemoteAddMethod(arg1: int, kwarg1: int = 0) -> int:
11
+ print(arg1, kwarg1)
12
+ return arg1 + kwarg1
vortex/rpc/__init__.py ADDED
File without changes