vortexpy 4.3.6__tar.gz → 6.0.0__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.
Files changed (154) hide show
  1. vortexpy-6.0.0/LICENSE +22 -0
  2. {vortexpy-4.3.6 → vortexpy-6.0.0}/PKG-INFO +10 -7
  3. {vortexpy-4.3.6 → vortexpy-6.0.0}/README.md +9 -6
  4. {vortexpy-4.3.6 → vortexpy-6.0.0}/pyproject.toml +1 -15
  5. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/DeferUtil.py +2 -25
  6. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/DeferUtil_test.py +0 -58
  7. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/Jsonable.py +4 -22
  8. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadEndpoint.py +31 -39
  9. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadEndpointTest.py +3 -2
  10. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadEnvelope.py +9 -9
  11. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadIO.py +11 -13
  12. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadPriority.py +1 -0
  13. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadResponse.py +3 -8
  14. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/SerialiseUtil.py +6 -85
  15. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/Tuple.py +9 -50
  16. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +1 -4
  17. vortexpy-4.3.6/vortex/TupleDocumentConverter.py → vortexpy-6.0.0/vortex/TupleRestful.py +89 -288
  18. vortexpy-6.0.0/vortex/TupleRestfulTest.py +267 -0
  19. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexAccessControlContextABC.py +47 -13
  20. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexAuthenticatorABC.py +6 -1
  21. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexFactory.py +1 -9
  22. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexPayloadProtocol.py +4 -9
  23. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexPermission.py +0 -1
  24. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexPermissionError.py +0 -1
  25. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexServer.py +20 -3
  26. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexServerConnection.py +2 -2
  27. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexServerHttpResource.py +1 -10
  28. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexServerWebsocket.py +7 -27
  29. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexSession.py +65 -14
  30. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexTcpMemoryLeakPayloadTest.py +24 -24
  31. vortexpy-6.0.0/vortex/VortexUserDetailsABC.py +40 -0
  32. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexUtil.py +0 -2
  33. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexWritePushProducer.py +8 -9
  34. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexWritePushProducer_test.py +8 -12
  35. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/__init__.py +1 -1
  36. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/data_loader/TupleDataLiveLockManager.py +147 -65
  37. vortexpy-6.0.0/vortex/data_loader/TupleDataLiveLockManager_test.py +336 -0
  38. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/data_loader/TupleDataLoader.py +16 -28
  39. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/data_loader/TupleDataLoaderDelegate.py +4 -14
  40. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/data_loader/TupleDataLoaderTuples.py +13 -9
  41. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/event_bus/TupleChangeEventBus.py +8 -7
  42. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleActionProcessor.py +87 -103
  43. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleActionProcessorProxy.py +13 -13
  44. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleActionProcessorTestHandler.py +3 -3
  45. vortexpy-6.0.0/vortex/handler/TupleActionProcessor_test.py +147 -0
  46. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataAccessGranterABC.py +4 -4
  47. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataObservableCache.py +7 -7
  48. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataObservableHandler.py +100 -107
  49. vortexpy-6.0.0/vortex/handler/TupleDataObservableHandler_test.py +280 -0
  50. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataObservableProxyHandler.py +78 -83
  51. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataObserverClient.py +6 -20
  52. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataProviderABC.py +1 -3
  53. vortexpy-6.0.0/vortex/restful/RestfulHttpClientTest.py +68 -0
  54. vortexpy-6.0.0/vortex/restful/RestfulResourceTest.py +108 -0
  55. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/rpc/RPC.py +44 -73
  56. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/sqla_orm/OrmCrudHandler.py +34 -8
  57. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/storage/TupleStorageSqlite.py +3 -3
  58. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/tuple_json_schema/tuple_json_schema_builder.py +1 -3
  59. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/tuple_json_schema/tuple_json_schema_test.py +2 -4
  60. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/twisted_runtime_metrics.py +48 -92
  61. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/twisted_runtime_metrics_test.py +7 -7
  62. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortexpy.egg-info/PKG-INFO +10 -7
  63. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortexpy.egg-info/SOURCES.txt +7 -25
  64. vortexpy-6.0.0/vortexpy.egg-info/top_level.txt +1 -0
  65. vortexpy-4.3.6/example/TwistedDeferredTimeout.py +0 -50
  66. vortexpy-4.3.6/example/__init__.py +0 -1
  67. vortexpy-4.3.6/example/run_http_upgraded_websocket_ping_client.py +0 -68
  68. vortexpy-4.3.6/example/run_http_upgraded_websocket_ping_server.py +0 -71
  69. vortexpy-4.3.6/example/run_http_upgraded_websocket_rpc_test_client.py +0 -67
  70. vortexpy-4.3.6/example/run_http_upgraded_websocket_rpc_test_server.py +0 -45
  71. vortexpy-4.3.6/example/run_websocket_rpc_test_client.py +0 -64
  72. vortexpy-4.3.6/example/run_websocket_rpc_test_server.py +0 -29
  73. vortexpy-4.3.6/example/setup_example_logging.py +0 -44
  74. vortexpy-4.3.6/example/store_and_load_json.py +0 -68
  75. vortexpy-4.3.6/vortex/PayloadResponse_test.py +0 -42
  76. vortexpy-4.3.6/vortex/SerialiseUtil_test.py +0 -186
  77. vortexpy-4.3.6/vortex/TupleBson.py +0 -203
  78. vortexpy-4.3.6/vortex/TupleBson_test.py +0 -300
  79. vortexpy-4.3.6/vortex/TupleRestful.py +0 -139
  80. vortexpy-4.3.6/vortex/TupleRestful_test.py +0 -209
  81. vortexpy-4.3.6/vortex/VortexServerWebsocket_test.py +0 -22
  82. vortexpy-4.3.6/vortex/VortexServer_test.py +0 -134
  83. vortexpy-4.3.6/vortex/handler/TupleDataObservableCache_test.py +0 -28
  84. vortexpy-4.3.6/vortex/handler/TupleDataObservableHandler_test.py +0 -392
  85. vortexpy-4.3.6/vortex/handler/TupleDataObservableProxyHandler_test.py +0 -214
  86. vortexpy-4.3.6/vortex/payload_many_tuples_benchmark_test.py +0 -71
  87. vortexpy-4.3.6/vortex/restful/restful_http_client_test.py +0 -73
  88. vortexpy-4.3.6/vortex/restful/restful_resource_test.py +0 -76
  89. vortexpy-4.3.6/vortex/rpc/RPC_test.py +0 -114
  90. vortexpy-4.3.6/vortex/tuple_test_fixtures.py +0 -206
  91. vortexpy-4.3.6/vortexpy.egg-info/top_level.txt +0 -2
  92. {vortexpy-4.3.6 → vortexpy-6.0.0}/setup.cfg +0 -0
  93. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/DataWrapTuple.py +0 -0
  94. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/MtlsAuthorizedClientRegistry.py +0 -0
  95. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/MtlsAuthorizedClientRegistry_test.py +0 -0
  96. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/MtlsServerSslOptions.py +0 -0
  97. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/Payload.py +0 -0
  98. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadFilterKeys.py +0 -0
  99. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadResponseTest.py +0 -0
  100. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PayloadTest.py +0 -0
  101. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/PerformTestActionTuple.py +0 -0
  102. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TestTuple.py +0 -0
  103. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TupleAction.py +0 -0
  104. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TupleDataForTest.py +0 -0
  105. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TuplePerfTest.py +0 -0
  106. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TupleSelector.py +0 -0
  107. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/TupleTest.py +0 -0
  108. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexABC.py +0 -0
  109. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexClientHttp.py +0 -0
  110. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexClientHttpTest.py +0 -0
  111. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexClientTcp.py +0 -0
  112. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexClientWebsocketFactory.py +0 -0
  113. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexConnectionABC.py +0 -0
  114. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexEndpointPermission.py +0 -0
  115. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexFactory_test.py +0 -0
  116. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexServerConnection_test.py +0 -0
  117. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexServerTcp.py +0 -0
  118. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexSession_test.py +0 -0
  119. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexTcpMemoryLeakRawTest.py +0 -0
  120. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/VortexTest.py +0 -0
  121. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -0
  122. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/data_loader/__init__.py +0 -0
  123. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/event_bus/TupleChangeEventABC.py +0 -0
  124. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/event_bus/TupleChangeEventBusObserverABC.py +0 -0
  125. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/event_bus/__init__.py +0 -0
  126. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleActionProcessor_shutdown_test.py +0 -0
  127. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleActionPushOffline.py +0 -0
  128. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleActionPushOffline_test.py +0 -0
  129. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataActionClient.py +0 -0
  130. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/TupleDataObservableTestHandler.py +0 -0
  131. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/VortexJSTupleLoaderTestHandler.py +0 -0
  132. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/handler/__init__.py +0 -0
  133. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/mypy_plugin.py +0 -0
  134. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/py.typed +0 -0
  135. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/restful/GzippedDataHttpClient.py +0 -0
  136. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/restful/GzippedDataHttpClientTest.py +0 -0
  137. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/restful/GzippedPayloadHttpClient.py +0 -0
  138. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/restful/RestfulHttpClient.py +0 -0
  139. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/restful/RestfulResource.py +0 -0
  140. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/restful/__init__.py +0 -0
  141. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/rpc/RPCTest.py +0 -0
  142. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/rpc/__init__.py +0 -0
  143. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/sqla_orm/TupleProviderForDb.py +0 -0
  144. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/sqla_orm/VortexTupleJson.py +0 -0
  145. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/sqla_orm/__init__.py +0 -0
  146. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/storage/TupleActionStorageSqlite.py +0 -0
  147. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/storage/__init__.py +0 -0
  148. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/tuple_json_schema/__init__.py +0 -0
  149. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/tuple_json_schema/constants.py +0 -0
  150. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/tuple_json_schema/json_schema_node_tuple.py +0 -0
  151. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortex/tuple_json_schema/tuple_json_schema_test_tuples.py +0 -0
  152. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortexpy.egg-info/dependency_links.txt +0 -0
  153. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortexpy.egg-info/entry_points.txt +0 -0
  154. {vortexpy-4.3.6 → vortexpy-6.0.0}/vortexpy.egg-info/requires.txt +0 -0
vortexpy-6.0.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016-2026 Flying Ion Pty Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortexpy
3
- Version: 4.3.6
3
+ Version: 6.0.0
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
6
  License: MIT
@@ -32,17 +32,20 @@ Requires-Dist: txhttputil>=1.2.0
32
32
  Flying Ion's observable, routable, data serialisation and transport code.
33
33
 
34
34
  See [README_PERMISSIONS.md](README_PERMISSIONS.md) for the two-level permission
35
- system (endpoint guards + access control context) introduced in v5.0.
36
-
37
- **Tuple and Payload tutorials:** [docs/README.md](docs/README.md) (simple → full features, encode/decode, threaded decode).
35
+ system (endpoint guards + access control context) introduced in v6.0.
38
36
 
39
37
  Why PY at the end? To be consistent with VortexJS, which is the Browser side package.
40
38
  See https://gitlab.com/flyingion/vortexjs
41
39
 
42
40
  Requirements:
43
- * Python 3.5
44
- * Twisted 16+
45
- * SQLAlchemy 1, 1.1
41
+ * Python 3.12 (development target)
42
+ * Twisted (autobahn / txwebsocket)
43
+ * SQLAlchemy >= 2.0
44
+
45
+ See `pyproject.toml` for the authoritative dependency list.
46
+
47
+ See [README_MIGRATION.md](README_MIGRATION.md) for breaking changes and
48
+ migration guides (4.4.0, 6.0.0).
46
49
 
47
50
  The "vortex" is designed to transport "Payloads" from a web browser (VortexJS) to a
48
51
  twisted web server (VortexPY). There is also a python client for python client to python server
@@ -3,17 +3,20 @@
3
3
  Flying Ion's observable, routable, data serialisation and transport code.
4
4
 
5
5
  See [README_PERMISSIONS.md](README_PERMISSIONS.md) for the two-level permission
6
- system (endpoint guards + access control context) introduced in v5.0.
7
-
8
- **Tuple and Payload tutorials:** [docs/README.md](docs/README.md) (simple → full features, encode/decode, threaded decode).
6
+ system (endpoint guards + access control context) introduced in v6.0.
9
7
 
10
8
  Why PY at the end? To be consistent with VortexJS, which is the Browser side package.
11
9
  See https://gitlab.com/flyingion/vortexjs
12
10
 
13
11
  Requirements:
14
- * Python 3.5
15
- * Twisted 16+
16
- * SQLAlchemy 1, 1.1
12
+ * Python 3.12 (development target)
13
+ * Twisted (autobahn / txwebsocket)
14
+ * SQLAlchemy >= 2.0
15
+
16
+ See `pyproject.toml` for the authoritative dependency list.
17
+
18
+ See [README_MIGRATION.md](README_MIGRATION.md) for breaking changes and
19
+ migration guides (4.4.0, 6.0.0).
17
20
 
18
21
  The "vortex" is designed to transport "Payloads" from a web browser (VortexJS) to a
19
22
  twisted web server (VortexPY). There is also a python client for python client to python server
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vortexpy"
7
- version = "4.3.6"
7
+ version = "6.0.0"
8
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."
@@ -53,17 +53,3 @@ namespaces = false
53
53
 
54
54
  [project.entry-points."mypy.plugins"]
55
55
  vortex_defer = "vortex.mypy_plugin:plugin"
56
-
57
- [tool.pytest.ini_options]
58
- testpaths = ["vortex"]
59
- python_files = ["*_test.py"]
60
- python_functions = ["test*"]
61
- norecursedirs = ["_trial_temp", "example", "old_fork"]
62
- markers = [
63
- "trial: legacy Twisted Trial *Test.py modules (not collected; run with trial CLI)",
64
- "slow: large payload or timing benchmarks",
65
- "integration: docker or multi-process integration suites under test-integration/",
66
- ]
67
-
68
- [tool.taskipy.tasks]
69
- test-integrations = "bash -c 'pytest test-integration/ -xvs -m integration'"
@@ -32,7 +32,6 @@ def callMethodLater(func):
32
32
  def wrapper(*args, **kwargs):
33
33
  return reactor.callLater(0, func, *args, **kwargs)
34
34
 
35
- wrapper.__wrapped__ = func
36
35
  return wrapper
37
36
 
38
37
 
@@ -47,7 +46,7 @@ def nonConcurrentMethod(method):
47
46
  call.running = False
48
47
 
49
48
  call.running = False
50
- call.__wrapped__ = method
49
+
51
50
  return call
52
51
 
53
52
 
@@ -61,7 +60,6 @@ def maybeDeferredWrap(funcToWrap):
61
60
  def func(*args, **kwargs):
62
61
  return maybeDeferred(funcToWrap, *args, **kwargs)
63
62
 
64
- func.__wrapped__ = funcToWrap
65
63
  return func
66
64
 
67
65
 
@@ -80,7 +78,6 @@ def ensureDeferredWrap(funcToWrap):
80
78
  def func(*args, **kwargs):
81
79
  return ensureDeferred(funcToWrap(*args, **kwargs))
82
80
 
83
- func.__wrapped__ = funcToWrap
84
81
  return func
85
82
 
86
83
 
@@ -143,7 +140,6 @@ def vortexInlineCallbacksLogAndConsumeFailure(loggerArg):
143
140
  d.addErrback(vortexLogAndConsumeFailure, loggerArg)
144
141
  return d
145
142
 
146
- called.__wrapped__ = funcToWrap
147
143
  return called
148
144
 
149
145
  return wrapper
@@ -157,7 +153,6 @@ def deferToThreadWrapWithLogger(
157
153
  loggerArg: logging.Logger,
158
154
  consumeError: bool = False,
159
155
  checkMainThread: bool = True,
160
- logException: bool = True,
161
156
  ) -> Callable[[Callable[_P, _R]], Callable[_P, Deferred[_R]]]:
162
157
  """Defer To Thread Wrap With Logger
163
158
 
@@ -168,9 +163,6 @@ def deferToThreadWrapWithLogger(
168
163
  :param checkMainThread: Don't use this parameter, it's only needed during reactor
169
164
  startup when the reactors thread isn't properly set and the check
170
165
  fails.
171
- :param logException: When True, thread exceptions are logged at ERROR with a full
172
- traceback. When False, the same events are logged at DEBUG with a
173
- neutral message so log analysers are not triggered.
174
166
 
175
167
  :return: A decorator that makes the wrapped callable return a Deferred.
176
168
 
@@ -238,26 +230,11 @@ def deferToThreadWrapWithLogger(
238
230
  methodFrame,
239
231
  )
240
232
 
241
- def handleFailure(failure):
242
- if logException:
243
- return vortexLogFailure(
244
- failure, loggerArg, consumeError=consumeError
245
- )
246
-
247
- loggerArg.debug(
248
- "Threaded call failed in %s.%s: %s",
249
- funcToWrap.__module__,
250
- funcToWrap.__qualname__,
251
- failure.getErrorMessage(),
252
- )
253
- return True if consumeError else failure
254
-
255
233
  d: Deferred[_R] = deferToThread(timedFuncToRun, *args, **kwargs)
256
234
  d.addCallback(checkForInlineCallbackYield)
257
- d.addErrback(handleFailure)
235
+ d.addErrback(vortexLogFailure, loggerArg, consumeError=consumeError)
258
236
  return d
259
237
 
260
- func.__wrapped__ = funcToWrap
261
238
  return func
262
239
 
263
240
  return wrapper
@@ -84,61 +84,3 @@ async def testDeferToThreadWrapWithLoggerCapturesSlowCallFrame() -> None:
84
84
  assert "twisted_runtime_metrics.py" not in snapshot.longestThreadedMethodFrame, (
85
85
  "captured frame must skip metrics collector internals"
86
86
  )
87
-
88
-
89
- @deferToThreadWrapWithLogger(logger, logException=False)
90
- def _deferUtilTestFailingWorkQuiet() -> str:
91
- raise ValueError("expected quiet failure")
92
-
93
-
94
- @deferToThreadWrapWithLogger(logger)
95
- def _deferUtilTestFailingWorkLoud() -> str:
96
- raise ValueError("expected loud failure")
97
-
98
-
99
- @pytest.mark.twisted
100
- @ensureDeferred
101
- async def testDeferToThreadWrapWithLoggerLogExceptionFalseLogsDebug(
102
- caplog: pytest.LogCaptureFixture,
103
- ) -> None:
104
- with caplog.at_level(logging.DEBUG, logger=logger.name):
105
- try:
106
- await _deferUtilTestFailingWorkQuiet()
107
- raise AssertionError("wrapped call must propagate the failure")
108
- except ValueError:
109
- pass
110
-
111
- errorRecords = [r for r in caplog.records if r.levelno >= logging.ERROR]
112
- assert errorRecords == [], (
113
- "logException=False must not log thread failures at ERROR"
114
- )
115
- debugRecords = [
116
- r
117
- for r in caplog.records
118
- if r.levelno == logging.DEBUG and "expected quiet failure" in r.message
119
- ]
120
- assert len(debugRecords) == 1, (
121
- "logException=False must log one neutral DEBUG record"
122
- )
123
-
124
-
125
- @pytest.mark.twisted
126
- @ensureDeferred
127
- async def testDeferToThreadWrapWithLoggerDefaultLogsError(
128
- caplog: pytest.LogCaptureFixture,
129
- ) -> None:
130
- with caplog.at_level(logging.DEBUG, logger=logger.name):
131
- try:
132
- await _deferUtilTestFailingWorkLoud()
133
- raise AssertionError("wrapped call must propagate the failure")
134
- except ValueError:
135
- pass
136
-
137
- errorRecords = [
138
- r
139
- for r in caplog.records
140
- if r.levelno >= logging.ERROR and "expected loud failure" in r.getMessage()
141
- ]
142
- assert errorRecords, (
143
- "default logException=True must log thread failures at ERROR"
144
- )
@@ -107,11 +107,9 @@ class Jsonable(object):
107
107
  try:
108
108
  valueType = V_NULL if value is None else toRapuiType(value)
109
109
  except KeyError as e:
110
- unsupportedType = e.args[0] if e.args else value.__class__
111
110
  raise KeyError(
112
- "%r field name is %r, type is %r"
113
- % (unsupportedType, name, value.__class__)
114
- ) from e
111
+ "%s field name is %s, type is %s", e, name, value.__class__
112
+ )
115
113
 
116
114
  # Payloads and Tuples
117
115
  if self.__isRawJsonableField(name):
@@ -292,8 +290,6 @@ class Jsonable(object):
292
290
  Returns and instance of this object populated with data from the json dict
293
291
  """
294
292
 
295
- hints = utilTypeHintsForClass(type(self))
296
-
297
293
  # Use the fromJsonField code to convert all the values
298
294
  for name, value in list(jsonDict.items()):
299
295
  if name.startswith("_"):
@@ -301,21 +297,7 @@ class Jsonable(object):
301
297
 
302
298
  if self.__isRawJsonableField(name):
303
299
  setattr(self, name, value)
304
- continue
305
-
306
- value = self.fromJsonField(value)
307
-
308
- # Reconstruct enum members for fields annotated with an enum type.
309
- # Serialisation emits the primitive value; the receiving class's
310
- # annotation carries the enum class back into the member.
311
- enumClass = utilEnumClassFromTypeHint(hints.get(name))
312
- if (
313
- enumClass is not None
314
- and value is not None
315
- and not isinstance(value, enumClass)
316
- ):
317
- value = enumClass(value)
318
-
319
- setattr(self, name, value)
300
+ else:
301
+ setattr(self, name, self.fromJsonField(value))
320
302
 
321
303
  return self
@@ -22,32 +22,18 @@ from vortex.VortexAccessControlContextABC import (
22
22
  NO_AUTH_ACCESS_CONTROL_CONTEXT,
23
23
  VortexAccessControlContextABC,
24
24
  )
25
- from vortex.VortexEndpointPermission import (
26
- EndpointPermissionsType as PermissionsType,
27
- )
25
+ from vortex.VortexEndpointPermission import EndpointPermissionsType as PermissionsType
28
26
  from vortex.VortexSession import (
29
27
  _callableLabel,
30
28
  checkAccessControlContextSignature,
29
+ checkEndpointPermissionsSignature,
31
30
  checkNoHttpSessionInSignature,
31
+ checkNoVortexSessionInSignature,
32
32
  )
33
33
 
34
34
  logger = logging.getLogger(__name__)
35
35
 
36
36
 
37
- # TODO: typings needs to support keyword args before tbis will work.
38
- # PayloadEndpointProcessCallable = Callable[
39
- # [
40
- # ...
41
- # # PayloadEnvelope, # payloadEnvelope
42
- # # str, # vortexUuid
43
- # # str, # vortexName
44
- # # Any, # vortexSession
45
- # # SendVortexMsgResponseCallable # sendResponse
46
- # ],
47
- # Optional[Deferred] # Return
48
- # ]
49
-
50
-
51
37
  class PayloadEndpoint:
52
38
  """
53
39
  The payload endpoint is responsible for matching payloads filters against
@@ -71,7 +57,6 @@ class PayloadEndpoint:
71
57
  acceptOnlyFromVortex: Optional[Union[str, tuple]] = None,
72
58
  ignoreFromVortex: Optional[Union[str, tuple]] = None,
73
59
  requiredPermissions: Optional[PermissionsType] = None,
74
- logProcessException: bool = True,
75
60
  ) -> None:
76
61
  """
77
62
  :param filt: The filter to match against payloads
@@ -83,13 +68,8 @@ class PayloadEndpoint:
83
68
  the caller's session must hold. Pass an empty frozenset to
84
69
  explicitly declare "no permissions required". Omitting the
85
70
  parameter (``None``) emits a deprecation warning.
86
- :param logProcessException: When True, handler exceptions log at ERROR.
87
- When False, the same events log at DEBUG with neutral wording.
88
71
  """
89
- self._requiredPermissions: Optional[PermissionsType] = (
90
- requiredPermissions
91
- )
92
- self._logProcessException = logProcessException
72
+ self._requiredPermissions: Optional[PermissionsType] = requiredPermissions
93
73
 
94
74
  if requiredPermissions is None:
95
75
  logger.warning(self._NO_PERMISSIONS_WARNING, filt)
@@ -97,13 +77,18 @@ class PayloadEndpoint:
97
77
  callableDesc = (
98
78
  f"PayloadEndpoint filt={filt} callback={_callableLabel(callable_)}"
99
79
  )
100
- self._callableAcceptsAccessControlContext = (
101
- checkAccessControlContextSignature(
80
+ self._callableAcceptsAccessControlContext = checkAccessControlContextSignature(
81
+ callable_,
82
+ callableDescription=callableDesc,
83
+ )
84
+ self._callableAcceptsEndpointPermissions = (
85
+ checkEndpointPermissionsSignature(
102
86
  callable_,
103
87
  callableDescription=callableDesc,
104
88
  )
105
89
  )
106
90
  checkNoHttpSessionInSignature(callable_, callableDesc)
91
+ checkNoVortexSessionInSignature(callable_, callableDesc)
107
92
 
108
93
  self._acceptOnlyFromVortex = acceptOnlyFromVortex
109
94
  if isinstance(self._acceptOnlyFromVortex, str):
@@ -178,10 +163,7 @@ class PayloadEndpoint:
178
163
  return False
179
164
 
180
165
  # Filter for the backend vortexes.
181
- if (
182
- self._acceptOnlyFromVortex
183
- and vortexName not in self._acceptOnlyFromVortex
184
- ):
166
+ if self._acceptOnlyFromVortex and vortexName not in self._acceptOnlyFromVortex:
185
167
  return False
186
168
 
187
169
  def removeUnhashable(filt):
@@ -225,14 +207,13 @@ class PayloadEndpoint:
225
207
  if not self.check(payloadEnvelope, vortexName):
226
208
  return
227
209
 
228
- if self.PERMITTER and not self.PERMITTER.check(
229
- payloadEnvelope, vortexSession
230
- ):
210
+ if self.PERMITTER and not self.PERMITTER.check(payloadEnvelope, vortexSession):
231
211
  logger.debug("Permission failed for %s", payloadEnvelope.filt)
232
212
  return
233
213
 
234
214
  # Level 1: Endpoint permission check
235
215
  auth = VortexFactory.getAuthenticator()
216
+ endpointPermissions = None
236
217
  if self._requiredPermissions:
237
218
  if not auth:
238
219
  logger.error(
@@ -259,16 +240,26 @@ class PayloadEndpoint:
259
240
  )
260
241
  return
261
242
 
243
+ # Level 1 injection: when the callable declares endpointPermissions,
244
+ # compute the caller's permissions once here at the boundary so
245
+ # handlers never see the session or the authenticator.
246
+ if (
247
+ self._callableAcceptsEndpointPermissions
248
+ and endpointPermissions is None
249
+ and auth
250
+ and vortexSession
251
+ ):
252
+ endpointPermissions = yield maybeDeferred(
253
+ auth.getEndpointPermissions, vortexSession
254
+ )
255
+
262
256
  # Level 2: Get access control context, but only if the callback uses it.
263
257
  # Default to the sentinel so handlers always receive a non-None value
264
258
  # with a clear error on attribute access when auth is unavailable.
265
259
  accessControlContext = NO_AUTH_ACCESS_CONTROL_CONTEXT
266
260
  if self._callableAcceptsAccessControlContext and auth and vortexSession:
267
261
  raw_ctx = yield maybeDeferred(
268
- auth.getAccessControlContext,
269
- vortexSession,
270
- vortexName,
271
- vortexUuid,
262
+ auth.getAccessControlContext, vortexSession, vortexName, vortexUuid
272
263
  )
273
264
  if not isinstance(raw_ctx, VortexAccessControlContextABC):
274
265
  logger.error(
@@ -283,15 +274,16 @@ class PayloadEndpoint:
283
274
 
284
275
  callable_ = self._wref()
285
276
  if callable_:
286
- kwargs = dict(
277
+ kwargs: Dict[str, object] = dict(
287
278
  payloadEnvelope=payloadEnvelope,
288
279
  vortexUuid=vortexUuid,
289
280
  vortexName=vortexName,
290
281
  sendResponse=sendResponse,
291
- vortexSession=vortexSession,
292
282
  )
293
283
  if self._callableAcceptsAccessControlContext:
294
284
  kwargs["accessControlContext"] = accessControlContext
285
+ if self._callableAcceptsEndpointPermissions:
286
+ kwargs["endpointPermissions"] = endpointPermissions
295
287
  return (yield maybeDeferred(callable_, **kwargs))
296
288
  else:
297
289
  PayloadIO().remove(self)
@@ -54,7 +54,6 @@ class PayloadEndpointPyTest(unittest.TestCase):
54
54
  payloadEnvelope=PayloadEnvelope(filt=payload.filt),
55
55
  vortexUuid="test",
56
56
  vortexName="test",
57
- vortexSession=None,
58
57
  sendResponse=lambda x: x,
59
58
  )
60
59
 
@@ -143,7 +142,9 @@ class PayloadEndpointPyTest(unittest.TestCase):
143
142
 
144
143
  def subScope():
145
144
  inst = PayloadEndpointPyTestAssignPayload(self)
146
- PayloadEndpoint(filt, inst.process, requiredPermissions=frozenset())
145
+ PayloadEndpoint(
146
+ filt, inst.process, requiredPermissions=frozenset()
147
+ )
147
148
 
148
149
  self._processEnvelope(payload)
149
150
  yield self._flushReactor()
@@ -57,9 +57,9 @@ class PayloadEnvelope(Jsonable):
57
57
  ) -> None:
58
58
  Jsonable.__init__(self)
59
59
  """Constructor"""
60
- assert not (data and encodedPayload), (
61
- "You can not pass encodedData and encodedPayload"
62
- )
60
+ assert not (
61
+ data and encodedPayload
62
+ ), "You can not pass encodedData and encodedPayload"
63
63
 
64
64
  self.filt: Dict = {} if filt is None else filt
65
65
  self.data: Union[Any, str, None] = data if data else encodedPayload
@@ -75,18 +75,18 @@ class PayloadEnvelope(Jsonable):
75
75
  @property
76
76
  def encodedPayload(self) -> Optional[str]:
77
77
  # self.data could be from VortexJS/Typescript where we allow `str` data
78
- assert self.data is None or isinstance(self.data, str), (
79
- "Encoded payload is not None or a string"
80
- )
78
+ assert self.data is None or isinstance(
79
+ self.data, str
80
+ ), "Encoded payload is not None or a string"
81
81
  return self.data
82
82
 
83
83
  @encodedPayload.setter
84
84
  def encodedPayload(self, val: Optional[str]):
85
85
  # This value should only be set from users of VortexPy and
86
86
  # Payload.toEncodedPayload so we only allow str or None
87
- assert val is None or isinstance(val, str), (
88
- "Encoded payload is not None or a string"
89
- )
87
+ assert val is None or isinstance(
88
+ val, str
89
+ ), "Encoded payload is not None or a string"
90
90
  self.data = val
91
91
 
92
92
  def isEmpty(self):
@@ -8,6 +8,7 @@
8
8
 
9
9
  import logging
10
10
  from datetime import datetime
11
+ from typing import Optional
11
12
 
12
13
  import pytz
13
14
  from twisted.internet import reactor
@@ -59,8 +60,8 @@ class PayloadIO(object):
59
60
  payloadEnvelope: PayloadEnvelope,
60
61
  vortexUuid: str,
61
62
  vortexName: str,
62
- vortexSession,
63
- sendResponse: SendVortexMsgResponseCallable,
63
+ vortexSession=None,
64
+ sendResponse: Optional[SendVortexMsgResponseCallable] = None,
64
65
  ):
65
66
  from vortex.VortexFactory import VortexFactory
66
67
 
@@ -95,8 +96,8 @@ class PayloadIO(object):
95
96
  payloadEnvelope: PayloadEnvelope,
96
97
  vortexUuid: str,
97
98
  vortexName: str,
98
- vortexSession,
99
- sendResponse: SendVortexMsgResponseCallable,
99
+ vortexSession=None,
100
+ sendResponse: Optional[SendVortexMsgResponseCallable] = None,
100
101
  ):
101
102
  startDate = datetime.now(pytz.utc)
102
103
 
@@ -106,6 +107,10 @@ class PayloadIO(object):
106
107
  common function
107
108
  """
108
109
  try:
110
+ assert sendResponse is not None, (
111
+ "PayloadIO._processLater: sendResponse must be set"
112
+ " to respond to an exception"
113
+ )
109
114
  sendResponse(
110
115
  PayloadEnvelope(
111
116
  filt=payloadEnvelope.filt,
@@ -115,15 +120,8 @@ class PayloadIO(object):
115
120
  except Exception as e:
116
121
  logger.exception(e)
117
122
 
118
- if endpoint._logProcessException:
119
- vortexLogFailure(failure, logger)
120
- logger.error(payloadEnvelope.filt)
121
- else:
122
- logger.debug(
123
- "Payload handler response for filt=%s: %s",
124
- payloadEnvelope.filt,
125
- failure.getErrorMessage(),
126
- )
123
+ vortexLogFailure(failure, logger)
124
+ logger.error(payloadEnvelope.filt)
127
125
 
128
126
  def callback(value, blocking=False):
129
127
  # A blocking call taking more than 100ms is BAD
@@ -7,4 +7,5 @@
7
7
  # Support : contact@flyingion.com
8
8
 
9
9
  RPC_PRIORITY = 10
10
+ HEARTBEAT_PRIORITY = 20
10
11
  DEFAULT_PRIORITY = 100
@@ -67,7 +67,7 @@ class PayloadResponse(Deferred):
67
67
  destVortexUuid: Optional[str] = None,
68
68
  timeout: Optional[float] = None,
69
69
  resultCheck=True,
70
- logTimeoutException=True,
70
+ logTimeoutError=True,
71
71
  ) -> None:
72
72
  """Constructor
73
73
 
@@ -81,8 +81,6 @@ class PayloadResponse(Deferred):
81
81
  :param timeout The timeout to wait for a response
82
82
  :param resultCheck Should the response payload.result be checked, if it fails
83
83
  it errback will be called.
84
- :param logTimeoutException: When True, payload timeouts log at ERROR.
85
- When False, timeouts log at DEBUG with neutral wording.
86
84
  """
87
85
  Deferred.__init__(self)
88
86
 
@@ -90,7 +88,7 @@ class PayloadResponse(Deferred):
90
88
  timeout = self.TIMEOUT
91
89
 
92
90
  self._resultCheck = resultCheck
93
- self._logTimeoutException = logTimeoutException
91
+ self._logTimeoutError = logTimeoutError
94
92
 
95
93
  # uuid4 can have collisions
96
94
  self._messageId = str(uuid4()) + str(PayloadResponse.__SEQ)
@@ -144,10 +142,8 @@ class PayloadResponse(Deferred):
144
142
  self._endpoint.shutdown()
145
143
  self._endpoint = None
146
144
 
147
- if self._logTimeoutException:
145
+ if self._logTimeoutError:
148
146
  logger.error("Timed out for payload %s", self._filt)
149
- else:
150
- logger.debug("Payload response timed out for %s", self._filt)
151
147
  self._status = self.TIMED_OUT
152
148
  return failure
153
149
 
@@ -157,7 +153,6 @@ class PayloadResponse(Deferred):
157
153
  vortexUuid: str,
158
154
  vortexName: str,
159
155
  sendResponse=None,
160
- vortexSession=None,
161
156
  ):
162
157
  if self._endpoint:
163
158
  self._endpoint.shutdown()