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,27 @@
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
+ from .Tuple import Tuple, TupleField, addTupleType
10
+
11
+
12
+ @addTupleType
13
+ class DataWrapTuple(Tuple):
14
+ """Data Wrap Tuple
15
+
16
+ This tuple wraps a peice of data for use in RapUI.
17
+
18
+ Attributes:
19
+ name What ever you want to use it for
20
+ data The data
21
+
22
+ """
23
+
24
+ __tupleType__ = "c.s.r.datawraptuple"
25
+
26
+ name = TupleField(None)
27
+ data = TupleField(None)
vortex/DeferUtil.py ADDED
@@ -0,0 +1,224 @@
1
+ import inspect
2
+ import logging
3
+
4
+ import twisted
5
+ from twisted.internet import reactor
6
+ from twisted.internet.defer import (
7
+ maybeDeferred,
8
+ Deferred,
9
+ inlineCallbacks,
10
+ ensureDeferred,
11
+ )
12
+ from twisted.internet.threads import deferToThread
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def callMethodLater(func):
18
+ def wrapper(*args, **kwargs):
19
+ return reactor.callLater(0, func, *args, **kwargs)
20
+
21
+ return wrapper
22
+
23
+
24
+ def nonConcurrentMethod(method):
25
+ def call(*args, **kwargs):
26
+ if call.running:
27
+ return
28
+ call.running = True
29
+ try:
30
+ return method(*args, **kwargs)
31
+ finally:
32
+ call.running = False
33
+
34
+ call.running = False
35
+
36
+ return call
37
+
38
+
39
+ def maybeDeferredWrap(funcToWrap):
40
+ """Maybe Deferred Wrap
41
+
42
+ A decorator that ensures a function will return a deferred.
43
+
44
+ """
45
+
46
+ def func(*args, **kwargs):
47
+ return maybeDeferred(funcToWrap, *args, **kwargs)
48
+
49
+ return func
50
+
51
+
52
+ def ensureDeferredWrap(funcToWrap):
53
+ """Ensured Deferred Wrap
54
+
55
+ A decorator that converts asyncio functions to ones that
56
+ return Twisted Deferred instead.
57
+
58
+ This keeps all the async processing in Twisteds reactive design while providing
59
+ support of the await/async syntax (and asyncio processing)
60
+
61
+
62
+ """
63
+
64
+ def func(*args, **kwargs):
65
+ return ensureDeferred(funcToWrap(*args, **kwargs))
66
+
67
+ return func
68
+
69
+
70
+ def vortexLogFailure(failure, loggerArg, consumeError=False, successValue=True):
71
+ """
72
+ Log a Twisted Failure instance using the provided logger.
73
+
74
+ Args:
75
+ failure: Twisted Failure instance to log
76
+ loggerArg: Logger instance to use for logging
77
+ consumeError: If True, returns successValue instead of the failure
78
+ successValue: Value to return if consumeError is True
79
+
80
+ Returns:
81
+ The original failure or successValue depending on consumeError
82
+ """
83
+ try:
84
+ if not hasattr(failure, "_vortexFailureLogged"):
85
+ exc_info = (
86
+ failure.type,
87
+ failure.value,
88
+ failure.getTracebackObject(),
89
+ )
90
+
91
+ # Log with full exception info for better tracebacks
92
+ loggerArg.exception(str(failure.value), exc_info=exc_info)
93
+
94
+ # Mark as logged to prevent duplicate logging
95
+ failure._vortexFailureLogged = True
96
+
97
+ return successValue if consumeError else failure
98
+
99
+ except Exception as e:
100
+ # If something goes wrong in our logging logic, log that error
101
+ loggerArg.exception("Error while logging failure: %s", str(e))
102
+ return failure
103
+
104
+
105
+ def vortexLogAndConsumeFailure(failure, loggerArg, successValue=True):
106
+ return vortexLogFailure(
107
+ failure, loggerArg, consumeError=True, successValue=successValue
108
+ )
109
+
110
+
111
+ printFailure = vortexLogFailure
112
+
113
+
114
+ def vortexInlineCallbacksLogAndConsumeFailure(loggerArg):
115
+ """Vortex InlineCallbacks Log and Consume Failure
116
+
117
+ This is exactly the same as @inlineCallbacks decorator, except it will log
118
+ and consume any deferred failures that are thrown.
119
+ """
120
+
121
+ def wrapper(funcToWrap):
122
+ funcIcb = inlineCallbacks(funcToWrap)
123
+
124
+ def called(*args, **kwargs):
125
+ d = funcIcb(*args, **kwargs)
126
+ d.addErrback(vortexLogAndConsumeFailure, loggerArg)
127
+ return d
128
+
129
+ return called
130
+
131
+ return wrapper
132
+
133
+
134
+ class YieldInDeferToThreadException(Exception):
135
+ pass
136
+
137
+
138
+ def deferToThreadWrapWithLogger(
139
+ logger, consumeError=False, checkMainThread=True
140
+ ):
141
+ """Defer To Thread Wrap With Logger
142
+
143
+ This method is a decorator used to send blocking methods to threads easily.
144
+
145
+ :param logger: An instance from logging.getLogger(..)
146
+ :param consumeError: Should errors occuring in the thread be consumed or errback'd
147
+ :param checkMainThread: Don't use this parameter, it's only needed during reactor
148
+ startup when the reactors thread isn't properly set and the check
149
+ fails.
150
+
151
+ :return: A deferred.
152
+
153
+ Usage: ::
154
+
155
+ import logging
156
+ logger = logging.getLogger(__name__)
157
+ @deferToThreadWrapWithLogger(logger)
158
+ def myFunction(arg1, kw=True):
159
+ pass
160
+
161
+
162
+ """
163
+ assert isinstance(
164
+ logger, logging.Logger
165
+ ), """Usage:
166
+ import logging
167
+ logger = logging.getLogger(__name__)
168
+ @deferToThreadWrapWithLogger(logger)
169
+ def myFunction(arg1, kw=True):
170
+ pass
171
+ """
172
+
173
+ def wrapper(funcToWrap) -> Deferred:
174
+ def checkForInlineCallbackYield(result):
175
+ if inspect.isgenerator(result):
176
+ logger.error(
177
+ "Module Name %s, Class Name %s, Function Name %s"
178
+ " has a yield in it. IT SHOULD NOT.",
179
+ funcToWrap.__module__,
180
+ funcToWrap.__class__.__name__,
181
+ funcToWrap.__name__,
182
+ )
183
+ raise YieldInDeferToThreadException()
184
+ return result
185
+
186
+ def func(*args, **kwargs):
187
+ if not twisted.python.threadable.isInIOThread() and checkMainThread:
188
+ logger.error(
189
+ "Module Name %s, Class Name %s, Function Name %s"
190
+ " is being deferred from a thread. IT SHOULD NOT.",
191
+ funcToWrap.__module__,
192
+ funcToWrap.__class__.__name__,
193
+ funcToWrap.__name__,
194
+ )
195
+ raise Exception(
196
+ "Deferring to a thread can only be done from the main thread"
197
+ )
198
+
199
+ d = deferToThread(funcToWrap, *args, **kwargs)
200
+ d.addCallback(checkForInlineCallbackYield)
201
+ d.addErrback(vortexLogFailure, logger, consumeError=consumeError)
202
+ return d
203
+
204
+ return func
205
+
206
+ return wrapper
207
+
208
+
209
+ def isMainThread():
210
+ return twisted.python.threadable.isInIOThread()
211
+
212
+
213
+ def noMainThread():
214
+ if isMainThread():
215
+ raise Exception(
216
+ "Blocking operations shouldn't occur in the reactors thread."
217
+ )
218
+
219
+
220
+ def yesMainThread():
221
+ if not isMainThread():
222
+ raise Exception(
223
+ "Async operations must occur in the reactors main thread."
224
+ )
vortex/Jsonable.py ADDED
@@ -0,0 +1,304 @@
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
+
10
+ import logging
11
+ import traceback
12
+ import typing
13
+ from copy import copy, deepcopy
14
+ from json import JSONEncoder
15
+ from typing import List
16
+ from _collections import defaultdict
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ from .SerialiseUtil import *
21
+
22
+ ###############################################################################
23
+ # JSON Constants
24
+ ###############################################################################
25
+
26
+ # Globally mess with the json dump separators for more compact json
27
+ JSONEncoder.item_separator = ","
28
+ JSONEncoder.key_separator = ":"
29
+
30
+
31
+ class Jsonable(object):
32
+ """Xmlable
33
+ Inherit from this class if you want to serialise to XML.
34
+ """
35
+
36
+ __fieldNames__: List[str] = None
37
+ __rapuiSerialiseType__ = T_GENERIC_CLASS
38
+
39
+ __rawJonableFields__: List[str] = None
40
+
41
+ JSON_CLASS_TYPE = "_ct"
42
+ JSON_CLASS = "_c"
43
+ JSON_TUPLE_TYPE = "_c"
44
+ JSON_FIELD_TYPE = "_ft"
45
+ JSON_FIELD_DATA = "_fd"
46
+
47
+ __memoryLoggingRefs = None
48
+ __memoryLoggingEnabled = False
49
+
50
+ @classmethod
51
+ def setupMemoryLogging(cls) -> None:
52
+ cls.__memoryLoggingRefs = defaultdict(lambda: 0)
53
+ cls.__memoryLoggingEnabled = True
54
+
55
+ @classmethod
56
+ def memoryLoggingDump(
57
+ cls, top=10, over=100
58
+ ) -> List[typing.Tuple[str, int]]:
59
+ if not Jsonable.__memoryLoggingRefs:
60
+ return []
61
+
62
+ data = sorted(
63
+ Jsonable.__memoryLoggingRefs.items(),
64
+ key=lambda x: x[1],
65
+ reverse=True,
66
+ )
67
+
68
+ return list(filter(lambda x: x[1] >= over, data))[:top]
69
+
70
+ def __memLoggingKey(self):
71
+ if getattr(self, "__rapuiSerialiseType__") == T_RAPUI_TUPLE:
72
+ key = "Tuple: " + getattr(self, "__tupleType__")
73
+
74
+ elif getattr(self, "__rapuiSerialiseType__") == T_RAPUI_PAYLOAD:
75
+ assert hasattr(self, "filt"), "Payload is missing filt"
76
+ if "key" in self.filt:
77
+ key = "Payload: key=" + self.filt["key"]
78
+ else:
79
+ key = "Payload: " + str(self.filt)
80
+
81
+ else:
82
+ key = "Jsonable: " + getattr(self, "__rapuiSerialiseType__")
83
+
84
+ return key
85
+
86
+ def __init__(self):
87
+ if Jsonable.__memoryLoggingEnabled:
88
+ Jsonable.__memoryLoggingRefs[self.__memLoggingKey()] += 1
89
+
90
+ def __del__(self):
91
+ if Jsonable.__memoryLoggingEnabled:
92
+ Jsonable.__memoryLoggingRefs[self.__memLoggingKey()] -= 1
93
+
94
+ def __isRawJsonableField(self, name: str) -> bool:
95
+ if not (name and self.__rawJonableFields__):
96
+ return False
97
+ return name in self.__rawJonableFields__
98
+
99
+ def toJsonField(self, value, jsonDict=None, name=None):
100
+ """To Json Field"""
101
+ from .Tuple import TupleField
102
+
103
+ if isinstance(value, TupleField):
104
+ value = (
105
+ None if value.defaultValue == None else copy(value.defaultValue)
106
+ )
107
+
108
+ try:
109
+ valueType = V_NULL if value is None else toRapuiType(value)
110
+ except KeyError as e:
111
+ raise KeyError(
112
+ "%s field name is %s, type is %s", e, name, value.__class__
113
+ )
114
+
115
+ # Payloads and Tuples
116
+ if self.__isRawJsonableField(name):
117
+ # This sho
118
+ convertedValue = deepcopy(value)
119
+
120
+ elif isinstance(value, Jsonable):
121
+ convertedValue = value.toJsonDict()
122
+
123
+ elif isinstance(value, dict):
124
+ convertedValue = {}
125
+ for key, value in list(value.items()):
126
+ self.toJsonField(value, convertedValue, key)
127
+
128
+ elif isinstance(value, (set, list, tuple)):
129
+ convertedValue = []
130
+ for val in value:
131
+ convertedValue.append(self.toJsonField(val))
132
+
133
+ # Decimals are detected as floats for some reason, so convert it here
134
+ elif valueType == T_FLOAT:
135
+ convertedValue = float(value)
136
+
137
+ elif valueType in (T_INT, T_BOOL, T_STR):
138
+ convertedValue = value
139
+
140
+ elif valueType == V_NULL:
141
+ convertedValue = None
142
+
143
+ else:
144
+ convertedValue = toStr(value)
145
+
146
+ # Non standard values need a dict to store their value type attributes
147
+ # Create a sub dict that contains the value and type
148
+ if valueType not in (
149
+ T_FLOAT,
150
+ T_STR,
151
+ V_NULL,
152
+ T_BOOL,
153
+ T_LIST,
154
+ T_DICT,
155
+ ) and not isinstance(value, Jsonable):
156
+ convertedValue = {
157
+ Jsonable.JSON_FIELD_TYPE: valueType,
158
+ Jsonable.JSON_FIELD_DATA: convertedValue,
159
+ }
160
+
161
+ if name and jsonDict is not None:
162
+ # Now assign the value and it's data type if applicable
163
+ if not toRapuiType(name) in (T_STR, T_INT, T_FLOAT):
164
+ raise Exception(
165
+ "name=%s, type=%s, is not an allowed dict key",
166
+ name,
167
+ toRapuiType(name),
168
+ )
169
+
170
+ jsonDict[name] = convertedValue
171
+
172
+ return convertedValue
173
+
174
+ # -----------------------------------------------------------------------------
175
+ def fromJsonField(self, value, valueType=None):
176
+ # Single Value
177
+ if (
178
+ valueType == V_NULL or value is None
179
+ ): # V_NULL will never be set in toJsonField
180
+ return None
181
+
182
+ if valueType == T_INT:
183
+ return int(value)
184
+
185
+ if isinstance(value, dict) and Jsonable.JSON_CLASS_TYPE in value:
186
+ valueType = value[Jsonable.JSON_CLASS_TYPE]
187
+
188
+ # JSON handles these types natively,
189
+ # if there is no type then these are the right types
190
+ if valueType is None:
191
+ valueType = toRapuiType(value)
192
+ if valueType in (T_BOOL, T_FLOAT, T_INT, T_STR):
193
+ return value
194
+
195
+ # Non standard values need a dict to store their value type attributes, decode these
196
+ if isinstance(value, dict) and Jsonable.JSON_FIELD_TYPE in value:
197
+ return self.fromJsonField(
198
+ value[Jsonable.JSON_FIELD_DATA], value[Jsonable.JSON_FIELD_TYPE]
199
+ )
200
+
201
+ # Tuple
202
+ if valueType == T_RAPUI_TUPLE:
203
+ if not Jsonable.JSON_TUPLE_TYPE in value:
204
+ raise Exception(
205
+ "Tuple is missing type field '_c',"
206
+ " if this is a javascript tuple,"
207
+ " ensure it's constructor calls super()"
208
+ )
209
+
210
+ tupleType = value[Jsonable.JSON_TUPLE_TYPE]
211
+
212
+ from .Tuple import TUPLE_TYPES_BY_NAME
213
+
214
+ if not tupleType in TUPLE_TYPES_BY_NAME:
215
+ raise Exception(
216
+ "Tuple type |%s| not registered within this program.",
217
+ tupleType,
218
+ )
219
+
220
+ try:
221
+ from .Tuple import TUPLE_TYPES_BY_NAME
222
+
223
+ return TUPLE_TYPES_BY_NAME[tupleType]().fromJsonDict(value)
224
+
225
+ except Exception as e:
226
+ logger.critical(traceback.format_exc())
227
+ raise Exception("%s for tuple type %s" % (str(e), tupleType))
228
+
229
+ # Handle the case of payloads within payloads
230
+ if valueType == T_RAPUI_PAYLOAD:
231
+ from .Payload import Payload
232
+
233
+ return Payload().fromJsonDict(value)
234
+
235
+ if valueType == T_RAPUI_PAYLOAD_ENVELOPE:
236
+ from .PayloadEnvelope import PayloadEnvelope
237
+
238
+ return PayloadEnvelope().fromJsonDict(value)
239
+
240
+ if valueType == T_GENERIC_CLASS:
241
+ # OTHER JSONABLES GO HERE, INSPECT THE tuple_TYPE
242
+ # jsonClass = value[JSON_CLASS]
243
+ raise NotImplementedError()
244
+ # import importlib
245
+ # MyClass = getattr(importlib.import_module("module.submodule"), "Klass")
246
+ # instance = MyClass()
247
+
248
+ if valueType == T_DICT:
249
+ # Dict
250
+ restoredDict = {}
251
+ for subName, subVal in list(value.items()):
252
+ try:
253
+ subNamStr = subName
254
+ subName = float(subNamStr)
255
+ subName = int(subNamStr)
256
+ except ValueError:
257
+ pass
258
+ restoredDict[subName] = self.fromJsonField(subVal)
259
+
260
+ return restoredDict
261
+
262
+ if valueType == T_LIST:
263
+ restoredList = []
264
+ for subVal in value:
265
+ restoredList.append(self.fromJsonField(subVal))
266
+
267
+ return restoredList
268
+
269
+ return fromStr(value, valueType)
270
+
271
+ def toJsonDict(self):
272
+
273
+ fieldNames = self.__fieldNames__
274
+
275
+ jsonDict = {Jsonable.JSON_CLASS_TYPE: self.__rapuiSerialiseType__}
276
+
277
+ if hasattr(self, "tupleName"):
278
+ jsonDict[Jsonable.JSON_TUPLE_TYPE] = self.tupleName()
279
+
280
+ else:
281
+ jsonDict[Jsonable.JSON_CLASS] = className(self)
282
+
283
+ for name in fieldNames:
284
+ data = getattr(self, name)
285
+ self.toJsonField(data, jsonDict, name)
286
+
287
+ return jsonDict
288
+
289
+ def fromJsonDict(self, jsonDict):
290
+ """From Xml
291
+ Returns and instance of this object populated with data from the json dict
292
+ """
293
+
294
+ # Use the fromJsonField code to convert all the values
295
+ for name, value in list(jsonDict.items()):
296
+ if name.startswith("_"):
297
+ continue
298
+
299
+ if self.__isRawJsonableField(name):
300
+ setattr(self, name, value)
301
+ else:
302
+ setattr(self, name, self.fromJsonField(value))
303
+
304
+ return self
vortex/Payload.py ADDED
@@ -0,0 +1,115 @@
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 zlib
11
+ from base64 import b64encode, b64decode
12
+ from datetime import datetime
13
+ from typing import List, Optional, Dict
14
+
15
+ import pytz
16
+ import json
17
+
18
+ from vortex.DeferUtil import deferToThreadWrapWithLogger, noMainThread
19
+ from .Jsonable import Jsonable
20
+ from .SerialiseUtil import T_RAPUI_PAYLOAD
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ PayloadList = List["Payload"]
25
+ EncodedPayloadList = List[bytes]
26
+
27
+
28
+ class Payload(Jsonable):
29
+ """Payload
30
+ This object represents a hierarchy of data transferred between client and server
31
+ """
32
+
33
+ __fieldNames__ = ["filt", "tuples", "date"]
34
+ __rapuiSerialiseType__ = T_RAPUI_PAYLOAD
35
+
36
+ def __init__(self, filt: Optional[Dict] = None, tuples=None) -> None:
37
+ """Constructor"""
38
+ super().__init__()
39
+ self.filt = {} if filt is None else filt
40
+ self.tuples = [] if tuples is None else tuples
41
+ self.date = datetime.now(pytz.utc)
42
+
43
+ if isinstance(filt, str):
44
+ self.filt = {self.filt: None}
45
+
46
+ if not isinstance(self.tuples, list):
47
+ self.tuples = [self.tuples]
48
+
49
+ # -------------------------------------------
50
+ # PayloadEnvelopes helper messages
51
+
52
+ def makePayloadEnvelope(self, result=None, compressionLevel: int = 9):
53
+ from .PayloadEnvelope import PayloadEnvelope
54
+
55
+ noMainThread()
56
+ encodedSelf = self.toEncodedPayload(compressionLevel=compressionLevel)
57
+ return PayloadEnvelope(
58
+ self.filt, encodedPayload=encodedSelf, date=self.date, result=result
59
+ )
60
+
61
+ @deferToThreadWrapWithLogger(logger)
62
+ def makePayloadEnvelopeDefer(self, result=None, compressionLevel: int = 9):
63
+ return self.makePayloadEnvelope(
64
+ result=result, compressionLevel=compressionLevel
65
+ )
66
+
67
+ # -------------------------------------------
68
+ # VortexMsg helper messages
69
+
70
+ def makePayloadEnvelopeVortexMsg(
71
+ self, result=None, compressionLevel: int = 9
72
+ ):
73
+ noMainThread()
74
+ return self.makePayloadEnvelope(
75
+ result=result, compressionLevel=compressionLevel
76
+ ).toVortexMsg()
77
+
78
+ @deferToThreadWrapWithLogger(logger)
79
+ def makePayloadEnvelopeVortexMsgDefer(
80
+ self, result=None, compressionLevel: int = 9
81
+ ):
82
+ return self.makePayloadEnvelopeVortexMsg(
83
+ result=result, compressionLevel=compressionLevel
84
+ )
85
+
86
+ # -------------------------------------------
87
+ # JSON Related methods
88
+ def _fromJson(self, jsonStr):
89
+ jsonDict = json.loads(jsonStr)
90
+
91
+ assert jsonDict[Jsonable.JSON_CLASS_TYPE] == self.__rapuiSerialiseType__
92
+ return self.fromJsonDict(jsonDict)
93
+
94
+ def _toJson(self) -> str:
95
+ return json.dumps(self.toJsonDict())
96
+
97
+ # -------------------------------------------
98
+ # VortexServer Message Methods
99
+ def toEncodedPayload(self, compressionLevel: int = 9) -> str:
100
+ jsonStr = self._toJson()
101
+ return b64encode(
102
+ zlib.compress(jsonStr.encode("UTF-8"), compressionLevel)
103
+ ).decode()
104
+
105
+ @deferToThreadWrapWithLogger(logger)
106
+ def toEncodedPayloadDefer(self, compressionLevel: int = 9) -> str:
107
+ return self.toEncodedPayload(compressionLevel=compressionLevel)
108
+
109
+ def fromEncodedPayload(self, encodedPayload: str):
110
+ jsonStr = zlib.decompress(b64decode(encodedPayload))
111
+ return self._fromJson(jsonStr)
112
+
113
+ @deferToThreadWrapWithLogger(logger)
114
+ def fromEncodedPayloadDefer(self, encodedPayload: str):
115
+ return self.fromEncodedPayload(encodedPayload)