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,90 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : https://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+ import abc
10
+ from abc import abstractmethod
11
+ from typing import Union, Optional
12
+
13
+ from twisted.internet.defer import Deferred
14
+
15
+ from vortex.TupleSelector import TupleSelector
16
+ from vortex.data_loader.TupleDataLoaderTupleABC import TupleDataLoaderTupleABC
17
+
18
+
19
+ class TupleDataLoaderDelegateABC(abc.ABC):
20
+ """Tuple Data Loader Delegate ABC
21
+
22
+ ABC for delegate classes to handle the reading and writing for each forms'
23
+ data. Handlers must be register in
24
+ (`Tuple`, `TupleDataLoaderDelegateABC`) pairs with the `TupleDataLoader`
25
+
26
+ """
27
+
28
+ def start(self):
29
+ pass
30
+
31
+ def shutdown(self):
32
+ pass
33
+
34
+ @abstractmethod
35
+ def createData(
36
+ self, data: TupleDataLoaderTupleABC
37
+ ) -> Union[Deferred, TupleSelector]:
38
+ """Load Data
39
+
40
+ The `createData` method is called when data does not exist and needs
41
+ to be created.
42
+
43
+ :param data: The new object to create
44
+ :param selector: The current tuple selector for the data.
45
+ :return: The `TupleSelector` or a `Deferred` which eventually
46
+ results in a `TupleSelector`. The tuple selector must select the new
47
+ data.
48
+ """
49
+ pass
50
+
51
+ @abstractmethod
52
+ def loadData(
53
+ self, selector: TupleSelector
54
+ ) -> Union[Deferred, TupleDataLoaderTupleABC]:
55
+ """Load Data
56
+
57
+ The `loadData` method is called when data needs to be loaded from a
58
+ storage medium. The query is represented by a `TupleSelector` value
59
+ `selector`.
60
+
61
+ :param selector: The `TupleSelector` for which to load data
62
+ :return: The `Tuple` or a `Deferred` which eventually results in a `Tuple`
63
+ """
64
+ pass
65
+
66
+ @abstractmethod
67
+ def storeData(
68
+ self, data: TupleDataLoaderTupleABC, tupleSelector: TupleSelector
69
+ ) -> Optional[TupleSelector]:
70
+ """Store Data
71
+
72
+ Store the data into the storage medium. The `data` is the `Tuple` which
73
+ needs to be written
74
+
75
+ :param data: Tuple to write and store
76
+ :param selector: Previous selector used to get the data
77
+ :return: TupleSelector: An optional new tuple selector if it has changed
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ def deleteData(self, selector: TupleSelector) -> Deferred:
83
+ """Delete Data
84
+
85
+ Delete the data referenced by the `selector`
86
+
87
+ :param selector: `TupleSelector` for data to be deleted
88
+ :return: Deferred
89
+ """
90
+ pass
@@ -0,0 +1,10 @@
1
+ import logging
2
+ from abc import ABCMeta
3
+
4
+ from vortex.Tuple import Tuple
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class TupleDataLoaderTupleABC(Tuple, metaclass=ABCMeta):
10
+ pass
@@ -0,0 +1,53 @@
1
+ from typing import Optional
2
+
3
+ from vortex.Tuple import addTupleType, TupleField, Tuple
4
+ from vortex.TupleAction import TupleActionABC
5
+ from vortex.TupleSelector import TupleSelector
6
+ from vortex.data_loader.TupleDataLoaderTupleABC import TupleDataLoaderTupleABC
7
+
8
+
9
+ @addTupleType
10
+ class _DataLoaderTupleAction(TupleActionABC):
11
+ __tupleType__ = "vortex._DataLoaderTupleAction"
12
+
13
+ CREATE_ACTION = "create"
14
+ STORE_ACTION = "store"
15
+ LOAD_ACTION = "load"
16
+ DELETE_ACTION = "delete"
17
+
18
+ userUuid: str = TupleField()
19
+ delegateUuid: str = TupleField()
20
+ dataTuple: TupleDataLoaderTupleABC = TupleField()
21
+ action: str = TupleField()
22
+ tupleDataSelector: TupleSelector = TupleField()
23
+
24
+
25
+ @addTupleType
26
+ class _DataLoaderTupleActionResponseTuple(Tuple):
27
+ __tupleType__ = "vortex._DataLoaderTupleActionResponseTuple"
28
+
29
+ tupleData: Optional[TupleDataLoaderTupleABC] = TupleField()
30
+ tupleDataSelector: Optional[TupleSelector] = TupleField()
31
+
32
+
33
+ @addTupleType
34
+ class _LockDataTupleAction(TupleActionABC):
35
+ __tupleType__ = "vortex._LockDataTupleAction"
36
+
37
+ lock: bool = TupleField()
38
+ userUuid: str = TupleField()
39
+ delegateUuid: str = TupleField()
40
+ tupleDataSelector: TupleSelector = TupleField()
41
+ liveUpdateDataTuple: TupleDataLoaderTupleABC = TupleField()
42
+
43
+
44
+ @addTupleType
45
+ class _DataLockStatusTuple(Tuple):
46
+ __tupleType__ = "vortex._DataLockStatusTuple"
47
+
48
+ locked: bool = TupleField(False)
49
+ deleted: bool = TupleField(False)
50
+ lockedByUserUuid: Optional[str] = TupleField()
51
+ lockAutoExpireDate: Optional[str] = TupleField()
52
+ liveUpdateDataFromDelegateUuid: Optional[str] = TupleField()
53
+ liveUpdateDataTuple: Optional[TupleDataLoaderTupleABC] = TupleField()
File without changes
@@ -0,0 +1,15 @@
1
+ import json
2
+ from abc import ABCMeta
3
+
4
+
5
+ class TupleChangeEventABC(metaclass=ABCMeta):
6
+ __eventType__ = None
7
+
8
+ @classmethod
9
+ def eventType(cls) -> str:
10
+ return cls.__eventType__
11
+
12
+ def __repr__(self):
13
+ return json.dumps(
14
+ {"class": self.__class__.__name__, "fields": self.__dict__}
15
+ )
@@ -0,0 +1,192 @@
1
+ import logging
2
+ from _weakrefset import WeakSet
3
+ from collections import defaultdict
4
+ from datetime import datetime
5
+ from typing import Optional, Type, Union
6
+
7
+ import pytz
8
+ from twisted.internet import reactor
9
+ from twisted.internet.defer import inlineCallbacks
10
+
11
+ from vortex.TupleSelector import TupleSelector
12
+ from vortex.event_bus.TupleChangeEventABC import TupleChangeEventABC
13
+ from vortex.event_bus.TupleChangeEventBusObserverABC import (
14
+ TupleChangeEventBusObserverABC,
15
+ )
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class TupleChangeEventBus:
21
+ def __init__(self):
22
+ self._weakObserversForAllTupleChangeEventABCNames: WeakSet[
23
+ TupleChangeEventBusObserverABC
24
+ ] = WeakSet([])
25
+
26
+ self._weakObserversBySelectorName: dict[
27
+ str, WeakSet[TupleChangeEventBusObserverABC]
28
+ ] = defaultdict(lambda: WeakSet([]))
29
+
30
+ def notifyMany(self, events: list[TupleChangeEventABC]) -> None:
31
+ for event in events:
32
+ self.notify(event)
33
+
34
+ def notify(self, event: TupleChangeEventABC) -> None:
35
+ """
36
+ Notify
37
+
38
+ Notify the bus of a change to a tuple.
39
+
40
+ This bus does not automatically call
41
+ TupleDataObservableHandler.notifyOfTupleUpdate(...)
42
+ that must be done by classes implementing
43
+ TupleChangeEventBusObserverABC
44
+
45
+ IMPORTANT: You can use this bus for whatever you like,
46
+ however, see best practices.
47
+
48
+ BEST PRACTICE: Only notify the bus of precisely
49
+ the data that has changed, as close to the code that changes it as
50
+ possible.
51
+
52
+ Do not notify the bus of tuple selectors that need updating.
53
+
54
+ Classes implementing TupleChangeEventBusObserverABC
55
+ should observe changed data and themselves
56
+ decide what tuple selectors they will
57
+ notify their observers for.
58
+
59
+ For example. If you have two tuple selectors:
60
+
61
+ 1) TupleChangeEventABC(MyTuple, {id:1})
62
+ 2) TupleChangeEventABC(ListOfMyTuples)
63
+
64
+ TupleChangeEventBus.notify should be called for MyTuple
65
+ just after changes are committed.
66
+
67
+ TupleChangeEventBus.notify SHOULD NOT BE called for
68
+ ListOfMyTuples
69
+
70
+ A typical observer's method might look like this:
71
+
72
+ ```python
73
+ def notifyFromBus(self, event: TupleChangeEventABC) -> None:
74
+ if event.isForTuple(MyTuple):
75
+ self.tupleDataObservable.notifyOfTupleUpdate(event)
76
+ self.tupleDataObservable.notifyOfTupleUpdate(
77
+ TupleChangeEventABC(ListOfMyTuples)
78
+ )
79
+ return
80
+
81
+ ...
82
+
83
+ ```
84
+
85
+ :param event: The tuple selector that describes the tuple
86
+ that has been changed. This change might be created, updated, or
87
+ deleted.
88
+ :return:
89
+ """
90
+ weakSets = [self._weakObserversForAllTupleChangeEventABCNames]
91
+
92
+ eventName = event.eventType()
93
+
94
+ if eventName in self._weakObserversBySelectorName:
95
+ weakSets.append(list(self._weakObserversBySelectorName[eventName]))
96
+
97
+ for weakSet in weakSets:
98
+ for item in weakSet:
99
+ if not item:
100
+ continue
101
+
102
+ # noinspection PyUnresolvedReferences
103
+ reactor.callLater(0, self._notify, item, event)
104
+
105
+ @inlineCallbacks
106
+ def _notify(
107
+ self,
108
+ observable: TupleChangeEventBusObserverABC,
109
+ event: TupleChangeEventABC,
110
+ ):
111
+ try:
112
+ startDate = datetime.now(pytz.utc)
113
+ yield observable.notifyFromBus(event)
114
+
115
+ # A blocking call taking more than 100ms is BAD
116
+ # Otherwise a call taking more than a 1s is just poor performance.
117
+ secondsTaken = (datetime.now(pytz.utc) - startDate).total_seconds()
118
+ if 0.1 < secondsTaken:
119
+ logger.debug(
120
+ "notifyFromBus took %s for %s, event %s",
121
+ secondsTaken,
122
+ observable.__class__.__name__,
123
+ event,
124
+ )
125
+
126
+ except Exception as e:
127
+ logger.error(
128
+ "Observable %s raised an error"
129
+ " when being called with event %s",
130
+ observable.__class__.__name__,
131
+ event,
132
+ )
133
+ logger.exception(e)
134
+
135
+ def addObserver(
136
+ self,
137
+ observer: TupleChangeEventBusObserverABC,
138
+ eventNameOrClass: Optional[
139
+ Union[
140
+ str,
141
+ Type[TupleChangeEventABC],
142
+ TupleChangeEventABC,
143
+ ]
144
+ ] = None,
145
+ ) -> None:
146
+ assert isinstance(
147
+ observer, TupleChangeEventBusObserverABC
148
+ ), "observer must be a TupleChangeEventBusObserverABC"
149
+
150
+ if eventNameOrClass is None:
151
+ self._weakObserversForAllTupleChangeEventABCNames.add(observer)
152
+ return
153
+
154
+ eventName = self._nameFromEvent(eventNameOrClass)
155
+
156
+ self._weakObserversBySelectorName[eventName].add(observer)
157
+
158
+ # noinspection PyMethodMayBeStatic
159
+ def _nameFromEvent(
160
+ self,
161
+ nameOrTuple: Optional[
162
+ Union[str, Type[TupleChangeEventABC], TupleChangeEventABC]
163
+ ],
164
+ ) -> Optional[str]:
165
+ # When the TupleSelector is reconstructed / deserialize,
166
+ # it's constructed with
167
+ # no arguments
168
+ if nameOrTuple is None:
169
+ return None
170
+
171
+ if isinstance(nameOrTuple, str):
172
+ name = nameOrTuple
173
+
174
+ elif isinstance(nameOrTuple, TupleChangeEventABC):
175
+ name = nameOrTuple.eventType()
176
+
177
+ elif issubclass(nameOrTuple, TupleChangeEventABC):
178
+ name = nameOrTuple.eventType()
179
+
180
+ else:
181
+ raise Exception(
182
+ "Argument should be of type "
183
+ "Union[str, Type[TupleChangeEventABC], TupleChangeEventABC]"
184
+ )
185
+
186
+ if name == TupleSelector.tupleName():
187
+ raise Exception(
188
+ "We've found the name of TupleSelector.tupleType(),"
189
+ "which means we've been passed a TupleSelector."
190
+ )
191
+
192
+ return name
@@ -0,0 +1,11 @@
1
+ from abc import ABCMeta, abstractmethod
2
+
3
+ from vortex.event_bus.TupleChangeEventABC import TupleChangeEventABC
4
+
5
+
6
+ class TupleChangeEventBusObserverABC(metaclass=ABCMeta):
7
+ @abstractmethod
8
+ def notifyFromBus(self, event: TupleChangeEventABC) -> None:
9
+ raise NotImplementedError(
10
+ "TupleChangeEventBusObserverABC.notifyFromBus"
11
+ )
File without changes
@@ -0,0 +1,270 @@
1
+ import logging
2
+ from abc import ABCMeta
3
+ from abc import abstractmethod
4
+ from inspect import signature
5
+ from typing import Dict
6
+ from typing import Optional
7
+ from typing import Union
8
+
9
+ from twisted.internet.defer import Deferred
10
+ from twisted.internet.defer import fail
11
+ from twisted.internet.defer import inlineCallbacks
12
+ from twisted.internet.defer import succeed
13
+ from twisted.python import failure
14
+ from twisted.python.failure import Failure
15
+
16
+ from vortex.DeferUtil import vortexLogFailure
17
+ from vortex.Payload import Payload
18
+ from vortex.PayloadEndpoint import PayloadEndpoint
19
+ from vortex.PayloadEnvelope import PayloadEnvelope
20
+ from vortex.TupleAction import TupleActionABC
21
+ from vortex.TupleActionVortex import TupleActionVortex
22
+ from vortex.VortexABC import SendVortexMsgResponseCallable
23
+ from vortex.VortexFactory import VortexFactory
24
+ from vortex.VortexUtil import limitConcurrency
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class TupleActionProcessorDelegateABC(metaclass=ABCMeta):
30
+ @abstractmethod
31
+ def processTupleAction(
32
+ self, tupleAction: TupleActionABC, **kwargs
33
+ ) -> Deferred:
34
+ """Process Tuple Action
35
+
36
+ The method generates the vortexMsg for the vortex to send.
37
+
38
+ :param tupleAction: The C{TupleAction} to process.
39
+
40
+ """
41
+
42
+
43
+ class TupleActionProcessor:
44
+ def __init__(
45
+ self,
46
+ tupleActionProcessorName: str,
47
+ additionalFilt: Optional[Dict] = None,
48
+ defaultDelegate: Optional[TupleActionProcessorDelegateABC] = None,
49
+ acceptOnlyFromVortex: Optional[Union[str, tuple]] = None,
50
+ usedForProxy__=False,
51
+ ) -> None:
52
+ """Constructor
53
+
54
+ :param tupleActionProcessorName: The name of this observable
55
+
56
+ :param additionalFilt: Any additional filter keys that are required
57
+
58
+ :param defaultDelegate: The default delegate to send all actions to
59
+
60
+ :param acceptOnlyFromVortex: Accept requests only from this vortex,
61
+ The vortex can be str or tuple of str, or None to accept from any.
62
+ """
63
+
64
+ self._tupleActionProcessorName = tupleActionProcessorName
65
+ self._defaultDelegate = defaultDelegate
66
+
67
+ self._tupleProcessorsByTupleName: Dict[
68
+ str, TupleActionProcessorDelegateABC
69
+ ] = {}
70
+
71
+ if not usedForProxy__:
72
+ self._filt = dict(
73
+ name=tupleActionProcessorName, key="tupleActionProcessorName"
74
+ )
75
+
76
+ if additionalFilt:
77
+ self._filt.update(additionalFilt)
78
+
79
+ self._endpoint = PayloadEndpoint(
80
+ self._filt,
81
+ self._processLimited,
82
+ acceptOnlyFromVortex=acceptOnlyFromVortex,
83
+ )
84
+
85
+ def setDelegate(
86
+ self, tupleName: str, processor: TupleActionProcessorDelegateABC
87
+ ):
88
+ """Add Tuple Action Processor Delegate
89
+
90
+ :param tupleName: The tuple name to process actions for.
91
+ :param processor: The processor to use for processing this tuple name.
92
+
93
+ """
94
+ assert (
95
+ not tupleName in self._tupleProcessorsByTupleName
96
+ ), "TupleActionProcessor:%s, Tuple name %s is already registered" % (
97
+ self._tupleActionProcessorName,
98
+ tupleName,
99
+ )
100
+
101
+ assert isinstance(processor, TupleActionProcessorDelegateABC), (
102
+ "TupleActionProcessor:%s, provider must be an"
103
+ " instance of TupleActionProcessorDelegateABC"
104
+ % self._tupleActionProcessorName
105
+ )
106
+
107
+ self._tupleProcessorsByTupleName[tupleName] = processor
108
+
109
+ @property
110
+ def delegateCount(self) -> int:
111
+ return len(self._tupleProcessorsByTupleName)
112
+
113
+ def hasDelegate(self, tupleName: str) -> bool:
114
+ return tupleName in self._tupleProcessorsByTupleName
115
+
116
+ def shutdown(self):
117
+ self._endpoint.shutdown()
118
+
119
+ def _processLimited(self, *args, **kwargs):
120
+ d = self._processLimitedDecorated(*args, **kwargs)
121
+ if d:
122
+ d.addErrback(vortexLogFailure, logger, consumeError=True)
123
+
124
+ @limitConcurrency(logger, 25)
125
+ def _processLimitedDecorated(self, *args, **kwargs):
126
+ return self._process(*args, **kwargs)
127
+
128
+ @inlineCallbacks
129
+ def _process(
130
+ self,
131
+ payloadEnvelope: PayloadEnvelope,
132
+ vortexUuid: str,
133
+ sendResponse: SendVortexMsgResponseCallable,
134
+ **kwargs,
135
+ ):
136
+ """Process the Payload / Tuple Action"""
137
+ if not VortexFactory.isVortexUuidOnline(vortexUuid):
138
+ logger.debug(
139
+ "Vortex %s is no longer online, skipping reply", vortexUuid
140
+ )
141
+ return
142
+
143
+ # logger.debug(
144
+ # "Vortex %s is STILL online, continuing to reply", vortexUuid
145
+ # )
146
+
147
+ payload = yield payloadEnvelope.decodePayloadDefer()
148
+
149
+ assert (
150
+ len(payload.tuples) == 1
151
+ ), "TupleActionProcessor:%s Expected 1 tuples, received %s" % (
152
+ self._tupleActionProcessorName,
153
+ len(payload.tuples),
154
+ )
155
+
156
+ tupleAction = payload.tuples[0]
157
+
158
+ yield self._processTupleAction(
159
+ payloadEnvelope.filt,
160
+ sendResponse,
161
+ tupleAction,
162
+ vortexUuid=vortexUuid,
163
+ **kwargs,
164
+ )
165
+
166
+ def _processTupleAction(
167
+ self, payloadEnvelopeFilt, sendResponse, tupleAction, **kwargs
168
+ ):
169
+
170
+ assert isinstance(
171
+ tupleAction, TupleActionABC
172
+ ), "TupleActionProcessor:%s Expected TupleAction, received %s" % (
173
+ self._tupleActionProcessorName,
174
+ tupleAction.__class__,
175
+ )
176
+
177
+ tupleName = tupleAction.tupleName()
178
+ processor = self._tupleProcessorsByTupleName.get(tupleName)
179
+ if processor:
180
+ delegate = processor.processTupleAction
181
+
182
+ elif self._defaultDelegate:
183
+ delegate = self._defaultDelegate.processTupleAction
184
+
185
+ else:
186
+ raise Exception("No delegate registered for %s" % tupleName)
187
+
188
+ sig = signature(delegate)
189
+ if len(sig.parameters) == 2:
190
+ tupleActionVortex = TupleActionVortex(**kwargs)
191
+ d = self._customMaybeDeferred(
192
+ delegate, tupleAction, tupleActionVortex
193
+ )
194
+ else:
195
+ d = self._customMaybeDeferred(delegate, tupleAction)
196
+
197
+ d.addCallback(
198
+ self._callback, payloadEnvelopeFilt, tupleName, sendResponse
199
+ )
200
+ d.addErrback(
201
+ self._errback, payloadEnvelopeFilt, tupleName, sendResponse
202
+ )
203
+ return d
204
+
205
+ @inlineCallbacks
206
+ def _callback(
207
+ self,
208
+ result,
209
+ replyFilt: dict,
210
+ tupleName: str,
211
+ sendResponse: SendVortexMsgResponseCallable,
212
+ ):
213
+
214
+ if not isinstance(result, list):
215
+ result = [result]
216
+ payload = Payload(filt=replyFilt, tuples=result)
217
+
218
+ payloadEnvelope = yield payload.makePayloadEnvelopeDefer()
219
+ vortexMsg = yield payloadEnvelope.toVortexMsgDefer()
220
+ try:
221
+ yield sendResponse(vortexMsg)
222
+
223
+ except Exception as e:
224
+ logger.error(
225
+ "Failed to send TupleAction response for %s", tupleName
226
+ )
227
+ logger.exception(e)
228
+
229
+ @inlineCallbacks
230
+ def _errback(
231
+ self,
232
+ result: Failure,
233
+ replyFilt: dict,
234
+ tupleName: str,
235
+ sendResponse: SendVortexMsgResponseCallable,
236
+ ):
237
+
238
+ logger.error(
239
+ "TupleActionProcessor:%s Failed to process TupleActon",
240
+ self._tupleActionProcessorName,
241
+ )
242
+
243
+ vortexLogFailure(result, logger)
244
+ failureMessage = result.getErrorMessage()
245
+
246
+ payloadEnvelope = PayloadEnvelope(filt=replyFilt, result=failureMessage)
247
+ vortexMsg = yield payloadEnvelope.toVortexMsgDefer()
248
+ try:
249
+ yield sendResponse(vortexMsg)
250
+
251
+ except Exception as e:
252
+ logger.error(
253
+ "Failed to send TupleAction response for %s\n%s",
254
+ tupleName,
255
+ failureMessage,
256
+ )
257
+ logger.exception(e)
258
+
259
+ def _customMaybeDeferred(self, f, *args, **kw):
260
+ try:
261
+ result = f(*args, **kw)
262
+ except Exception as e:
263
+ return fail(failure.Failure(e))
264
+
265
+ if isinstance(result, Deferred):
266
+ return result
267
+ elif isinstance(result, failure.Failure):
268
+ return fail(result)
269
+ else:
270
+ return succeed(result)