vortexpy 4.3.6__tar.gz → 6.0.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-6.0.1/LICENSE +22 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/PKG-INFO +10 -5
- {vortexpy-4.3.6 → vortexpy-6.0.1}/README.md +9 -4
- {vortexpy-4.3.6 → vortexpy-6.0.1}/pyproject.toml +1 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadEndpoint.py +26 -16
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadEndpointTest.py +0 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadIO.py +9 -4
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadPriority.py +1 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadResponse.py +0 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/Tuple.py +6 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexAccessControlContextABC.py +47 -13
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexAuthenticatorABC.py +6 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexFactory.py +0 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexPayloadProtocol.py +2 -3
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServer.py +20 -4
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServerConnection.py +2 -2
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServerWebsocket.py +4 -2
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServer_test.py +15 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexSession.py +62 -9
- vortexpy-6.0.1/vortex/VortexUserDetailsABC.py +40 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexWritePushProducer_test.py +2 -2
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/__init__.py +1 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/data_loader/TupleDataLiveLockManager.py +147 -65
- vortexpy-6.0.1/vortex/data_loader/TupleDataLiveLockManager_test.py +336 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/data_loader/TupleDataLoader.py +32 -41
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/data_loader/TupleDataLoaderTuples.py +13 -9
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleActionProcessor.py +51 -34
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleActionProcessorProxy.py +7 -6
- vortexpy-6.0.1/vortex/handler/TupleActionProcessor_test.py +147 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObservableHandler.py +69 -69
- vortexpy-6.0.1/vortex/handler/TupleDataObservableHandler_test.py +488 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObservableProxyHandler.py +40 -36
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObserverClient.py +0 -1
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/rpc/RPC.py +5 -3
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/sqla_orm/OrmCrudHandler.py +30 -7
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortexpy.egg-info/PKG-INFO +10 -5
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortexpy.egg-info/SOURCES.txt +4 -0
- vortexpy-4.3.6/vortex/handler/TupleDataObservableHandler_test.py +0 -392
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/TwistedDeferredTimeout.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/run_http_upgraded_websocket_ping_client.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/run_http_upgraded_websocket_ping_server.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/run_http_upgraded_websocket_rpc_test_client.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/run_http_upgraded_websocket_rpc_test_server.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/run_websocket_rpc_test_client.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/run_websocket_rpc_test_server.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/setup_example_logging.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/example/store_and_load_json.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/setup.cfg +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/DataWrapTuple.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/DeferUtil.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/DeferUtil_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/Jsonable.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/MtlsAuthorizedClientRegistry.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/MtlsAuthorizedClientRegistry_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/MtlsServerSslOptions.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/Payload.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadEnvelope.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadFilterKeys.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadResponseTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadResponse_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PayloadTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/PerformTestActionTuple.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/SerialiseUtil.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/SerialiseUtil_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TestTuple.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleAction.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleBson.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleBson_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleDataForTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleDocumentConverter.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TuplePerfTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleRestful.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleRestful_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleSelector.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/TupleTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexClientHttp.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexClientHttpTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexClientTcp.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexClientWebsocketFactory.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexConnectionABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexEndpointPermission.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexFactory_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexPermission.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexPermissionError.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServerConnection_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServerHttpResource.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServerTcp.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexServerWebsocket_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexSession_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexTcpMemoryLeakPayloadTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexTcpMemoryLeakRawTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexUtil.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/VortexWritePushProducer.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/data_loader/TupleDataLoaderDelegate.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/data_loader/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/event_bus/TupleChangeEventABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/event_bus/TupleChangeEventBus.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/event_bus/TupleChangeEventBusObserverABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/event_bus/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleActionProcessorTestHandler.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleActionProcessor_shutdown_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleActionPushOffline.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleActionPushOffline_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataAccessGranterABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataActionClient.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObservableCache.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObservableCache_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObservableProxyHandler_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataObservableTestHandler.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/TupleDataProviderABC.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/VortexJSTupleLoaderTestHandler.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/handler/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/mypy_plugin.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/payload_many_tuples_benchmark_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/py.typed +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/GzippedDataHttpClient.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/GzippedDataHttpClientTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/GzippedPayloadHttpClient.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/RestfulHttpClient.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/RestfulResource.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/restful_http_client_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/restful/restful_resource_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/rpc/RPCTest.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/rpc/RPC_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/rpc/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/sqla_orm/TupleProviderForDb.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/sqla_orm/VortexTupleJson.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/sqla_orm/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/storage/TupleActionStorageSqlite.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/storage/TupleStorageSqlite.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/storage/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_json_schema/__init__.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_json_schema/constants.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_json_schema/json_schema_node_tuple.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_json_schema/tuple_json_schema_builder.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_json_schema/tuple_json_schema_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_json_schema/tuple_json_schema_test_tuples.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/tuple_test_fixtures.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/twisted_runtime_metrics.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortex/twisted_runtime_metrics_test.py +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortexpy.egg-info/dependency_links.txt +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortexpy.egg-info/entry_points.txt +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortexpy.egg-info/requires.txt +0 -0
- {vortexpy-4.3.6 → vortexpy-6.0.1}/vortexpy.egg-info/top_level.txt +0 -0
vortexpy-6.0.1/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:
|
|
3
|
+
Version: 6.0.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
6
|
License: MIT
|
|
@@ -32,7 +32,7 @@ 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
|
|
35
|
+
system (endpoint guards + access control context) introduced in v6.0.
|
|
36
36
|
|
|
37
37
|
**Tuple and Payload tutorials:** [docs/README.md](docs/README.md) (simple → full features, encode/decode, threaded decode).
|
|
38
38
|
|
|
@@ -40,9 +40,14 @@ Why PY at the end? To be consistent with VortexJS, which is the Browser side pac
|
|
|
40
40
|
See https://gitlab.com/flyingion/vortexjs
|
|
41
41
|
|
|
42
42
|
Requirements:
|
|
43
|
-
* Python 3.
|
|
44
|
-
* Twisted
|
|
45
|
-
* SQLAlchemy
|
|
43
|
+
* Python 3.12 (development target)
|
|
44
|
+
* Twisted (autobahn / txwebsocket)
|
|
45
|
+
* SQLAlchemy >= 2.0
|
|
46
|
+
|
|
47
|
+
See `pyproject.toml` for the authoritative dependency list.
|
|
48
|
+
|
|
49
|
+
See [README_MIGRATION.md](README_MIGRATION.md) for breaking changes and
|
|
50
|
+
migration guides (4.4.0, 6.0.0).
|
|
46
51
|
|
|
47
52
|
The "vortex" is designed to transport "Payloads" from a web browser (VortexJS) to a
|
|
48
53
|
twisted web server (VortexPY). There is also a python client for python client to python server
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
system (endpoint guards + access control context) introduced in v6.0.
|
|
7
7
|
|
|
8
8
|
**Tuple and Payload tutorials:** [docs/README.md](docs/README.md) (simple → full features, encode/decode, threaded decode).
|
|
9
9
|
|
|
@@ -11,9 +11,14 @@ Why PY at the end? To be consistent with VortexJS, which is the Browser side pac
|
|
|
11
11
|
See https://gitlab.com/flyingion/vortexjs
|
|
12
12
|
|
|
13
13
|
Requirements:
|
|
14
|
-
* Python 3.
|
|
15
|
-
* Twisted
|
|
16
|
-
* SQLAlchemy
|
|
14
|
+
* Python 3.12 (development target)
|
|
15
|
+
* Twisted (autobahn / txwebsocket)
|
|
16
|
+
* SQLAlchemy >= 2.0
|
|
17
|
+
|
|
18
|
+
See `pyproject.toml` for the authoritative dependency list.
|
|
19
|
+
|
|
20
|
+
See [README_MIGRATION.md](README_MIGRATION.md) for breaking changes and
|
|
21
|
+
migration guides (4.4.0, 6.0.0).
|
|
17
22
|
|
|
18
23
|
The "vortex" is designed to transport "Payloads" from a web browser (VortexJS) to a
|
|
19
24
|
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 = "
|
|
7
|
+
version = "6.0.1"
|
|
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."
|
|
@@ -28,26 +28,14 @@ from vortex.VortexEndpointPermission import (
|
|
|
28
28
|
from vortex.VortexSession import (
|
|
29
29
|
_callableLabel,
|
|
30
30
|
checkAccessControlContextSignature,
|
|
31
|
+
checkEndpointPermissionsSignature,
|
|
31
32
|
checkNoHttpSessionInSignature,
|
|
33
|
+
checkNoVortexSessionInSignature,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
logger = logging.getLogger(__name__)
|
|
35
37
|
|
|
36
38
|
|
|
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
39
|
class PayloadEndpoint:
|
|
52
40
|
"""
|
|
53
41
|
The payload endpoint is responsible for matching payloads filters against
|
|
@@ -103,7 +91,14 @@ class PayloadEndpoint:
|
|
|
103
91
|
callableDescription=callableDesc,
|
|
104
92
|
)
|
|
105
93
|
)
|
|
94
|
+
self._callableAcceptsEndpointPermissions = (
|
|
95
|
+
checkEndpointPermissionsSignature(
|
|
96
|
+
callable_,
|
|
97
|
+
callableDescription=callableDesc,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
106
100
|
checkNoHttpSessionInSignature(callable_, callableDesc)
|
|
101
|
+
checkNoVortexSessionInSignature(callable_, callableDesc)
|
|
107
102
|
|
|
108
103
|
self._acceptOnlyFromVortex = acceptOnlyFromVortex
|
|
109
104
|
if isinstance(self._acceptOnlyFromVortex, str):
|
|
@@ -233,6 +228,7 @@ class PayloadEndpoint:
|
|
|
233
228
|
|
|
234
229
|
# Level 1: Endpoint permission check
|
|
235
230
|
auth = VortexFactory.getAuthenticator()
|
|
231
|
+
endpointPermissions = None
|
|
236
232
|
if self._requiredPermissions:
|
|
237
233
|
if not auth:
|
|
238
234
|
logger.error(
|
|
@@ -259,6 +255,19 @@ class PayloadEndpoint:
|
|
|
259
255
|
)
|
|
260
256
|
return
|
|
261
257
|
|
|
258
|
+
# Level 1 injection: when the callable declares endpointPermissions,
|
|
259
|
+
# compute the caller's permissions once here at the boundary so
|
|
260
|
+
# handlers never see the session or the authenticator.
|
|
261
|
+
if (
|
|
262
|
+
self._callableAcceptsEndpointPermissions
|
|
263
|
+
and endpointPermissions is None
|
|
264
|
+
and auth
|
|
265
|
+
and vortexSession
|
|
266
|
+
):
|
|
267
|
+
endpointPermissions = yield maybeDeferred(
|
|
268
|
+
auth.getEndpointPermissions, vortexSession
|
|
269
|
+
)
|
|
270
|
+
|
|
262
271
|
# Level 2: Get access control context, but only if the callback uses it.
|
|
263
272
|
# Default to the sentinel so handlers always receive a non-None value
|
|
264
273
|
# with a clear error on attribute access when auth is unavailable.
|
|
@@ -283,15 +292,16 @@ class PayloadEndpoint:
|
|
|
283
292
|
|
|
284
293
|
callable_ = self._wref()
|
|
285
294
|
if callable_:
|
|
286
|
-
kwargs = dict(
|
|
295
|
+
kwargs: Dict[str, object] = dict(
|
|
287
296
|
payloadEnvelope=payloadEnvelope,
|
|
288
297
|
vortexUuid=vortexUuid,
|
|
289
298
|
vortexName=vortexName,
|
|
290
299
|
sendResponse=sendResponse,
|
|
291
|
-
vortexSession=vortexSession,
|
|
292
300
|
)
|
|
293
301
|
if self._callableAcceptsAccessControlContext:
|
|
294
302
|
kwargs["accessControlContext"] = accessControlContext
|
|
303
|
+
if self._callableAcceptsEndpointPermissions:
|
|
304
|
+
kwargs["endpointPermissions"] = endpointPermissions
|
|
295
305
|
return (yield maybeDeferred(callable_, **kwargs))
|
|
296
306
|
else:
|
|
297
307
|
PayloadIO().remove(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,
|
|
@@ -288,6 +288,12 @@ class TupleField:
|
|
|
288
288
|
class _Map:
|
|
289
289
|
pass
|
|
290
290
|
|
|
291
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
292
|
+
# Tuple classes declare fields as `field: str = TupleField()`.
|
|
293
|
+
# The Any return keeps those declarations type-correct without
|
|
294
|
+
# per-field type-ignore comments. Runtime behaviour is unchanged.
|
|
295
|
+
return super().__new__(cls)
|
|
296
|
+
|
|
291
297
|
def __init__(
|
|
292
298
|
self,
|
|
293
299
|
defaultValue=None,
|
|
@@ -6,8 +6,15 @@
|
|
|
6
6
|
# Website : https://flyingion.com
|
|
7
7
|
# Support : contact@flyingion.com
|
|
8
8
|
|
|
9
|
+
# ruff: noqa: N999
|
|
10
|
+
# Module names use PascalCase to match the primary class.
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
|
|
14
|
+
from vortex.VortexUserDetailsABC import VortexUserDetailsABC
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class VortexAccessControlContextABC(ABC):
|
|
11
18
|
"""Abstract base class for application-level access control contexts.
|
|
12
19
|
|
|
13
20
|
This is a **Level 2** permission mechanism. Where
|
|
@@ -16,23 +23,27 @@ class VortexAccessControlContextABC:
|
|
|
16
23
|
``VortexAccessControlContextABC`` answers "given access, what specific data
|
|
17
24
|
or actions is this user allowed to interact with?".
|
|
18
25
|
|
|
19
|
-
Subclass it and add domain-specific check
|
|
20
|
-
``super().__init__(vortexName, vortexUuid)`` to
|
|
21
|
-
metadata that the library passes through::
|
|
26
|
+
Subclass it, implement :meth:`userDetails`, and add domain-specific check
|
|
27
|
+
methods. Always call ``super().__init__(vortexName, vortexUuid)`` to
|
|
28
|
+
store the transport metadata that the library passes through::
|
|
22
29
|
|
|
23
|
-
class
|
|
30
|
+
class ExampleAccessControlContext(VortexAccessControlContextABC):
|
|
24
31
|
def __init__(self, user, vortexName: str, vortexUuid: str):
|
|
25
32
|
super().__init__(vortexName=vortexName, vortexUuid=vortexUuid)
|
|
26
33
|
self._user = user
|
|
27
34
|
|
|
35
|
+
@property
|
|
36
|
+
def userDetails(self):
|
|
37
|
+
return None
|
|
38
|
+
|
|
28
39
|
def canExecuteJob(self, job_id: int) -> bool:
|
|
29
40
|
return job_id in self._user.allowedJobIds
|
|
30
41
|
|
|
31
42
|
Obtain the context inside a processing method via the ``accessControlContext``
|
|
32
43
|
keyword argument that the library injects::
|
|
33
44
|
|
|
34
|
-
def processTupleAction(self, tupleAction, accessControlContext:
|
|
35
|
-
assert isinstance(accessControlContext,
|
|
45
|
+
def processTupleAction(self, tupleAction, accessControlContext: ExampleAccessControlContext):
|
|
46
|
+
assert isinstance(accessControlContext, ExampleAccessControlContext)
|
|
36
47
|
if not accessControlContext.canExecuteJob(tupleAction.jobId):
|
|
37
48
|
raise PermissionError("Not allowed")
|
|
38
49
|
|
|
@@ -46,10 +57,18 @@ class VortexAccessControlContextABC:
|
|
|
46
57
|
* ``vortexName`` — the name of the remote vortex that sent the request.
|
|
47
58
|
* ``vortexUuid`` — the per-connection UUID of that vortex.
|
|
48
59
|
|
|
60
|
+
**User identity**
|
|
61
|
+
|
|
62
|
+
Contexts must implement ``userDetails``, the identity of the logged-in
|
|
63
|
+
user provided by the host application (``None`` when no identity is
|
|
64
|
+
available). The library reads it to display who holds a live record
|
|
65
|
+
lock; it never mutates it.
|
|
66
|
+
|
|
49
67
|
When no authenticator is registered, or the session is unavailable, the
|
|
50
68
|
library passes :data:`NO_AUTH_ACCESS_CONTROL_CONTEXT` — a sentinel whose
|
|
51
|
-
``vortexName`` and ``vortexUuid`` are empty strings
|
|
52
|
-
access raises
|
|
69
|
+
``vortexName`` and ``vortexUuid`` are empty strings, whose ``userDetails``
|
|
70
|
+
is ``None``, and where any other attribute access raises
|
|
71
|
+
:class:`RuntimeError`.
|
|
53
72
|
|
|
54
73
|
To detect the no-auth case explicitly::
|
|
55
74
|
|
|
@@ -76,15 +95,26 @@ class VortexAccessControlContextABC:
|
|
|
76
95
|
def vortexUuid(self) -> str:
|
|
77
96
|
return self._vortexUuid
|
|
78
97
|
|
|
98
|
+
@property
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def userDetails(self) -> VortexUserDetailsABC | None:
|
|
101
|
+
"""Identity of the logged-in user for this session.
|
|
102
|
+
|
|
103
|
+
The library reads this to display who holds a live record lock; it
|
|
104
|
+
never mutates it. Return ``None`` when the host application does
|
|
105
|
+
not provide user identity.
|
|
106
|
+
"""
|
|
107
|
+
|
|
79
108
|
|
|
80
109
|
class _NoAuthAccessControlContext(VortexAccessControlContextABC):
|
|
81
110
|
"""Sentinel passed as ``accessControlContext`` when no authenticator is
|
|
82
111
|
registered or the session is unavailable.
|
|
83
112
|
|
|
84
|
-
``vortexName`` and ``vortexUuid`` return empty strings
|
|
85
|
-
attribute access raises a descriptive
|
|
86
|
-
in handlers are caught immediately
|
|
87
|
-
a bare ``AttributeError`` on
|
|
113
|
+
``vortexName`` and ``vortexUuid`` return empty strings and ``userDetails``
|
|
114
|
+
returns ``None``. Any other attribute access raises a descriptive
|
|
115
|
+
:class:`RuntimeError` so that bugs in handlers are caught immediately
|
|
116
|
+
with an actionable message rather than a bare ``AttributeError`` on
|
|
117
|
+
``None``.
|
|
88
118
|
"""
|
|
89
119
|
|
|
90
120
|
def __init__(self):
|
|
@@ -93,6 +123,10 @@ class _NoAuthAccessControlContext(VortexAccessControlContextABC):
|
|
|
93
123
|
object.__setattr__(self, "_vortexName", "")
|
|
94
124
|
object.__setattr__(self, "_vortexUuid", "")
|
|
95
125
|
|
|
126
|
+
@property
|
|
127
|
+
def userDetails(self) -> None:
|
|
128
|
+
return None
|
|
129
|
+
|
|
96
130
|
def __getattr__(self, name: str):
|
|
97
131
|
raise RuntimeError(
|
|
98
132
|
f"accessControlContext.{name} was accessed, but no authenticator is "
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
# Website : https://flyingion.com
|
|
7
7
|
# Support : contact@flyingion.com
|
|
8
8
|
|
|
9
|
+
# ruff: noqa: N999
|
|
10
|
+
# Module names use PascalCase to match the primary class.
|
|
11
|
+
|
|
9
12
|
from abc import ABC, abstractmethod
|
|
10
13
|
|
|
11
14
|
from twisted.internet.defer import Deferred
|
|
12
15
|
|
|
13
|
-
from vortex.VortexEndpointPermission import EndpointPermissionsType
|
|
14
16
|
from vortex.VortexSession import VortexSession
|
|
15
17
|
|
|
16
18
|
|
|
@@ -82,6 +84,9 @@ class VortexAuthenticatorABC(ABC):
|
|
|
82
84
|
def __init__(self, user, vortexName: str, vortexUuid: str):
|
|
83
85
|
super().__init__(vortexName=vortexName, vortexUuid=vortexUuid)
|
|
84
86
|
self._user = user
|
|
87
|
+
@property
|
|
88
|
+
def userDetails(self):
|
|
89
|
+
return None
|
|
85
90
|
def canEditRecord(self, record_id: int) -> bool:
|
|
86
91
|
return record_id in self._user.editableRecordIds
|
|
87
92
|
|
|
@@ -155,14 +155,13 @@ class VortexPayloadProtocol(Protocol, metaclass=ABCMeta):
|
|
|
155
155
|
def _deliverPayload(self, payloadEnvelope):
|
|
156
156
|
"""Deliver a decoded payload envelope to the PayloadIO for processing.
|
|
157
157
|
|
|
158
|
-
Subclasses (e.g. VortexTcpServerProtocol) may override this to
|
|
159
|
-
|
|
158
|
+
Subclasses (e.g. VortexTcpServerProtocol) may override this to pass the
|
|
159
|
+
connection vortexSession or wait for authentication before dispatching.
|
|
160
160
|
"""
|
|
161
161
|
PayloadIO().process(
|
|
162
162
|
payloadEnvelope,
|
|
163
163
|
vortexUuid=self._serverVortexUuid,
|
|
164
164
|
vortexName=self._serverVortexName,
|
|
165
|
-
vortexSession=None,
|
|
166
165
|
sendResponse=self._createResponseSenderCallable(),
|
|
167
166
|
)
|
|
168
167
|
|
|
@@ -138,6 +138,23 @@ class VortexServer(VortexABC):
|
|
|
138
138
|
|
|
139
139
|
return "%s:%s" % (connection.ip, connection.port)
|
|
140
140
|
|
|
141
|
+
def httpSessionForVortexUuid(self, vortexUuid: str) -> Optional[Session]:
|
|
142
|
+
"""HTTP Session for Vortex UUID
|
|
143
|
+
|
|
144
|
+
Return the HTTP session associated with the connection for
|
|
145
|
+
*vortexUuid*, or ``None`` when the vortex is not connected or has no
|
|
146
|
+
registered session. For TCP connections this is the
|
|
147
|
+
:class:`~vortex.VortexSession.VortexTcpSession` stub stored at
|
|
148
|
+
connection time.
|
|
149
|
+
|
|
150
|
+
:param vortexUuid: The per-connection UUID of the remote vortex.
|
|
151
|
+
:return: The session for the connection, or ``None``.
|
|
152
|
+
"""
|
|
153
|
+
connection = self._connectionByVortexUuid.get(vortexUuid)
|
|
154
|
+
if connection is None:
|
|
155
|
+
return None
|
|
156
|
+
return self._httpSessionsBySessionUuid.get(connection.httpSessionUuid)
|
|
157
|
+
|
|
141
158
|
def connectionOpened(self, httpSession, vortexConnection):
|
|
142
159
|
if self._DEBUG_LOGGING:
|
|
143
160
|
logger.debug("VortexServer - connectionOpened")
|
|
@@ -154,9 +171,9 @@ class VortexServer(VortexABC):
|
|
|
154
171
|
)
|
|
155
172
|
|
|
156
173
|
httpSessionConns = VortexSessionI(httpSession).connections
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
174
|
+
existingConn = httpSessionConns.get(vortexUuid)
|
|
175
|
+
if existingConn is not None and existingConn is not vortexConnection:
|
|
176
|
+
existingConn.close()
|
|
160
177
|
httpSessionConns[vortexUuid] = vortexConnection
|
|
161
178
|
|
|
162
179
|
if vortexUuid in self._connectionByVortexUuid:
|
|
@@ -304,7 +321,6 @@ class VortexServer(VortexABC):
|
|
|
304
321
|
payloadEnvelope,
|
|
305
322
|
vortexUuid=self._uuid,
|
|
306
323
|
vortexName=self._name,
|
|
307
|
-
vortexSession=None,
|
|
308
324
|
sendResponse=sendResponse,
|
|
309
325
|
)
|
|
310
326
|
|
|
@@ -12,7 +12,7 @@ from datetime import datetime
|
|
|
12
12
|
import pytz
|
|
13
13
|
from twisted.internet import task
|
|
14
14
|
|
|
15
|
-
from .PayloadPriority import DEFAULT_PRIORITY
|
|
15
|
+
from .PayloadPriority import DEFAULT_PRIORITY, HEARTBEAT_PRIORITY
|
|
16
16
|
from .VortexConnectionABC import VortexConnectionABC
|
|
17
17
|
from .VortexServer import VortexServer, HEART_BEAT_PERIOD, HEART_BEAT_TIMEOUT
|
|
18
18
|
from .VortexWritePushProducer import VortexWritePushProducer
|
|
@@ -94,7 +94,7 @@ class VortexServerConnection(VortexConnectionABC):
|
|
|
94
94
|
self.close()
|
|
95
95
|
return
|
|
96
96
|
|
|
97
|
-
self._write(b".",
|
|
97
|
+
self._write(b".", HEARTBEAT_PRIORITY)
|
|
98
98
|
|
|
99
99
|
@property
|
|
100
100
|
def ip(self):
|
|
@@ -336,7 +336,6 @@ class VortexWebSocketUpgradeResource(resource.Resource):
|
|
|
336
336
|
request.channel.upgradeToWebsocket(websocketProtocol)
|
|
337
337
|
|
|
338
338
|
def unregister():
|
|
339
|
-
request.channel.unregisterProducer()
|
|
340
339
|
websocketProtocol.unregisterProducer()
|
|
341
340
|
|
|
342
341
|
producer = VortexWritePushProducer(
|
|
@@ -351,7 +350,10 @@ class VortexWebSocketUpgradeResource(resource.Resource):
|
|
|
351
350
|
producer.ERROR_DATA_LENGTH = 50 * 1024 * 1024
|
|
352
351
|
|
|
353
352
|
unregister()
|
|
354
|
-
|
|
353
|
+
# Register only on the WebSocket protocol. Registering the same producer
|
|
354
|
+
# on HTTPChannel._requestProducer as well lets HTTPChannel.pauseProducing
|
|
355
|
+
# stall outbound traffic without a matching resume while the websocket
|
|
356
|
+
# request is still open (NOT_DONE_YET).
|
|
355
357
|
websocketProtocol.registerProducer(producer, streaming=True)
|
|
356
358
|
|
|
357
359
|
def closedError(failure):
|
|
@@ -84,12 +84,26 @@ class VortexServerConnectionClosedTest(SynchronousTestCase):
|
|
|
84
84
|
server.connectionOpened(session, conn_a)
|
|
85
85
|
server.connectionOpened(session, conn_b)
|
|
86
86
|
|
|
87
|
+
# A new connection with a different vortexUuid must not close the
|
|
88
|
+
# existing same-session connection (two browser tabs stay live).
|
|
87
89
|
conns = VortexSessionI(session).connections
|
|
90
|
+
self.assertEqual(conns.get("uuid-a"), conn_a)
|
|
91
|
+
self.assertEqual(conns.get("uuid-b"), conn_b)
|
|
92
|
+
|
|
93
|
+
# Reconnecting with the same vortexUuid replaces the old connection.
|
|
94
|
+
conn_a2 = _MockServerConnection(
|
|
95
|
+
server, "uuid-a", "peek-office-app", session
|
|
96
|
+
)
|
|
97
|
+
server.connectionOpened(session, conn_a2)
|
|
98
|
+
|
|
99
|
+
self.assertIs(conns.get("uuid-a"), conn_a2)
|
|
88
100
|
self.assertEqual(conns.get("uuid-b"), conn_b)
|
|
89
|
-
self.assertNotIn("uuid-a", conns)
|
|
90
101
|
|
|
102
|
+
# The replaced connection's teardown is idempotent and does not
|
|
103
|
+
# disturb the surviving connections.
|
|
91
104
|
server.connectionClosed(conn_a)
|
|
92
105
|
|
|
106
|
+
self.assertIs(conns.get("uuid-a"), conn_a2)
|
|
93
107
|
self.assertEqual(conns.get("uuid-b"), conn_b)
|
|
94
108
|
self.assertIn("uuid-b", server._connectionByVortexUuid)
|
|
95
109
|
|
|
@@ -181,28 +181,81 @@ def checkAccessControlContextSignature(
|
|
|
181
181
|
return True
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def
|
|
185
|
-
|
|
184
|
+
def checkEndpointPermissionsSignature(
|
|
185
|
+
callable_,
|
|
186
|
+
callableDescription: str = "",
|
|
187
|
+
) -> bool:
|
|
188
|
+
"""Check whether *callable_* accepts an ``endpointPermissions`` parameter.
|
|
186
189
|
|
|
187
|
-
Called at registration time
|
|
188
|
-
|
|
190
|
+
Called at **registration time** (not call time) so that the boundary knows
|
|
191
|
+
whether to compute and inject the caller's endpoint permissions frozenset.
|
|
192
|
+
|
|
193
|
+
:param callable_: The callback to inspect.
|
|
194
|
+
:param callableDescription: Human-readable description for log messages.
|
|
195
|
+
:return: ``True`` if the callable declares an explicit
|
|
196
|
+
``endpointPermissions`` parameter, ``False`` otherwise.
|
|
189
197
|
"""
|
|
198
|
+
return checkAccessControlContextSignature(
|
|
199
|
+
callable_,
|
|
200
|
+
paramName="endpointPermissions",
|
|
201
|
+
callableDescription=callableDescription,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _checkNoRemovedParamInSignature(
|
|
206
|
+
callable_,
|
|
207
|
+
paramName: str,
|
|
208
|
+
replacementHint: str,
|
|
209
|
+
callableDescription: str = "",
|
|
210
|
+
):
|
|
211
|
+
"""Log an ERROR if *callable_* declares a removed parameter explicitly."""
|
|
190
212
|
try:
|
|
191
213
|
sig = inspect.signature(callable_)
|
|
192
214
|
except (ValueError, TypeError):
|
|
193
215
|
return
|
|
194
216
|
|
|
195
|
-
if
|
|
196
|
-
param = sig.parameters[
|
|
217
|
+
if paramName in sig.parameters:
|
|
218
|
+
param = sig.parameters[paramName]
|
|
197
219
|
if param.kind != inspect.Parameter.VAR_KEYWORD:
|
|
198
220
|
logger.error(
|
|
199
|
-
"CRITICAL FIX REQUIRED: %s declares '
|
|
200
|
-
"removed in
|
|
201
|
-
"This callback will NOT receive '
|
|
221
|
+
"CRITICAL FIX REQUIRED: %s declares '%s' which has been "
|
|
222
|
+
"removed in v6.0.0. %s "
|
|
223
|
+
"This callback will NOT receive '%s'.",
|
|
202
224
|
callableDescription or _callableLabel(callable_),
|
|
225
|
+
paramName,
|
|
226
|
+
replacementHint,
|
|
227
|
+
paramName,
|
|
203
228
|
)
|
|
204
229
|
|
|
205
230
|
|
|
231
|
+
def checkNoHttpSessionInSignature(callable_, callableDescription: str = ""):
|
|
232
|
+
"""Log an ERROR if *callable_* declares httpSession as an explicit parameter.
|
|
233
|
+
|
|
234
|
+
Called at registration time to detect callbacks that still reference the
|
|
235
|
+
removed httpSession parameter.
|
|
236
|
+
"""
|
|
237
|
+
_checkNoRemovedParamInSignature(
|
|
238
|
+
callable_,
|
|
239
|
+
"httpSession",
|
|
240
|
+
"Use 'accessControlContext' instead.",
|
|
241
|
+
callableDescription,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def checkNoVortexSessionInSignature(callable_, callableDescription: str = ""):
|
|
246
|
+
"""Log an ERROR if *callable_* declares vortexSession as an explicit parameter.
|
|
247
|
+
|
|
248
|
+
Called at registration time to detect callbacks that still reference the
|
|
249
|
+
removed vortexSession parameter.
|
|
250
|
+
"""
|
|
251
|
+
_checkNoRemovedParamInSignature(
|
|
252
|
+
callable_,
|
|
253
|
+
"vortexSession",
|
|
254
|
+
"Use 'accessControlContext' or 'endpointPermissions' instead.",
|
|
255
|
+
callableDescription,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
206
259
|
def _callableLabel(callable_) -> str:
|
|
207
260
|
"""Return a human-readable label for a callable for error messages."""
|
|
208
261
|
if hasattr(callable_, "__qualname__"):
|