vortexpy 4.0.28__tar.gz → 4.1.1__tar.gz
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.
- {vortexpy-4.0.28/vortexpy.egg-info → vortexpy-4.1.1}/PKG-INFO +2 -24
- {vortexpy-4.0.28 → vortexpy-4.1.1}/pyproject.toml +2 -2
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadEndpoint.py +1 -1
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadEndpointTest.py +60 -49
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/Tuple.py +17 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexFactory.py +8 -2
- vortexpy-4.1.1/vortex/__init__.py +9 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/event_bus/TupleChangeEventBus.py +23 -0
- vortexpy-4.1.1/vortex/tuple_json_schema/__init__.py +18 -0
- vortexpy-4.1.1/vortex/tuple_json_schema/constants.py +35 -0
- vortexpy-4.1.1/vortex/tuple_json_schema/json_schema_node_tuple.py +99 -0
- vortexpy-4.1.1/vortex/tuple_json_schema/tuple_json_schema_builder.py +225 -0
- vortexpy-4.1.1/vortex/tuple_json_schema/tuple_json_schema_test.py +257 -0
- vortexpy-4.1.1/vortex/tuple_json_schema/tuple_json_schema_test_tuples.py +69 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1/vortexpy.egg-info}/PKG-INFO +2 -24
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortexpy.egg-info/SOURCES.txt +6 -1
- vortexpy-4.1.1/vortexpy.egg-info/dependency_links.txt +1 -0
- vortexpy-4.0.28/LICENSE +0 -22
- vortexpy-4.0.28/vortex/__init__.py +0 -9
- vortexpy-4.0.28/vortex/py.typed +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/README.md +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/setup.cfg +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/DataWrapTuple.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/DeferUtil.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/Jsonable.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/Payload.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadEnvelope.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadFilterKeys.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadIO.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadPriority.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadResponse.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadResponseTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PayloadTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/PerformTestActionTuple.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/SerialiseUtil.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TestTuple.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TupleAction.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TupleDataForTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TuplePerfTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TupleRestful.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TupleRestfulTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TupleSelector.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/TupleTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexAccessControlContextABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexAuthenticatorABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexClientHttp.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexClientHttpTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexClientTcp.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexClientWebsocketFactory.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexConnectionABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexEndpointPermission.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexPayloadProtocol.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexPermission.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexPermissionError.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexServer.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexServerConnection.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexServerHttpResource.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexServerTcp.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexServerWebsocket.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexSession.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexTcpMemoryLeakPayloadTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexTcpMemoryLeakRawTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexUtil.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/VortexWritePushProducer.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/data_loader/TupleDataLiveLockManager.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/data_loader/TupleDataLoader.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/data_loader/TupleDataLoaderDelegate.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/data_loader/TupleDataLoaderTuples.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/data_loader/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/event_bus/TupleChangeEventABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/event_bus/TupleChangeEventBusObserverABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/event_bus/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleActionProcessor.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleActionProcessorProxy.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleActionProcessorTestHandler.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataAccessGranterABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataActionClient.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataObservableCache.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataObservableHandler.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataObservableProxyHandler.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataObservableTestHandler.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataObserverClient.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/TupleDataProviderABC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/VortexJSTupleLoaderTestHandler.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/handler/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/mypy_plugin.py +0 -0
- /vortexpy-4.0.28/vortexpy.egg-info/dependency_links.txt → /vortexpy-4.1.1/vortex/py.typed +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/GzippedDataHttpClient.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/GzippedDataHttpClientTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/GzippedPayloadHttpClient.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/RestfulHttpClient.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/RestfulHttpClientTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/RestfulResource.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/RestfulResourceTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/restful/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/rpc/RPC.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/rpc/RPCTest.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/rpc/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/sqla_orm/OrmCrudHandler.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/sqla_orm/TupleProviderForDb.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/sqla_orm/VortexTupleJson.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/sqla_orm/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/storage/TupleStorageSqlite.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortex/storage/__init__.py +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortexpy.egg-info/entry_points.txt +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortexpy.egg-info/requires.txt +0 -0
- {vortexpy-4.0.28 → vortexpy-4.1.1}/vortexpy.egg-info/top_level.txt +0 -0
|
@@ -1,31 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vortexpy
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Flying Ion's observable, routable, data serialisation and transport code.
|
|
5
5
|
Author-email: Flying Ion Pty Ltd <contact@flyingion.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2016-2026 Flying Ion Pty Ltd
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
28
|
-
|
|
6
|
+
License: MIT
|
|
29
7
|
Project-URL: Homepage, https://flyingion.com
|
|
30
8
|
Project-URL: Repository, https://gitlab.com/flyingion/vortexpy
|
|
31
9
|
Project-URL: Issues, https://gitlab.com/flyingion/vortexpy/-/issues
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vortexpy"
|
|
7
|
-
version = "4.
|
|
8
|
-
license = {
|
|
7
|
+
version = "4.1.1"
|
|
8
|
+
license = {text = "MIT"}
|
|
9
9
|
authors = [{name = "Flying Ion Pty Ltd", email = "contact@flyingion.com"}]
|
|
10
10
|
description = "Flying Ion's observable, routable, data serialisation and transport code."
|
|
11
11
|
readme = "README.md"
|
|
@@ -274,7 +274,7 @@ class PayloadEndpoint:
|
|
|
274
274
|
)
|
|
275
275
|
if self._callableAcceptsAccessControlContext:
|
|
276
276
|
kwargs["accessControlContext"] = accessControlContext
|
|
277
|
-
return callable_
|
|
277
|
+
return (yield maybeDeferred(callable_, **kwargs))
|
|
278
278
|
else:
|
|
279
279
|
PayloadIO().remove(self)
|
|
280
280
|
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
# Website : https://flyingion.com
|
|
7
7
|
# Support : contact@flyingion.com
|
|
8
8
|
|
|
9
|
+
from twisted.internet import reactor
|
|
10
|
+
from twisted.internet.defer import Deferred
|
|
11
|
+
from twisted.internet.defer import inlineCallbacks
|
|
9
12
|
from twisted.trial import unittest
|
|
10
13
|
|
|
11
14
|
from vortex.Payload import Payload
|
|
@@ -19,7 +22,7 @@ class PayloadEndpointPyTestAssignPayload:
|
|
|
19
22
|
self._ut = unitTest
|
|
20
23
|
|
|
21
24
|
def process(self, payloadEnvelope: PayloadEnvelope, **kwargs):
|
|
22
|
-
self._ut.
|
|
25
|
+
self._ut.deliveredPayloadEnvelope = payloadEnvelope
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
class PayloadEndpointPyTest(unittest.TestCase):
|
|
@@ -28,46 +31,66 @@ class PayloadEndpointPyTest(unittest.TestCase):
|
|
|
28
31
|
|
|
29
32
|
self.deliveredPayloadEnvelope = None
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
@inlineCallbacks
|
|
35
|
+
def _flushReactor(self):
|
|
36
|
+
# PayloadIO schedules via callLater(0); PayloadEndpoint.process yields
|
|
37
|
+
# maybeDeferred — allow a few reactor turns to finish delivery.
|
|
38
|
+
for _ in range(3):
|
|
39
|
+
d = Deferred()
|
|
40
|
+
reactor.callLater(0, d.callback, None)
|
|
41
|
+
yield d
|
|
42
|
+
|
|
43
|
+
def _newPayload(self, plFilt: dict) -> Payload:
|
|
32
44
|
payload = Payload()
|
|
33
45
|
payload.filt.update(plFilt)
|
|
34
46
|
for x in range(6):
|
|
35
47
|
payload.filt["%s" % x] = x
|
|
48
|
+
return payload
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
self._keepFuncInScope = processPayload
|
|
41
|
-
|
|
42
|
-
PayloadEndpoint(epFilt, processPayload, requiredPermissions=frozenset())
|
|
43
|
-
|
|
50
|
+
def _processEnvelope(self, payload: Payload) -> None:
|
|
51
|
+
# Build envelope without Payload.makePayloadEnvelope() — that path
|
|
52
|
+
# forbids the reactor thread (blocking encode).
|
|
44
53
|
PayloadIO().process(
|
|
45
|
-
payloadEnvelope=payload.
|
|
54
|
+
payloadEnvelope=PayloadEnvelope(filt=payload.filt),
|
|
46
55
|
vortexUuid="test",
|
|
47
56
|
vortexName="test",
|
|
48
57
|
vortexSession=None,
|
|
49
58
|
sendResponse=lambda x: x,
|
|
50
59
|
)
|
|
51
60
|
|
|
61
|
+
def _registerAndProcess(self, plFilt, epFilt) -> Payload:
|
|
62
|
+
payload = self._newPayload(plFilt)
|
|
63
|
+
|
|
64
|
+
def processPayload(payloadEnvelope: PayloadEnvelope, **kwargs):
|
|
65
|
+
self.deliveredPayloadEnvelope = payloadEnvelope
|
|
66
|
+
|
|
67
|
+
self._keepFuncInScope = processPayload
|
|
68
|
+
|
|
69
|
+
PayloadEndpoint(epFilt, processPayload, requiredPermissions=frozenset())
|
|
70
|
+
self._processEnvelope(payload)
|
|
52
71
|
return payload
|
|
53
72
|
|
|
73
|
+
@inlineCallbacks
|
|
54
74
|
def testFiltMatches(self):
|
|
55
75
|
plFilt = {"key": "unittest", "This matches": 555}
|
|
56
76
|
epFilt = {"key": "unittest", "This matches": 555}
|
|
57
77
|
|
|
58
|
-
payload = self.
|
|
78
|
+
payload = self._registerAndProcess(plFilt, epFilt)
|
|
79
|
+
yield self._flushReactor()
|
|
59
80
|
|
|
60
81
|
self.assertEqual(
|
|
61
|
-
payload,
|
|
62
|
-
self.deliveredPayloadEnvelope,
|
|
82
|
+
payload.filt,
|
|
83
|
+
self.deliveredPayloadEnvelope.filt,
|
|
63
84
|
"PayloadIO/PayloadEndpoint delivery error",
|
|
64
85
|
)
|
|
65
86
|
|
|
87
|
+
@inlineCallbacks
|
|
66
88
|
def testFiltValueUnmatched(self):
|
|
67
89
|
plFilt = {"key": "unittest", "This matches": 555}
|
|
68
90
|
epFilt = {"key": "unittest", "This matches": 0}
|
|
69
91
|
|
|
70
|
-
self.
|
|
92
|
+
self._registerAndProcess(plFilt, epFilt)
|
|
93
|
+
yield self._flushReactor()
|
|
71
94
|
|
|
72
95
|
self.assertEqual(
|
|
73
96
|
self.deliveredPayloadEnvelope,
|
|
@@ -75,11 +98,13 @@ class PayloadEndpointPyTest(unittest.TestCase):
|
|
|
75
98
|
"PayloadIO/PayloadEndpoint unmatched value test error",
|
|
76
99
|
)
|
|
77
100
|
|
|
101
|
+
@inlineCallbacks
|
|
78
102
|
def testFiltKeyUnmatched(self):
|
|
79
103
|
plFilt = {"key": "unittest", "This matches": 555}
|
|
80
104
|
epFilt = {"key": "unittest", "This doesnt matches": 555}
|
|
81
105
|
|
|
82
|
-
self.
|
|
106
|
+
self._registerAndProcess(plFilt, epFilt)
|
|
107
|
+
yield self._flushReactor()
|
|
83
108
|
|
|
84
109
|
self.assertEqual(
|
|
85
110
|
self.deliveredPayloadEnvelope,
|
|
@@ -87,11 +112,10 @@ class PayloadEndpointPyTest(unittest.TestCase):
|
|
|
87
112
|
"PayloadIO/PayloadEndpoint unmatched value test error",
|
|
88
113
|
)
|
|
89
114
|
|
|
115
|
+
@inlineCallbacks
|
|
90
116
|
def testFunctionGoesOutOfScope(self):
|
|
91
117
|
filt = {"key": "unittest", "This matches": 555}
|
|
92
|
-
|
|
93
|
-
payload = Payload()
|
|
94
|
-
payload.filt = filt
|
|
118
|
+
payload = self._newPayload(filt)
|
|
95
119
|
|
|
96
120
|
def subScope():
|
|
97
121
|
def outOfScopeFunc(
|
|
@@ -99,15 +123,12 @@ class PayloadEndpointPyTest(unittest.TestCase):
|
|
|
99
123
|
):
|
|
100
124
|
self.deliveredPayloadEnvelope = payloadEnvelope
|
|
101
125
|
|
|
102
|
-
PayloadEndpoint(
|
|
126
|
+
PayloadEndpoint(
|
|
127
|
+
filt, outOfScopeFunc, requiredPermissions=frozenset()
|
|
128
|
+
)
|
|
103
129
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
vortexUuid="test",
|
|
107
|
-
vortexName="test",
|
|
108
|
-
vortexSession=None,
|
|
109
|
-
sendResponse=lambda x: x,
|
|
110
|
-
)
|
|
130
|
+
self._processEnvelope(payload)
|
|
131
|
+
yield self._flushReactor()
|
|
111
132
|
|
|
112
133
|
self.assertEqual(
|
|
113
134
|
self.deliveredPayloadEnvelope,
|
|
@@ -115,23 +136,19 @@ class PayloadEndpointPyTest(unittest.TestCase):
|
|
|
115
136
|
"PayloadIO/PayloadEndpoint unmatched value test error",
|
|
116
137
|
)
|
|
117
138
|
|
|
139
|
+
@inlineCallbacks
|
|
118
140
|
def testClassGoesOutOdScope(self):
|
|
119
141
|
filt = {"key": "unittest", "This matches": 555}
|
|
120
|
-
|
|
121
|
-
payload = Payload()
|
|
122
|
-
payload.filt = filt
|
|
142
|
+
payload = self._newPayload(filt)
|
|
123
143
|
|
|
124
144
|
def subScope():
|
|
125
145
|
inst = PayloadEndpointPyTestAssignPayload(self)
|
|
126
|
-
PayloadEndpoint(
|
|
146
|
+
PayloadEndpoint(
|
|
147
|
+
filt, inst.process, requiredPermissions=frozenset()
|
|
148
|
+
)
|
|
127
149
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
vortexUuid="test",
|
|
131
|
-
vortexName="test",
|
|
132
|
-
vortexSession=None,
|
|
133
|
-
sendResponse=lambda x: x,
|
|
134
|
-
)
|
|
150
|
+
self._processEnvelope(payload)
|
|
151
|
+
yield self._flushReactor()
|
|
135
152
|
|
|
136
153
|
self.assertEqual(
|
|
137
154
|
self.deliveredPayloadEnvelope,
|
|
@@ -139,25 +156,19 @@ class PayloadEndpointPyTest(unittest.TestCase):
|
|
|
139
156
|
"PayloadIO/PayloadEndpoint unmatched value test error",
|
|
140
157
|
)
|
|
141
158
|
|
|
159
|
+
@inlineCallbacks
|
|
142
160
|
def testClassStaysInScope(self):
|
|
143
161
|
filt = {"key": "unittest", "This matches": 555}
|
|
144
|
-
|
|
145
|
-
payload = Payload()
|
|
146
|
-
payload.filt = filt
|
|
162
|
+
payload = self._newPayload(filt)
|
|
147
163
|
|
|
148
164
|
inst = PayloadEndpointPyTestAssignPayload(self)
|
|
149
165
|
PayloadEndpoint(filt, inst.process, requiredPermissions=frozenset())
|
|
150
166
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
vortexUuid="test",
|
|
154
|
-
vortexName="test",
|
|
155
|
-
vortexSession=None,
|
|
156
|
-
sendResponse=lambda x: x,
|
|
157
|
-
)
|
|
167
|
+
self._processEnvelope(payload)
|
|
168
|
+
yield self._flushReactor()
|
|
158
169
|
|
|
159
170
|
self.assertEqual(
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
payload.filt,
|
|
172
|
+
self.deliveredPayloadEnvelope.filt,
|
|
162
173
|
"PayloadIO/PayloadEndpoint unmatched value test error",
|
|
163
174
|
)
|
|
@@ -595,6 +595,23 @@ class Tuple(Jsonable, _TupleToPlainJsonMixin, _TupleToSqlaJsonMixin):
|
|
|
595
595
|
def tupleFieldNames(cls) -> List[str]:
|
|
596
596
|
return cls.__fieldNames__
|
|
597
597
|
|
|
598
|
+
@classmethod
|
|
599
|
+
def tupleJsonSchema(cls) -> "JsonSchemaNodeTuple":
|
|
600
|
+
"""Tuple JSON Schema
|
|
601
|
+
|
|
602
|
+
Build a typed Draft 2020-12 subset schema tree from this tuple's
|
|
603
|
+
``TupleField`` metadata.
|
|
604
|
+
|
|
605
|
+
:return: Root ``JsonSchemaNodeTuple``. Call ``buildJsonSchemaDict()``
|
|
606
|
+
for Draft 2020-12 JSON Schema keys, or ``tupleToRestfulJsonDict()``
|
|
607
|
+
for Vortex restful JSON.
|
|
608
|
+
"""
|
|
609
|
+
from vortex.tuple_json_schema.tuple_json_schema_builder import (
|
|
610
|
+
TupleJsonSchemaBuilder,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
return TupleJsonSchemaBuilder.buildJsonSchema(cls)
|
|
614
|
+
|
|
598
615
|
@classmethod
|
|
599
616
|
def tupleName(cls): # DEPRECIATED
|
|
600
617
|
return cls.__tupleType__
|
|
@@ -446,8 +446,14 @@ class VortexFactory:
|
|
|
446
446
|
yield deferLater(reactor, 0.05, lambda: None)
|
|
447
447
|
|
|
448
448
|
while cls.__listeningPorts:
|
|
449
|
-
cls.__listeningPorts.pop().stopListening()
|
|
450
|
-
|
|
449
|
+
stopDeferred = cls.__listeningPorts.pop().stopListening()
|
|
450
|
+
if stopDeferred is not None:
|
|
451
|
+
yield stopDeferred
|
|
452
|
+
|
|
453
|
+
# Let Twisted finish unregistering closed ports from the reactor
|
|
454
|
+
# before before-shutdown returns — avoids epoll Bad file descriptor
|
|
455
|
+
# spam when other before-shutdown handlers close sockets next.
|
|
456
|
+
yield deferLater(reactor, 0.05, lambda: None)
|
|
451
457
|
|
|
452
458
|
cls.__vortexInfoState = _VortexFactoryVortexInfo()
|
|
453
459
|
|
|
@@ -163,6 +163,29 @@ class TupleChangeEventBus:
|
|
|
163
163
|
|
|
164
164
|
self._weakObserversBySelectorName[eventName].add(observer)
|
|
165
165
|
|
|
166
|
+
def removeObserver(
|
|
167
|
+
self,
|
|
168
|
+
observer: TupleChangeEventBusObserverABC,
|
|
169
|
+
eventNameOrClass: Optional[
|
|
170
|
+
Union[
|
|
171
|
+
str,
|
|
172
|
+
Type[TupleChangeEventABC],
|
|
173
|
+
TupleChangeEventABC,
|
|
174
|
+
]
|
|
175
|
+
] = None,
|
|
176
|
+
) -> None:
|
|
177
|
+
assert isinstance(
|
|
178
|
+
observer, TupleChangeEventBusObserverABC
|
|
179
|
+
), "observer must be a TupleChangeEventBusObserverABC"
|
|
180
|
+
|
|
181
|
+
if eventNameOrClass is None:
|
|
182
|
+
self._weakObserversForAllTupleChangeEventABCNames.discard(observer)
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
eventName = self._nameFromEvent(eventNameOrClass)
|
|
186
|
+
|
|
187
|
+
self._weakObserversBySelectorName[eventName].discard(observer)
|
|
188
|
+
|
|
166
189
|
# noinspection PyMethodMayBeStatic
|
|
167
190
|
def _nameFromEvent(
|
|
168
191
|
self,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright (c) 2016-2026 Flying Ion Pty Ltd
|
|
2
|
+
#
|
|
3
|
+
# This software is open source; the MIT license applies.
|
|
4
|
+
# See LICENSE for the full license text.
|
|
5
|
+
#
|
|
6
|
+
# Website : https://flyingion.com
|
|
7
|
+
# Support : contact@flyingion.com
|
|
8
|
+
|
|
9
|
+
"""Tuple JSON Schema — typed Vortex tuples for Draft 2020-12 subset schemas.
|
|
10
|
+
|
|
11
|
+
Import from submodules::
|
|
12
|
+
|
|
13
|
+
from vortex.tuple_json_schema.json_schema_node_tuple import JsonSchemaNodeTuple
|
|
14
|
+
from vortex.tuple_json_schema.tuple_json_schema_builder import (
|
|
15
|
+
TupleJsonSchemaBuilder,
|
|
16
|
+
)
|
|
17
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_TYPE_STRING
|
|
18
|
+
"""
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Copyright (c) 2016-2026 Flying Ion Pty Ltd
|
|
2
|
+
#
|
|
3
|
+
# This software is open source; the MIT license applies.
|
|
4
|
+
# See LICENSE for the full license text.
|
|
5
|
+
#
|
|
6
|
+
# Website : https://flyingion.com
|
|
7
|
+
# Support : contact@flyingion.com
|
|
8
|
+
|
|
9
|
+
"""JSON Schema string constants for Draft 2020-12 subset."""
|
|
10
|
+
|
|
11
|
+
JSON_SCHEMA_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema"
|
|
12
|
+
|
|
13
|
+
JSON_SCHEMA_KEY_SCHEMA = "$schema"
|
|
14
|
+
JSON_SCHEMA_KEY_DEFS = "$defs"
|
|
15
|
+
JSON_SCHEMA_KEY_REF = "$ref"
|
|
16
|
+
JSON_SCHEMA_KEY_TYPE = "type"
|
|
17
|
+
JSON_SCHEMA_KEY_TITLE = "title"
|
|
18
|
+
JSON_SCHEMA_KEY_PROPERTIES = "properties"
|
|
19
|
+
JSON_SCHEMA_KEY_REQUIRED = "required"
|
|
20
|
+
JSON_SCHEMA_KEY_ITEMS = "items"
|
|
21
|
+
JSON_SCHEMA_KEY_FORMAT = "format"
|
|
22
|
+
JSON_SCHEMA_KEY_CONTENT_ENCODING = "contentEncoding"
|
|
23
|
+
JSON_SCHEMA_KEY_DESCRIPTION = "description"
|
|
24
|
+
|
|
25
|
+
JSON_SCHEMA_TYPE_OBJECT = "object"
|
|
26
|
+
JSON_SCHEMA_TYPE_STRING = "string"
|
|
27
|
+
JSON_SCHEMA_TYPE_INTEGER = "integer"
|
|
28
|
+
JSON_SCHEMA_TYPE_NUMBER = "number"
|
|
29
|
+
JSON_SCHEMA_TYPE_BOOLEAN = "boolean"
|
|
30
|
+
JSON_SCHEMA_TYPE_ARRAY = "array"
|
|
31
|
+
|
|
32
|
+
JSON_SCHEMA_FORMAT_DATE_TIME = "date-time"
|
|
33
|
+
JSON_SCHEMA_CONTENT_ENCODING_BINARY = "binary"
|
|
34
|
+
|
|
35
|
+
JSON_SCHEMA_DEFS_REF_PREFIX = f"#/{JSON_SCHEMA_KEY_DEFS}/"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Copyright (c) 2016-2026 Flying Ion Pty Ltd
|
|
2
|
+
#
|
|
3
|
+
# This software is open source; the MIT license applies.
|
|
4
|
+
# See LICENSE for the full license text.
|
|
5
|
+
#
|
|
6
|
+
# Website : https://flyingion.com
|
|
7
|
+
# Support : contact@flyingion.com
|
|
8
|
+
|
|
9
|
+
"""JSON Schema node as a Vortex tuple (Draft 2020-12 subset)."""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from vortex.Tuple import Tuple
|
|
16
|
+
from vortex.Tuple import TupleField
|
|
17
|
+
from vortex.Tuple import addTupleType
|
|
18
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_CONTENT_ENCODING
|
|
19
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_DEFS
|
|
20
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_DESCRIPTION
|
|
21
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_FORMAT
|
|
22
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_ITEMS
|
|
23
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_PROPERTIES
|
|
24
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_REF
|
|
25
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_REQUIRED
|
|
26
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_SCHEMA
|
|
27
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_TITLE
|
|
28
|
+
from vortex.tuple_json_schema.constants import JSON_SCHEMA_KEY_TYPE
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@addTupleType
|
|
32
|
+
class JsonSchemaNodeTuple(Tuple):
|
|
33
|
+
"""JSON Schema Node Tuple
|
|
34
|
+
|
|
35
|
+
Typed Draft 2020-12 subset node. Application code uses this tuple;
|
|
36
|
+
call ``buildJsonSchemaDict()`` only at a boundary that needs raw JSON Schema
|
|
37
|
+
(``$schema`` / ``$ref`` / ``$defs`` keys). Prefer
|
|
38
|
+
``tupleToRestfulJsonDict()`` when serialising as a normal Vortex tuple.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
__tupleType__ = "flyingion.vortex.JsonSchemaNodeTuple"
|
|
42
|
+
|
|
43
|
+
schemaUri: str | None = TupleField(None, typingType=str | None)
|
|
44
|
+
schemaType: str | None = TupleField(None, typingType=str | None)
|
|
45
|
+
title: str | None = TupleField(None, typingType=str | None)
|
|
46
|
+
description: str | None = TupleField(None, typingType=str | None)
|
|
47
|
+
format: str | None = TupleField(None, typingType=str | None)
|
|
48
|
+
contentEncoding: str | None = TupleField(None, typingType=str | None)
|
|
49
|
+
ref: str | None = TupleField(None, typingType=str | None)
|
|
50
|
+
items: JsonSchemaNodeTuple | None = TupleField(None)
|
|
51
|
+
properties: dict[str, JsonSchemaNodeTuple] | None = TupleField(None)
|
|
52
|
+
required: list[str] | None = TupleField(None, typingType=list[str] | None)
|
|
53
|
+
defs: dict[str, JsonSchemaNodeTuple] | None = TupleField(None)
|
|
54
|
+
|
|
55
|
+
def buildJsonSchemaDict(self) -> dict[str, Any]:
|
|
56
|
+
"""Build JSON Schema Dict
|
|
57
|
+
|
|
58
|
+
Emit a Draft 2020-12 subset dict with JSON Schema keys
|
|
59
|
+
(``$schema``, ``$ref``, ``$defs``, ``type``, …).
|
|
60
|
+
|
|
61
|
+
:return: JSON-serialisable schema dict.
|
|
62
|
+
"""
|
|
63
|
+
out: dict[str, Any] = {}
|
|
64
|
+
if self.schemaUri is not None:
|
|
65
|
+
out[JSON_SCHEMA_KEY_SCHEMA] = self.schemaUri
|
|
66
|
+
if self.schemaType is not None:
|
|
67
|
+
out[JSON_SCHEMA_KEY_TYPE] = self.schemaType
|
|
68
|
+
if self.title is not None:
|
|
69
|
+
out[JSON_SCHEMA_KEY_TITLE] = self.title
|
|
70
|
+
if self.description is not None:
|
|
71
|
+
out[JSON_SCHEMA_KEY_DESCRIPTION] = self.description
|
|
72
|
+
if self.format is not None:
|
|
73
|
+
out[JSON_SCHEMA_KEY_FORMAT] = self.format
|
|
74
|
+
if self.contentEncoding is not None:
|
|
75
|
+
out[JSON_SCHEMA_KEY_CONTENT_ENCODING] = self.contentEncoding
|
|
76
|
+
if self.ref is not None:
|
|
77
|
+
out[JSON_SCHEMA_KEY_REF] = self.ref
|
|
78
|
+
if self.items is not None:
|
|
79
|
+
out[JSON_SCHEMA_KEY_ITEMS] = self.items.buildJsonSchemaDict()
|
|
80
|
+
if self.properties is not None:
|
|
81
|
+
out[JSON_SCHEMA_KEY_PROPERTIES] = {
|
|
82
|
+
name: node.buildJsonSchemaDict()
|
|
83
|
+
for name, node in self.properties.items()
|
|
84
|
+
}
|
|
85
|
+
if self.required is not None:
|
|
86
|
+
out[JSON_SCHEMA_KEY_REQUIRED] = list(self.required)
|
|
87
|
+
if self.defs is not None:
|
|
88
|
+
out[JSON_SCHEMA_KEY_DEFS] = {
|
|
89
|
+
name: node.buildJsonSchemaDict()
|
|
90
|
+
for name, node in self.defs.items()
|
|
91
|
+
}
|
|
92
|
+
return out
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
JsonSchemaNodeTuple.items.typingType = JsonSchemaNodeTuple | None
|
|
96
|
+
JsonSchemaNodeTuple.properties.typingType = (
|
|
97
|
+
dict[str, JsonSchemaNodeTuple] | None
|
|
98
|
+
)
|
|
99
|
+
JsonSchemaNodeTuple.defs.typingType = dict[str, JsonSchemaNodeTuple] | None
|