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
vortex/TupleAction.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
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 uuid
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Dict
|
|
12
|
+
from typing import List
|
|
13
|
+
|
|
14
|
+
import pytz
|
|
15
|
+
|
|
16
|
+
from vortex.Tuple import Tuple
|
|
17
|
+
from vortex.Tuple import TupleField
|
|
18
|
+
from vortex.Tuple import addTupleType
|
|
19
|
+
from vortex.TupleSelector import TupleSelector
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TupleActionABC(Tuple):
|
|
23
|
+
uuid: str = TupleField(
|
|
24
|
+
comment="Uniquely generated id for this action", typingType=str
|
|
25
|
+
)
|
|
26
|
+
dateTime: datetime = TupleField(
|
|
27
|
+
comment="The datetime this action was created", typingType=datetime
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def __init__(self, **kwargs):
|
|
31
|
+
if not self.__tupleType__:
|
|
32
|
+
raise NotImplementedError(
|
|
33
|
+
"TupleActionABC can not be instantiated,"
|
|
34
|
+
" please inherit it and implement a tuple"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
Tuple.__init__(self, **kwargs)
|
|
38
|
+
|
|
39
|
+
if not "uuid" in kwargs:
|
|
40
|
+
self.uuid = str(uuid.uuid1()) # Based on the host, and the time
|
|
41
|
+
|
|
42
|
+
if not "dateTime" in kwargs:
|
|
43
|
+
self.dateTime = datetime.now(pytz.utc)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@addTupleType
|
|
47
|
+
class TupleGenericAction(TupleActionABC):
|
|
48
|
+
"""Tuple Generic Action
|
|
49
|
+
|
|
50
|
+
This is a generic action, to be used when the implementor doesn't want to implement
|
|
51
|
+
concrete classes for each action type.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
__tupleType__ = "vortex.TupleGenericAction"
|
|
56
|
+
|
|
57
|
+
key = TupleField(comment="An optional key for this action", typingType=str)
|
|
58
|
+
|
|
59
|
+
data = TupleField(comment="Optional data for the update", typingType=str)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@addTupleType
|
|
63
|
+
class TupleUpdateAction(TupleActionABC):
|
|
64
|
+
__tupleType__ = "vortex.TupleUpdateAction"
|
|
65
|
+
|
|
66
|
+
tupleSelector = TupleField(
|
|
67
|
+
comment="The tuple selector for this action", typingType=TupleSelector
|
|
68
|
+
)
|
|
69
|
+
tupleChanges = TupleField(
|
|
70
|
+
comment="An array of {old:v,new:v} dicts for the changes",
|
|
71
|
+
typingType=List[Dict],
|
|
72
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
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 vortex.TestTuple import TestTuple
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def makeTestTupleData(count=5):
|
|
13
|
+
tuples = []
|
|
14
|
+
for num in range(count):
|
|
15
|
+
uniStr = "#%s double hyphen :-( — “fancy quotes”" % num
|
|
16
|
+
tuples.append(
|
|
17
|
+
TestTuple(
|
|
18
|
+
aInt=num,
|
|
19
|
+
aBoolTrue=bool(num % 2),
|
|
20
|
+
aString="This is tuple #%s" % num,
|
|
21
|
+
aStrWithUnicode=uniStr,
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
return tuples
|
vortex/TuplePerfTest.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
import os
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
import psutil
|
|
6
|
+
import pytz
|
|
7
|
+
|
|
8
|
+
process = psutil.Process(os.getpid())
|
|
9
|
+
|
|
10
|
+
ITER_COUNT = 1000 * 1000 * 5
|
|
11
|
+
|
|
12
|
+
RESULT = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def makeL(i):
|
|
16
|
+
# Use this line to negate the effect of the strings on the test
|
|
17
|
+
# return "Python is smart and will only create one string with this line"
|
|
18
|
+
|
|
19
|
+
# Use this if you want to see the difference with 5 million unique strings
|
|
20
|
+
return "This is a sample string %s" % i
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def timeit(method):
|
|
24
|
+
def timed(*args, **kw):
|
|
25
|
+
global RESULT
|
|
26
|
+
RESULT = None
|
|
27
|
+
gc.collect()
|
|
28
|
+
|
|
29
|
+
s = datetime.now(pytz.utc)
|
|
30
|
+
startMem = process.memory_info().rss
|
|
31
|
+
RESULT = method(*args, **kw)
|
|
32
|
+
e = datetime.now(pytz.utc)
|
|
33
|
+
endMem = process.memory_info().rss
|
|
34
|
+
|
|
35
|
+
sizeMb = (endMem - startMem) / 1024 / 1024
|
|
36
|
+
sizeMbStr = "{0:,}".format(round(sizeMb, 2))
|
|
37
|
+
|
|
38
|
+
print(
|
|
39
|
+
"Time Taken = %s, \t%s, \tSize = %s"
|
|
40
|
+
% (e - s, method.__name__, sizeMbStr)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return timed
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
from vortex.Tuple import Tuple, addTupleType, TupleHash
|
|
47
|
+
from vortex.Payload import Payload
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@addTupleType
|
|
51
|
+
class X(Tuple):
|
|
52
|
+
__tupleType__ = "X"
|
|
53
|
+
__slots__ = ["i", "l"]
|
|
54
|
+
|
|
55
|
+
def __init__(self, i=None, l=None):
|
|
56
|
+
self.i, self.l = i, l
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@timeit
|
|
60
|
+
def provile_dict_of_nt():
|
|
61
|
+
return [X(i=i, l=makeL(i)) for i in range(ITER_COUNT)]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
provile_dict_of_nt()
|
|
66
|
+
|
|
67
|
+
tupleIn = X("val1", "val2")
|
|
68
|
+
encodedPayload = Payload(tuples=[tupleIn]).toEncodedPayload()
|
|
69
|
+
payload = Payload().fromEncodedPayload(encodedPayload)
|
|
70
|
+
|
|
71
|
+
assert TupleHash(tupleIn) == TupleHash(payload.tuples[0])
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from vortex.Tuple import (
|
|
5
|
+
Tuple,
|
|
6
|
+
addTupleType,
|
|
7
|
+
TupleField,
|
|
8
|
+
PolymorphicTupleTypeFieldArg,
|
|
9
|
+
TupleHash,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@addTupleType
|
|
16
|
+
class Parent(Tuple):
|
|
17
|
+
__tupleType__ = "com.synerty.Tuples.Parent"
|
|
18
|
+
__tupleArgs__ = (PolymorphicTupleTypeFieldArg("concreteType"),)
|
|
19
|
+
|
|
20
|
+
aStr: str = TupleField()
|
|
21
|
+
concreteType: str = TupleField()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@addTupleType
|
|
25
|
+
class Child(Parent):
|
|
26
|
+
__tupleType__ = "com.synerty.Tuples.Child"
|
|
27
|
+
|
|
28
|
+
aInt: int = TupleField()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@addTupleType
|
|
32
|
+
class Another(Tuple):
|
|
33
|
+
__tupleType__ = "com.synerty.Tuples.Another"
|
|
34
|
+
|
|
35
|
+
aInt: int = TupleField()
|
|
36
|
+
concreteType: str = TupleField()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PolymorphicTupleTypeFieldArgTest(unittest.TestCase):
|
|
40
|
+
def testParentTupleToFromJSON(self):
|
|
41
|
+
parent1 = Parent(aStr="Test", concreteType=Parent.tupleType())
|
|
42
|
+
parent2 = Parent(aStr="Test", concreteType=Parent.tupleType())
|
|
43
|
+
parent3 = Parent(aStr="Test 1", concreteType=Parent.tupleType())
|
|
44
|
+
|
|
45
|
+
self.assertEqual(TupleHash(parent1), TupleHash(parent2))
|
|
46
|
+
self.assertNotEqual(TupleHash(parent1), TupleHash(parent3))
|
|
47
|
+
|
|
48
|
+
def testChildFieldsInToJsonDict(self):
|
|
49
|
+
data = dict(aStr="Test", aInt=1, concreteType=Child.tupleType())
|
|
50
|
+
child = Child(**data)
|
|
51
|
+
|
|
52
|
+
self.assertEqual(data, child.tupleToRestfulJsonDict())
|
|
53
|
+
|
|
54
|
+
def testChildTupleToFromJSON(self):
|
|
55
|
+
# Construct Child through Child
|
|
56
|
+
child1 = Child(aStr="Test", aInt=1, concreteType=Child.tupleType())
|
|
57
|
+
|
|
58
|
+
# Construct Child through Parent
|
|
59
|
+
child2 = Tuple.restfulJsonDictToTupleWithValidation(
|
|
60
|
+
child1.tupleToRestfulJsonDict(), Parent
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.assertEqual(TupleHash(child1), TupleHash(child2))
|
|
64
|
+
|
|
65
|
+
# Parent should not match Child
|
|
66
|
+
parent1 = Parent(aStr="Test", concreteType=Parent.tupleType())
|
|
67
|
+
self.assertNotEqual(TupleHash(child1), TupleHash(parent1))
|
|
68
|
+
|
|
69
|
+
# Parent should only be able to init its children
|
|
70
|
+
another1 = Another(aInt=1, concreteType=Another.tupleType())
|
|
71
|
+
with self.assertRaises(TypeError):
|
|
72
|
+
Tuple.restfulJsonDictToTupleWithValidation(
|
|
73
|
+
another1.tupleToRestfulJsonDict(), Parent
|
|
74
|
+
)
|
|
@@ -0,0 +1,140 @@
|
|
|
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 json
|
|
11
|
+
from typing import Dict, Annotated
|
|
12
|
+
from typing import List
|
|
13
|
+
from typing import Union
|
|
14
|
+
|
|
15
|
+
from ddt import data, ddt
|
|
16
|
+
from twisted.trial import unittest
|
|
17
|
+
|
|
18
|
+
from vortex.Tuple import Tuple
|
|
19
|
+
from vortex.Tuple import TupleField
|
|
20
|
+
from vortex.Tuple import TupleHash, IntTupleFieldValidator
|
|
21
|
+
from vortex.Tuple import addTupleType
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@addTupleType
|
|
25
|
+
class RestfulTestTuple(Tuple):
|
|
26
|
+
"""
|
|
27
|
+
classdocs
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
__tupleType__ = "synerty.vortex.RestfulTestTuple"
|
|
31
|
+
aInt: int = TupleField()
|
|
32
|
+
aIntWithValidator: Annotated[
|
|
33
|
+
int, IntTupleFieldValidator(1, 100)
|
|
34
|
+
] = TupleField()
|
|
35
|
+
aFloat: float = TupleField()
|
|
36
|
+
aString: str = TupleField()
|
|
37
|
+
aBoolTrue: bool = TupleField()
|
|
38
|
+
aBoolFalse: bool = TupleField()
|
|
39
|
+
aList: List[Union[str, int, bool, float]] = TupleField()
|
|
40
|
+
aDict: Dict[str, Union[str, float]] = TupleField()
|
|
41
|
+
aSubTuple: "RestfulSubTestTuple" = TupleField()
|
|
42
|
+
aListOfSubTuples: List["RestfulSubTestTuple"] = TupleField()
|
|
43
|
+
aDictOfSubTuples: Dict[str, "RestfulSubTestTuple"] = TupleField()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@addTupleType
|
|
47
|
+
class RestfulSubTestTuple(Tuple):
|
|
48
|
+
"""Test Sub Tuple
|
|
49
|
+
This tuple will test if we can have tuples in tuples and still serialise and
|
|
50
|
+
deserialise them.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
__tupleType__ = "rapui.synerty.RestfulSubTestTuple"
|
|
54
|
+
subInt: int = TupleField()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _makeRestfultTestTuple():
|
|
58
|
+
tuple_ = RestfulTestTuple()
|
|
59
|
+
tuple_.aBoolTrue = True
|
|
60
|
+
tuple_.aBoolFalse = False
|
|
61
|
+
tuple_.aFloat = 2.56
|
|
62
|
+
tuple_.aInt = 1231231
|
|
63
|
+
tuple_.aIntWithValidator = 50
|
|
64
|
+
tuple_.aString = "test string from 345345345@$#%#$%#$%#$%#"
|
|
65
|
+
tuple_.aList = ["y", 3, True, 3.3]
|
|
66
|
+
tuple_.aDict = {
|
|
67
|
+
"a": "test str",
|
|
68
|
+
"1": 1.234,
|
|
69
|
+
} # Key strings are loaded as unicode
|
|
70
|
+
tuple_.aDictOfSubTuples = {}
|
|
71
|
+
|
|
72
|
+
subTuple = RestfulSubTestTuple()
|
|
73
|
+
subTuple.subInt = 2048
|
|
74
|
+
tuple_.aSubTuple = subTuple
|
|
75
|
+
|
|
76
|
+
tuple_.aListOfSubTuples = []
|
|
77
|
+
for x in range(2):
|
|
78
|
+
subTuple = RestfulSubTestTuple()
|
|
79
|
+
subTuple.subInt = x
|
|
80
|
+
tuple_.aListOfSubTuples.append(subTuple)
|
|
81
|
+
|
|
82
|
+
subTuple = RestfulSubTestTuple()
|
|
83
|
+
subTuple.subInt = x + 10
|
|
84
|
+
tuple_.aDictOfSubTuples[str(x)] = subTuple
|
|
85
|
+
|
|
86
|
+
return tuple_
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@ddt
|
|
90
|
+
class TupleRestfulTest(unittest.TestCase):
|
|
91
|
+
def setUp(self):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
def tearDown(self):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def _testSerialisation(self, origTuple):
|
|
98
|
+
# To Restful JSON Object
|
|
99
|
+
jsonDict = origTuple.tupleToRestfulJsonDict()
|
|
100
|
+
# Pass it through the JSON object to JSON string conversion
|
|
101
|
+
jsonDict = json.loads(json.dumps(jsonDict))
|
|
102
|
+
# Reconstruct a new Tuple
|
|
103
|
+
deserialisedTuple = (
|
|
104
|
+
RestfulTestTuple().restfulJsonDictToTupleWithValidation(
|
|
105
|
+
jsonDict, RestfulTestTuple
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
return deserialisedTuple
|
|
109
|
+
|
|
110
|
+
@data(-1, 110)
|
|
111
|
+
def testIntValidatorInvalid(self, int_):
|
|
112
|
+
# Create tuple
|
|
113
|
+
origTuple = _makeRestfultTestTuple()
|
|
114
|
+
origTuple.aIntWithValidator = int_
|
|
115
|
+
self.assertRaises(ValueError, self._testSerialisation, origTuple)
|
|
116
|
+
|
|
117
|
+
@data(1, 99)
|
|
118
|
+
def testIntValidatorValid(self, int_):
|
|
119
|
+
# Create tuple
|
|
120
|
+
origTuple = _makeRestfultTestTuple()
|
|
121
|
+
origTuple.aIntWithValidator = int_
|
|
122
|
+
self.assertEqual(
|
|
123
|
+
self._testSerialisation(origTuple).aIntWithValidator, int_
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def testValidatorPass(self):
|
|
127
|
+
# Create tuple
|
|
128
|
+
origTuple = _makeRestfultTestTuple()
|
|
129
|
+
|
|
130
|
+
deserialisedTuple = self._testSerialisation(origTuple)
|
|
131
|
+
|
|
132
|
+
self.assertEqual(TupleHash(origTuple), TupleHash(origTuple))
|
|
133
|
+
self.assertEqual(
|
|
134
|
+
TupleHash(deserialisedTuple), TupleHash(deserialisedTuple)
|
|
135
|
+
)
|
|
136
|
+
self.assertEqual(
|
|
137
|
+
TupleHash(origTuple),
|
|
138
|
+
TupleHash(deserialisedTuple),
|
|
139
|
+
"Tuple serialise -> deserialise error",
|
|
140
|
+
)
|
vortex/TupleSelector.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
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 json
|
|
10
|
+
from typing import Dict, Optional, Union, Type
|
|
11
|
+
|
|
12
|
+
from vortex.Tuple import Tuple, addTupleType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InvalidTupleSelectorNameException(Exception):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@addTupleType
|
|
20
|
+
class TupleSelector(Tuple):
|
|
21
|
+
__tupleType__ = "vortex.TupleSelector"
|
|
22
|
+
__slots__ = ["name", "selector"]
|
|
23
|
+
|
|
24
|
+
# name: str = TupleField(comment="The tuple name this selector is for")
|
|
25
|
+
# selector: Dict[str, Any] = TupleField(comment="The values to select")
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
name: Optional[Union[str, Type[Tuple], Tuple]] = None,
|
|
30
|
+
selector: Optional[Dict] = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
Tuple.__init__(self)
|
|
33
|
+
self.name = self.nameFromTuple(name)
|
|
34
|
+
self.selector = selector if selector else {}
|
|
35
|
+
|
|
36
|
+
def __eq__(self, y):
|
|
37
|
+
return self.toJsonStr() == y.toJsonStr()
|
|
38
|
+
|
|
39
|
+
def __hash__(self):
|
|
40
|
+
return hash(self.toJsonStr())
|
|
41
|
+
|
|
42
|
+
# noinspection PyMethodMayBeStatic
|
|
43
|
+
@classmethod
|
|
44
|
+
def nameFromTuple(
|
|
45
|
+
cls, nameOrTuple: Optional[Union[str, Type[Tuple], Tuple]]
|
|
46
|
+
) -> Optional[str]:
|
|
47
|
+
# When the TupleSelector is reconstructed / deserialised, it's constructed with
|
|
48
|
+
# no arguments
|
|
49
|
+
if nameOrTuple is None:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
if isinstance(nameOrTuple, str):
|
|
53
|
+
name = nameOrTuple
|
|
54
|
+
|
|
55
|
+
elif isinstance(nameOrTuple, Tuple):
|
|
56
|
+
name = nameOrTuple.tupleType()
|
|
57
|
+
|
|
58
|
+
elif issubclass(nameOrTuple, Tuple):
|
|
59
|
+
name = nameOrTuple.tupleType()
|
|
60
|
+
|
|
61
|
+
else:
|
|
62
|
+
raise Exception(
|
|
63
|
+
"Argument should be of type Union[str, Type[Tuple], Tuple]"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if name == TupleSelector.tupleName():
|
|
67
|
+
raise InvalidTupleSelectorNameException(
|
|
68
|
+
"We've found the name of TupleSelector.tupleType(),"
|
|
69
|
+
"which means we've been passed a TupleSelector."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return name
|
|
73
|
+
|
|
74
|
+
def isForTuple(
|
|
75
|
+
self, nameOrTuple: Optional[Union[str, Type[Tuple], Tuple]]
|
|
76
|
+
) -> bool:
|
|
77
|
+
return self.name == self.nameFromTuple(nameOrTuple)
|
|
78
|
+
|
|
79
|
+
def toJsonStr(self) -> str:
|
|
80
|
+
"""To Json Str
|
|
81
|
+
|
|
82
|
+
This method dumps the c{TupleSelector} data to a json string.
|
|
83
|
+
|
|
84
|
+
It sorts the dict keys and
|
|
85
|
+
"""
|
|
86
|
+
fieldJsonDict = self.toJsonField(self.selector)
|
|
87
|
+
return json.dumps(
|
|
88
|
+
{"name": self.name, "selector": fieldJsonDict}, sort_keys=True
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def fromJsonStr(cls, jsonStr: str) -> "TupleSelector":
|
|
93
|
+
"""From Json Str
|
|
94
|
+
|
|
95
|
+
This method creates a new c{TupleSelector} from the ordered json string dumped
|
|
96
|
+
from .toJsonStr
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
data = json.loads(jsonStr)
|
|
100
|
+
newTs = TupleSelector(name=data["name"], selector={})
|
|
101
|
+
newTs.selector = newTs.fromJsonField(data["selector"])
|
|
102
|
+
return newTs
|
|
103
|
+
|
|
104
|
+
def __repr__(self):
|
|
105
|
+
return self.toJsonStr()
|
vortex/TupleTest.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
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 datetime
|
|
11
|
+
import json
|
|
12
|
+
|
|
13
|
+
from twisted.trial import unittest
|
|
14
|
+
|
|
15
|
+
from vortex.TestTuple import TestSubTuple, TestTuple
|
|
16
|
+
from vortex.Tuple import TupleHash
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def makeTupleA():
|
|
20
|
+
tuple_ = TestTuple()
|
|
21
|
+
tuple_.aBoolTrue = True
|
|
22
|
+
tuple_.aBoolFalse = False
|
|
23
|
+
tuple_.aDate = datetime.datetime(2010, 4, 7, 2, 33, 19, 666)
|
|
24
|
+
tuple_.aFloat = 2.56
|
|
25
|
+
tuple_.aInt = 1231231
|
|
26
|
+
tuple_.aString = "test string from 345345345@$#%#$%#$%#$%#"
|
|
27
|
+
tuple_.aSet = ["x", 2, False] # Sets are restored as Lists
|
|
28
|
+
tuple_.aList = ["y", 3, True, 3.3]
|
|
29
|
+
tuple_.aDict = {
|
|
30
|
+
"a": "test str", # Key strings are loaded as unicode
|
|
31
|
+
1: 1.234,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
subTuple = TestSubTuple()
|
|
35
|
+
subTuple.subInt = 2048
|
|
36
|
+
tuple_.aSubTuple = subTuple
|
|
37
|
+
#
|
|
38
|
+
tuple_.aListOfSubTuples = []
|
|
39
|
+
for x in range(2):
|
|
40
|
+
subTuple = TestSubTuple()
|
|
41
|
+
subTuple.subInt = x
|
|
42
|
+
tuple_.aListOfSubTuples.append(subTuple)
|
|
43
|
+
|
|
44
|
+
return tuple_
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TuplePyTest(unittest.TestCase):
|
|
48
|
+
PERFORMANCE_ITERATIONS = 10000
|
|
49
|
+
|
|
50
|
+
def setUp(self):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
def tearDown(self):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def testTupleToFromJson(self):
|
|
57
|
+
# Create tuple
|
|
58
|
+
origTuple = makeTupleA()
|
|
59
|
+
|
|
60
|
+
# To Json
|
|
61
|
+
jsonDictStr = json.dumps(origTuple.toJsonDict())
|
|
62
|
+
deserialisedTuple = TestTuple().fromJsonDict(json.loads(jsonDictStr))
|
|
63
|
+
# print xmlStr
|
|
64
|
+
|
|
65
|
+
self.assertEqual(TupleHash(origTuple), TupleHash(origTuple))
|
|
66
|
+
self.assertEqual(
|
|
67
|
+
TupleHash(deserialisedTuple), TupleHash(deserialisedTuple)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# print TupleHash(origTuple)._key()
|
|
71
|
+
# print TupleHash(deserialisedTuple)._key()
|
|
72
|
+
|
|
73
|
+
self.assertEqual(
|
|
74
|
+
TupleHash(origTuple),
|
|
75
|
+
TupleHash(deserialisedTuple),
|
|
76
|
+
"Tuple serialise -> deserialise error",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def testTupleToFromJsonPerformanceMulti(self):
|
|
80
|
+
for _ in range(self.PERFORMANCE_ITERATIONS):
|
|
81
|
+
origTuple = makeTupleA()
|
|
82
|
+
jsonDictStr = json.dumps(origTuple.toJsonDict())
|
|
83
|
+
TestTuple().fromJsonDict(json.loads(jsonDictStr))
|
|
84
|
+
|
|
85
|
+
def testTupleToFromJsonPerformanceSingle(self):
|
|
86
|
+
origTuple = makeTupleA()
|
|
87
|
+
origTuple.aListOfSubTuples = [
|
|
88
|
+
makeTupleA() for _ in range(self.PERFORMANCE_ITERATIONS)
|
|
89
|
+
]
|
|
90
|
+
jsonDictStr = json.dumps(origTuple.toJsonDict())
|
|
91
|
+
TestTuple().fromJsonDict(json.loads(jsonDictStr))
|
|
92
|
+
|
|
93
|
+
def testTupleHash(self):
|
|
94
|
+
hashA1 = TupleHash(makeTupleA())
|
|
95
|
+
hashA2 = TupleHash(makeTupleA())
|
|
96
|
+
print("hashA1 = %s" % hashA1.__hash__())
|
|
97
|
+
print("hashA2 = %s" % hashA2.__hash__())
|
|
98
|
+
|
|
99
|
+
hashB1 = TupleHash(makeTupleA())
|
|
100
|
+
hashB1.tupl.aString = "Not the same as the others"
|
|
101
|
+
print("hashB1 = %s" % hashB1.__hash__())
|
|
102
|
+
|
|
103
|
+
hashA1.__hash__()
|
|
104
|
+
self.assertTrue(
|
|
105
|
+
hashA1.__hash__() == hashA1.__hash__(),
|
|
106
|
+
"TupleHash A1.__hash__() == A1.__hash__()",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
self.assertTrue(hashA1 == hashA2, "TupleHash __eq__ fail, A1 == A2")
|
|
110
|
+
self.assertFalse(hashA1 != hashA2, "TupleHash __ne__ fail, A1 != A2")
|
|
111
|
+
self.assertFalse(hashA1 == hashB1, "TupleHash __eq__ fail, A1 == B1")
|
|
112
|
+
self.assertTrue(hashA1 != hashB1, "TupleHash __ne__ fail, A1 != B1")
|
|
113
|
+
|
|
114
|
+
self.assertTrue(
|
|
115
|
+
hashA1 in {hashA2: None}, "TupleHash in fail, A1 in {A2:None}"
|
|
116
|
+
)
|
|
117
|
+
self.assertFalse(
|
|
118
|
+
hashA1 not in {hashA2: None},
|
|
119
|
+
"TupleHash in fail, A1 not in {A2:None}",
|
|
120
|
+
)
|
|
121
|
+
self.assertTrue(
|
|
122
|
+
hashA1 not in {hashB1: None},
|
|
123
|
+
"TupleHash in fail, A1 not in {B1:None}",
|
|
124
|
+
)
|
|
125
|
+
self.assertFalse(
|
|
126
|
+
hashA1 in {hashB1: None}, "TupleHash in fail, A1 in {B1:None}"
|
|
127
|
+
)
|
vortex/VortexABC.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from abc import ABCMeta, abstractmethod
|
|
2
|
+
from typing import Optional, Union, Callable, List
|
|
3
|
+
|
|
4
|
+
from twisted.internet.defer import Deferred
|
|
5
|
+
|
|
6
|
+
from vortex.PayloadEnvelope import VortexMsgList
|
|
7
|
+
|
|
8
|
+
SendVortexMsgResponseCallable = Callable[
|
|
9
|
+
[Union[VortexMsgList, bytes], int], Deferred
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VortexInfo:
|
|
14
|
+
"""Vortex Info
|
|
15
|
+
|
|
16
|
+
This class stores details about the end of a vortex.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, name, uuid):
|
|
20
|
+
self.name, self.uuid = name, uuid
|
|
21
|
+
|
|
22
|
+
def __repr__(self):
|
|
23
|
+
return "VortexInfo, Name=%s, UUID=%s" % (self.name, self.uuid)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class VortexABC(metaclass=ABCMeta):
|
|
27
|
+
@property
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def localVortexInfo(self) -> VortexInfo:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def remoteVortexInfo(self) -> List[VortexInfo]:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def requiresBase64Encoding(self) -> bool:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def sendVortexMsg(
|
|
44
|
+
self,
|
|
45
|
+
vortexMsgs: Union[VortexMsgList, bytes],
|
|
46
|
+
vortexUuid: Optional[str] = None,
|
|
47
|
+
):
|
|
48
|
+
"""Send Vortex Msg
|
|
49
|
+
|
|
50
|
+
Sends the vortex message to any conencted clients with vortexUuid.
|
|
51
|
+
Or broadcast it to all connected vortex clients if it's None
|
|
52
|
+
|
|
53
|
+
:param vortexMsgs: The vortex message(s) to send
|
|
54
|
+
:param vortexUuid: The vortexUuid of the client to send to.
|
|
55
|
+
"""
|