vortexpy 4.0.25__tar.gz → 4.0.28__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 (119) hide show
  1. {vortexpy-4.0.25 → vortexpy-4.0.28}/LICENSE +2 -1
  2. vortexpy-4.0.28/PKG-INFO +186 -0
  3. {vortexpy-4.0.25 → vortexpy-4.0.28}/README.md +9 -2
  4. vortexpy-4.0.28/pyproject.toml +55 -0
  5. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/DataWrapTuple.py +8 -8
  6. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/DeferUtil.py +26 -11
  7. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/Jsonable.py +7 -8
  8. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/Payload.py +8 -8
  9. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadEndpoint.py +107 -21
  10. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadEndpointTest.py +15 -16
  11. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadEnvelope.py +7 -8
  12. vortexpy-4.0.28/vortex/PayloadFilterKeys.py +14 -0
  13. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadIO.py +12 -12
  14. vortexpy-4.0.28/vortex/PayloadPriority.py +10 -0
  15. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadResponse.py +11 -1
  16. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadResponseTest.py +8 -0
  17. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/PayloadTest.py +8 -8
  18. vortexpy-4.0.28/vortex/PerformTestActionTuple.py +23 -0
  19. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/SerialiseUtil.py +7 -8
  20. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TestTuple.py +10 -16
  21. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/Tuple.py +28 -266
  22. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TupleAction.py +8 -8
  23. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TupleDataForTest.py +8 -8
  24. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TuplePerfTest.py +8 -0
  25. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +31 -3
  26. vortexpy-4.0.28/vortex/TupleRestful.py +563 -0
  27. vortexpy-4.0.28/vortex/TupleRestfulTest.py +267 -0
  28. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TupleSelector.py +8 -8
  29. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/TupleTest.py +7 -8
  30. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexABC.py +8 -0
  31. vortexpy-4.0.28/vortex/VortexAccessControlContextABC.py +110 -0
  32. vortexpy-4.0.28/vortex/VortexAuthenticatorABC.py +152 -0
  33. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexClientHttp.py +8 -8
  34. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexClientHttpTest.py +8 -0
  35. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexClientTcp.py +8 -8
  36. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexClientWebsocketFactory.py +7 -8
  37. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexConnectionABC.py +7 -8
  38. vortexpy-4.0.28/vortex/VortexEndpointPermission.py +40 -0
  39. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexFactory.py +30 -2
  40. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexPayloadProtocol.py +25 -24
  41. vortexpy-4.0.28/vortex/VortexPermission.py +17 -0
  42. vortexpy-4.0.28/vortex/VortexPermissionError.py +40 -0
  43. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexServer.py +11 -10
  44. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexServerConnection.py +8 -8
  45. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexServerHttpResource.py +20 -8
  46. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexServerTcp.py +37 -8
  47. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexServerWebsocket.py +28 -8
  48. vortexpy-4.0.28/vortex/VortexSession.py +220 -0
  49. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexTcpMemoryLeakPayloadTest.py +11 -9
  50. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexTcpMemoryLeakRawTest.py +8 -8
  51. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexTest.py +7 -8
  52. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexUtil.py +8 -0
  53. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/VortexWritePushProducer.py +8 -8
  54. vortexpy-4.0.28/vortex/__init__.py +9 -0
  55. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/data_loader/TupleDataLiveLockManager.py +12 -5
  56. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/data_loader/TupleDataLoader.py +96 -31
  57. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/data_loader/TupleDataLoaderDelegate.py +31 -12
  58. vortexpy-4.0.28/vortex/data_loader/TupleDataLoaderTupleABC.py +18 -0
  59. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/data_loader/TupleDataLoaderTuples.py +8 -0
  60. vortexpy-4.0.28/vortex/data_loader/__init__.py +8 -0
  61. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/event_bus/TupleChangeEventABC.py +8 -0
  62. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/event_bus/TupleChangeEventBus.py +8 -0
  63. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/event_bus/TupleChangeEventBusObserverABC.py +8 -0
  64. vortexpy-4.0.28/vortex/event_bus/__init__.py +8 -0
  65. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleActionProcessor.py +143 -40
  66. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleActionProcessorProxy.py +10 -1
  67. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleActionProcessorTestHandler.py +8 -0
  68. vortexpy-4.0.28/vortex/handler/TupleDataAccessGranterABC.py +48 -0
  69. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleDataActionClient.py +8 -0
  70. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleDataObservableCache.py +18 -1
  71. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleDataObservableHandler.py +138 -22
  72. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleDataObservableProxyHandler.py +124 -10
  73. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleDataObservableTestHandler.py +18 -6
  74. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/TupleDataObserverClient.py +9 -0
  75. vortexpy-4.0.28/vortex/handler/TupleDataProviderABC.py +39 -0
  76. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/handler/VortexJSTupleLoaderTestHandler.py +8 -0
  77. vortexpy-4.0.28/vortex/handler/__init__.py +8 -0
  78. vortexpy-4.0.28/vortex/mypy_plugin.py +57 -0
  79. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/GzippedDataHttpClient.py +8 -0
  80. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/GzippedDataHttpClientTest.py +8 -0
  81. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/GzippedPayloadHttpClient.py +9 -1
  82. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/RestfulHttpClient.py +9 -1
  83. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/RestfulHttpClientTest.py +8 -0
  84. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/RestfulResource.py +8 -0
  85. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/restful/RestfulResourceTest.py +8 -0
  86. vortexpy-4.0.28/vortex/restful/__init__.py +8 -0
  87. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/rpc/RPC.py +42 -4
  88. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/rpc/RPCTest.py +8 -0
  89. vortexpy-4.0.28/vortex/rpc/__init__.py +8 -0
  90. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/sqla_orm/OrmCrudHandler.py +11 -9
  91. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/sqla_orm/TupleProviderForDb.py +17 -3
  92. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/sqla_orm/VortexTupleJson.py +8 -0
  93. vortexpy-4.0.28/vortex/sqla_orm/__init__.py +8 -0
  94. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortex/storage/TupleStorageSqlite.py +8 -0
  95. vortexpy-4.0.28/vortex/storage/__init__.py +8 -0
  96. vortexpy-4.0.28/vortexpy.egg-info/PKG-INFO +186 -0
  97. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortexpy.egg-info/SOURCES.txt +12 -1
  98. vortexpy-4.0.28/vortexpy.egg-info/entry_points.txt +2 -0
  99. vortexpy-4.0.25/PKG-INFO +0 -44
  100. vortexpy-4.0.25/pyproject.toml +0 -45
  101. vortexpy-4.0.25/vortex/PayloadFilterKeys.py +0 -15
  102. vortexpy-4.0.25/vortex/PayloadPriority.py +0 -11
  103. vortexpy-4.0.25/vortex/PerformTestActionTuple.py +0 -30
  104. vortexpy-4.0.25/vortex/TupleActionVortex.py +0 -5
  105. vortexpy-4.0.25/vortex/TupleRestfulTest.py +0 -140
  106. vortexpy-4.0.25/vortex/__init__.py +0 -10
  107. vortexpy-4.0.25/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -10
  108. vortexpy-4.0.25/vortex/event_bus/__init__.py +0 -0
  109. vortexpy-4.0.25/vortex/handler/__init__.py +0 -0
  110. vortexpy-4.0.25/vortex/restful/__init__.py +0 -0
  111. vortexpy-4.0.25/vortex/rpc/__init__.py +0 -0
  112. vortexpy-4.0.25/vortex/sqla_orm/__init__.py +0 -0
  113. vortexpy-4.0.25/vortex/storage/__init__.py +0 -0
  114. vortexpy-4.0.25/vortexpy.egg-info/PKG-INFO +0 -44
  115. {vortexpy-4.0.25 → vortexpy-4.0.28}/setup.cfg +0 -0
  116. /vortexpy-4.0.25/vortex/data_loader/__init__.py → /vortexpy-4.0.28/vortex/py.typed +0 -0
  117. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortexpy.egg-info/dependency_links.txt +0 -0
  118. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortexpy.egg-info/requires.txt +0 -0
  119. {vortexpy-4.0.25 → vortexpy-4.0.28}/vortexpy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2016
3
+ Copyright (c) 2016-2026 Flying Ion Pty Ltd
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: vortexpy
3
+ Version: 4.0.28
4
+ Summary: Flying Ion's observable, routable, data serialisation and transport code.
5
+ Author-email: Flying Ion Pty Ltd <contact@flyingion.com>
6
+ License: MIT License
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
+
29
+ Project-URL: Homepage, https://flyingion.com
30
+ Project-URL: Repository, https://gitlab.com/flyingion/vortexpy
31
+ Project-URL: Issues, https://gitlab.com/flyingion/vortexpy/-/issues
32
+ Keywords: vortex,observable,http,compressed,flying-ion
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Programming Language :: Python :: 3.9
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Description-Content-Type: text/markdown
40
+ Requires-Dist: SQLAlchemy>=2.0
41
+ Requires-Dist: GeoAlchemy2
42
+ Requires-Dist: txwebsocket>=1.1.0
43
+ Requires-Dist: autobahn[twisted]
44
+ Requires-Dist: vcversioner
45
+ Requires-Dist: six
46
+ Requires-Dist: pytz
47
+ Requires-Dist: reactivex>=4.0.4
48
+ Requires-Dist: ddt>=1.6.0
49
+ Requires-Dist: psutil
50
+ Requires-Dist: txhttputil>=1.2.0
51
+
52
+ # VortexPY
53
+
54
+ Flying Ion's observable, routable, data serialisation and transport code.
55
+
56
+ See [README_PERMISSIONS.md](README_PERMISSIONS.md) for the two-level permission
57
+ system (endpoint guards + access control context) introduced in v5.0.
58
+
59
+ Why PY at the end? To be consistent with VortexJS, which is the Browser side package.
60
+ See https://gitlab.com/flyingion/vortexjs
61
+
62
+ Requirements:
63
+ * Python 3.5
64
+ * Twisted 16+
65
+ * SQLAlchemy 1, 1.1
66
+
67
+ The "vortex" is designed to transport "Payloads" from a web browser (VortexJS) to a
68
+ twisted web server (VortexPY). There is also a python client for python client to python server
69
+ communication.
70
+
71
+ ### Vortex, The Observable Part
72
+
73
+ Vortex was designed to allow the Python server to send updates to the browser with out
74
+ the browser having to create HTTP request polls.
75
+
76
+ This is achieved by the browser/client sending one or more payloads to the server as a
77
+ HTTP POST, the server then uses this connection to send payloads back to the client.
78
+
79
+ This HTTP POST connection remains active, Until the client needs
80
+ to send more Payloads to the server. To do this, the client simply starts a new HTTP POST
81
+ request to the server, the server will then close the old request and begin sending
82
+ Payloads down the respose path of the new request.
83
+
84
+ Python classes = Vortex
85
+
86
+ ### Transport
87
+
88
+ Transport occurs over a HTTP(S) layer.
89
+
90
+ Experimental classes are present to transport data to/from subprocess pipes, this
91
+ worked, except the subprocess didn't die and the idea was abandoned
92
+ (It was a Celery worker).
93
+
94
+ Because of this effort, it's much easier to add other transport mechanisms.
95
+
96
+ Python classes = VortexResource (Receive), VortexConnection (Send)
97
+
98
+ ### Payloads
99
+ A "Payload" is a class that stores some routing information, a result and a list of
100
+ "Tuples".
101
+
102
+ The routing information is a dict / json object which looks like this
103
+ {key:"anything.type.of.data.list"}
104
+ (See Routable for more info)
105
+
106
+ The tuples attribute stores the data, typically this is a list of Tuple (see Tuples)
107
+ objects, however, it can be any data structure that JSON can serialise,
108
+ plus Date()/datetime.
109
+
110
+ The Payload class is responsible for serialising and compressing it's data down to a
111
+ "Vortex Msg", ready for transport. Compression is done with zlib, this imrpovement sees
112
+ data reduced to 10% of the original size.
113
+
114
+ There is custom code that handles converting the structure to JSON objects for json.dumps
115
+ so support can be (carefully) added for other data types such as bytes.
116
+
117
+ Python classes = Payload
118
+
119
+ ### Tuples
120
+
121
+ Not to be confused with the built in Python tuple syntax (1, 2, 3), Tuples are registered
122
+ classes with registered attributes.
123
+
124
+ Vortex Tuples are important in the design as classes that inherit the vortex Tuple
125
+ class will reconstructed as the proper classes at the other end in python. They will have
126
+ onlt the fields registerd as TupleFields populated when they are reconstructed.
127
+
128
+ This is especially useful when using the SQLAlchemy ORM, as the Tuple code recognises
129
+ column definitions as TupleFields.
130
+
131
+ You can just assign session.query(MyTable).all() to payload.tuples, and the vortex will
132
+ know how to serialise.
133
+
134
+ From here you can send it do the browser, where the browser may edit some attributes of
135
+ the tuples, then send it back to the python server to update the database.
136
+ (More on this in Handlers)
137
+
138
+ Python classes = Tuple, TupleField
139
+
140
+ ### Routable
141
+
142
+ When a vortex receives a payload, it is sent to the PayloadIO
143
+
144
+ The PayloadIO, then passes it on to each PayloadEndpoint that has registered with it.
145
+
146
+ Payload routing ends with PayloadEndpoints, These endpoints are constructed with another
147
+ filter dict/json like object, and a callback.
148
+
149
+ When PayloadIO tells a PaylaodEndpoint that
150
+ a new Payload has arrived, the PayloadEndpoint checks the Payload.filt against it's own
151
+ filt.
152
+
153
+ If the PayloadEndpoints filt is a subset of the Payload.filt, the PayloadEndpoint then
154
+ calls it's callable and passes it the Payload.
155
+
156
+ PayloadEndpoints register them selves with PayloadIO on construction.
157
+ PayloadIO keeps a list of weak refs to the PayloadEndpoints.
158
+
159
+
160
+
161
+
162
+ Payloads contain a "filt" attribute, which is a json like dict/object.
163
+
164
+
165
+ Python classes = PayloadEndpoint, PayloadIO
166
+
167
+ ### Handlers
168
+
169
+ Handlers are the provided classes that handle data from the PayloadEndpoints.
170
+
171
+ * ModelHandler : Simply specify the payload filter and implement the "buildModel" method.
172
+ * OrmCrudHandler : This handler used to Create, Update, Retrieve and Delete SQLAlchemy data.
173
+ It can handle an array of tuples, of different types, and has various
174
+ hooks allowing the data to be customised before being stored or retreived.
175
+ * AsyncModelHandler : Experimental handler that will likely be deleted.
176
+
177
+
178
+ #Change Log
179
+
180
+ #### 0.3.0
181
+
182
+ Implemented PY side WebSockets
183
+
184
+ ## License
185
+
186
+ Copyright (c) 2016-2026 [Flying Ion Pty Ltd](https://flyingion.com). Released under the [MIT License](LICENSE).
@@ -1,9 +1,12 @@
1
1
  # VortexPY
2
2
 
3
- Synerty's observable, routable, data serialisation and transport code.
3
+ Flying Ion's observable, routable, data serialisation and transport code.
4
+
5
+ See [README_PERMISSIONS.md](README_PERMISSIONS.md) for the two-level permission
6
+ system (endpoint guards + access control context) introduced in v5.0.
4
7
 
5
8
  Why PY at the end? To be consistent with VortexJS, which is the Browser side package.
6
- See https://github.com/Synerty/vortexjs
9
+ See https://gitlab.com/flyingion/vortexjs
7
10
 
8
11
  Requirements:
9
12
  * Python 3.5
@@ -126,3 +129,7 @@ Handlers are the provided classes that handle data from the PayloadEndpoints.
126
129
  #### 0.3.0
127
130
 
128
131
  Implemented PY side WebSockets
132
+
133
+ ## License
134
+
135
+ Copyright (c) 2016-2026 [Flying Ion Pty Ltd](https://flyingion.com). Released under the [MIT License](LICENSE).
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vortexpy"
7
+ version = "4.0.28"
8
+ license = {file = "LICENSE"}
9
+ authors = [{name = "Flying Ion Pty Ltd", email = "contact@flyingion.com"}]
10
+ description = "Flying Ion's observable, routable, data serialisation and transport code."
11
+ readme = "README.md"
12
+ keywords = [
13
+ "vortex",
14
+ "observable",
15
+ "http",
16
+ "compressed",
17
+ "flying-ion",
18
+ ]
19
+ classifiers = [
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ ]
27
+ dependencies = [
28
+ "SQLAlchemy >= 2.0",
29
+ "GeoAlchemy2",
30
+ "txwebsocket>=1.1.0",
31
+ "autobahn[twisted]",
32
+ "vcversioner",
33
+ "six",
34
+ "pytz",
35
+ "reactivex>=4.0.4",
36
+ "ddt >=1.6.0",
37
+ "psutil",
38
+ "txhttputil>=1.2.0",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://flyingion.com"
43
+ Repository = "https://gitlab.com/flyingion/vortexpy"
44
+ Issues = "https://gitlab.com/flyingion/vortexpy/-/issues"
45
+
46
+ [tool.setuptools]
47
+ include-package-data = false
48
+ license-files = []
49
+
50
+ [tool.setuptools.packages.find]
51
+ exclude = ["test"]
52
+ namespaces = false
53
+
54
+ [project.entry-points."mypy.plugins"]
55
+ vortex_defer = "vortex.mypy_plugin:plugin"
@@ -1,11 +1,11 @@
1
- """
2
- * Created by Synerty Pty Ltd
3
- *
4
- * This software is open source, the MIT license applies.
5
- *
6
- * Website : http://www.synerty.com
7
- * Support : support@synerty.com
8
- """
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
9
  from .Tuple import Tuple, TupleField, addTupleType
10
10
 
11
11
 
@@ -1,5 +1,15 @@
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
+
1
9
  import inspect
2
10
  import logging
11
+ from collections.abc import Callable
12
+ from typing import ParamSpec, TypeVar
3
13
 
4
14
  import twisted
5
15
  from twisted.internet import reactor
@@ -13,6 +23,9 @@ from twisted.internet.threads import deferToThread
13
23
 
14
24
  logger = logging.getLogger(__name__)
15
25
 
26
+ _P = ParamSpec("_P")
27
+ _R = TypeVar("_R")
28
+
16
29
 
17
30
  def callMethodLater(func):
18
31
  def wrapper(*args, **kwargs):
@@ -136,19 +149,21 @@ class YieldInDeferToThreadException(Exception):
136
149
 
137
150
 
138
151
  def deferToThreadWrapWithLogger(
139
- logger, consumeError=False, checkMainThread=True
140
- ):
152
+ loggerArg: logging.Logger,
153
+ consumeError: bool = False,
154
+ checkMainThread: bool = True,
155
+ ) -> Callable[[Callable[_P, _R]], Callable[_P, Deferred[_R]]]:
141
156
  """Defer To Thread Wrap With Logger
142
157
 
143
158
  This method is a decorator used to send blocking methods to threads easily.
144
159
 
145
- :param logger: An instance from logging.getLogger(..)
160
+ :param loggerArg: An instance from logging.getLogger(..)
146
161
  :param consumeError: Should errors occuring in the thread be consumed or errback'd
147
162
  :param checkMainThread: Don't use this parameter, it's only needed during reactor
148
163
  startup when the reactors thread isn't properly set and the check
149
164
  fails.
150
165
 
151
- :return: A deferred.
166
+ :return: A decorator that makes the wrapped callable return a Deferred.
152
167
 
153
168
  Usage: ::
154
169
 
@@ -161,7 +176,7 @@ def deferToThreadWrapWithLogger(
161
176
 
162
177
  """
163
178
  assert isinstance(
164
- logger, logging.Logger
179
+ loggerArg, logging.Logger
165
180
  ), """Usage:
166
181
  import logging
167
182
  logger = logging.getLogger(__name__)
@@ -170,10 +185,10 @@ def deferToThreadWrapWithLogger(
170
185
  pass
171
186
  """
172
187
 
173
- def wrapper(funcToWrap) -> Deferred:
188
+ def wrapper(funcToWrap: Callable[_P, _R]) -> Callable[_P, Deferred[_R]]:
174
189
  def checkForInlineCallbackYield(result):
175
190
  if inspect.isgenerator(result):
176
- logger.error(
191
+ loggerArg.error(
177
192
  "Module Name %s, Class Name %s, Function Name %s"
178
193
  " has a yield in it. IT SHOULD NOT.",
179
194
  funcToWrap.__module__,
@@ -183,9 +198,9 @@ def deferToThreadWrapWithLogger(
183
198
  raise YieldInDeferToThreadException()
184
199
  return result
185
200
 
186
- def func(*args, **kwargs):
201
+ def func(*args: _P.args, **kwargs: _P.kwargs) -> Deferred[_R]:
187
202
  if not twisted.python.threadable.isInIOThread() and checkMainThread:
188
- logger.error(
203
+ loggerArg.error(
189
204
  "Module Name %s, Class Name %s, Function Name %s"
190
205
  " is being deferred from a thread. IT SHOULD NOT.",
191
206
  funcToWrap.__module__,
@@ -196,9 +211,9 @@ def deferToThreadWrapWithLogger(
196
211
  "Deferring to a thread can only be done from the main thread"
197
212
  )
198
213
 
199
- d = deferToThread(funcToWrap, *args, **kwargs)
214
+ d: Deferred[_R] = deferToThread(funcToWrap, *args, **kwargs)
200
215
  d.addCallback(checkForInlineCallbackYield)
201
- d.addErrback(vortexLogFailure, logger, consumeError=consumeError)
216
+ d.addErrback(vortexLogFailure, loggerArg, consumeError=consumeError)
202
217
  return d
203
218
 
204
219
  return func
@@ -1,11 +1,10 @@
1
- """
2
- * Created by Synerty Pty Ltd
3
- *
4
- * This software is open source, the MIT license applies.
5
- *
6
- * Website : http://www.synerty.com
7
- * Support : support@synerty.com
8
- """
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
9
8
 
10
9
  import logging
11
10
  import traceback
@@ -1,11 +1,11 @@
1
- """
2
- * Created by Synerty Pty Ltd
3
- *
4
- * This software is open source, the MIT license applies.
5
- *
6
- * Website : http://www.synerty.com
7
- * Support : support@synerty.com
8
- """
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
9
  import logging
10
10
  import zlib
11
11
  from base64 import b64encode, b64decode
@@ -1,11 +1,11 @@
1
- """
2
- * Created by Synerty Pty Ltd
3
- *
4
- * This software is open source, the MIT license applies.
5
- *
6
- * Website : http://www.synerty.com
7
- * Support : support@synerty.com
8
- """
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
9
  import inspect
10
10
  import logging
11
11
  import types
@@ -13,9 +13,21 @@ import weakref
13
13
  from copy import copy
14
14
  from typing import Callable, Optional, Dict, Union
15
15
 
16
+ from twisted.internet.defer import inlineCallbacks, maybeDeferred
17
+
16
18
  from vortex.PayloadEnvelope import PayloadEnvelope
17
19
  from vortex.PayloadIO import PayloadIO
18
20
  from vortex.VortexABC import SendVortexMsgResponseCallable
21
+ from vortex.VortexAccessControlContextABC import (
22
+ NO_AUTH_ACCESS_CONTROL_CONTEXT,
23
+ VortexAccessControlContextABC,
24
+ )
25
+ from vortex.VortexEndpointPermission import EndpointPermissionsType as PermissionsType
26
+ from vortex.VortexSession import (
27
+ _callableLabel,
28
+ checkAccessControlContextSignature,
29
+ checkNoHttpSessionInSignature,
30
+ )
19
31
 
20
32
  logger = logging.getLogger(__name__)
21
33
 
@@ -27,7 +39,7 @@ logger = logging.getLogger(__name__)
27
39
  # # PayloadEnvelope, # payloadEnvelope
28
40
  # # str, # vortexUuid
29
41
  # # str, # vortexName
30
- # # Any, # httpSession
42
+ # # Any, # vortexSession
31
43
  # # SendVortexMsgResponseCallable # sendResponse
32
44
  # ],
33
45
  # Optional[Deferred] # Return
@@ -43,19 +55,46 @@ class PayloadEndpoint:
43
55
 
44
56
  PERMITTER = None
45
57
 
58
+ #: Deprecation warning emitted when no requiredPermissions are set.
59
+ _NO_PERMISSIONS_WARNING = (
60
+ "PayloadEndpoint filt=%s has no requiredPermissions set. "
61
+ "In a future release, endpoints without explicit permissions "
62
+ "will raise a RegistrationError."
63
+ )
64
+
46
65
  def __init__(
47
66
  self,
48
67
  filt: Dict,
49
68
  callable_,
50
69
  acceptOnlyFromVortex: Optional[Union[str, tuple]] = None,
51
70
  ignoreFromVortex: Optional[Union[str, tuple]] = None,
71
+ requiredPermissions: Optional[PermissionsType] = None,
52
72
  ) -> None:
53
73
  """
54
74
  :param filt: The filter to match against payloads
55
75
  :param callable_: This will be called and passed the payload if it matches
56
76
  :param acceptOnlyFromVortex: Accept payloads only from this vortex,
57
77
  The vortex can be str or tuple of str, or None to accept from any.
78
+ :param requiredPermissions: Frozenset of
79
+ :class:`~vortex.VortexPermission.VortexPermission` class objects that
80
+ the caller's session must hold. Pass an empty frozenset to
81
+ explicitly declare "no permissions required". Omitting the
82
+ parameter (``None``) emits a deprecation warning.
58
83
  """
84
+ self._requiredPermissions: Optional[PermissionsType] = requiredPermissions
85
+
86
+ if requiredPermissions is None:
87
+ logger.warning(self._NO_PERMISSIONS_WARNING, filt)
88
+
89
+ callableDesc = (
90
+ f"PayloadEndpoint filt={filt} callback={_callableLabel(callable_)}"
91
+ )
92
+ self._callableAcceptsAccessControlContext = checkAccessControlContextSignature(
93
+ callable_,
94
+ callableDescription=callableDesc,
95
+ )
96
+ checkNoHttpSessionInSignature(callable_, callableDesc)
97
+
59
98
  self._acceptOnlyFromVortex = acceptOnlyFromVortex
60
99
  if isinstance(self._acceptOnlyFromVortex, str):
61
100
  self._acceptOnlyFromVortex = (self._acceptOnlyFromVortex,)
@@ -79,8 +118,8 @@ class PayloadEndpoint:
79
118
  self._callable = callable_
80
119
  self._callable._endpointWeakClass = self
81
120
 
82
- def __call__(self, payload):
83
- self._callable(payload)
121
+ def __call__(self, **kwargs):
122
+ self._callable(**kwargs)
84
123
 
85
124
  w = W(callable_)
86
125
 
@@ -129,10 +168,7 @@ class PayloadEndpoint:
129
168
  return False
130
169
 
131
170
  # Filter for the backend vortexes.
132
- if (
133
- self._acceptOnlyFromVortex
134
- and vortexName not in self._acceptOnlyFromVortex
135
- ):
171
+ if self._acceptOnlyFromVortex and vortexName not in self._acceptOnlyFromVortex:
136
172
  return False
137
173
 
138
174
  def removeUnhashable(filt):
@@ -162,33 +198,83 @@ class PayloadEndpoint:
162
198
 
163
199
  return set(ourFilt).issubset(theirFilt)
164
200
 
201
+ @inlineCallbacks
165
202
  def process(
166
203
  self,
167
204
  payloadEnvelope: PayloadEnvelope,
168
205
  vortexUuid: str,
169
206
  vortexName: str,
170
- httpSession,
207
+ vortexSession,
171
208
  sendResponse: SendVortexMsgResponseCallable,
172
209
  ):
210
+ from vortex.VortexFactory import VortexFactory
173
211
 
174
212
  if not self.check(payloadEnvelope, vortexName):
175
213
  return
176
214
 
177
- if self.PERMITTER and not self.PERMITTER.check(
178
- payloadEnvelope, httpSession
179
- ):
215
+ if self.PERMITTER and not self.PERMITTER.check(payloadEnvelope, vortexSession):
180
216
  logger.debug("Permission failed for %s", payloadEnvelope.filt)
181
217
  return
182
218
 
219
+ # Level 1: Endpoint permission check
220
+ auth = VortexFactory.getAuthenticator()
221
+ if self._requiredPermissions:
222
+ if not auth:
223
+ logger.error(
224
+ "Permission check for filt=%s cannot be enforced:"
225
+ " no authenticator is registered."
226
+ " Call VortexFactory.registerAuthenticator(). Request denied.",
227
+ payloadEnvelope.filt,
228
+ )
229
+ return
230
+ if not vortexSession:
231
+ logger.error(
232
+ "Permission check for filt=%s cannot be enforced:"
233
+ " no VortexSession found. Request denied.",
234
+ payloadEnvelope.filt,
235
+ )
236
+ return
237
+ endpointPermissions = yield maybeDeferred(
238
+ auth.getEndpointPermissions, vortexSession
239
+ )
240
+ if not self._requiredPermissions.issubset(endpointPermissions):
241
+ logger.warning(
242
+ "Permission denied for endpoint filt=%s",
243
+ payloadEnvelope.filt,
244
+ )
245
+ return
246
+
247
+ # Level 2: Get access control context, but only if the callback uses it.
248
+ # Default to the sentinel so handlers always receive a non-None value
249
+ # with a clear error on attribute access when auth is unavailable.
250
+ accessControlContext = NO_AUTH_ACCESS_CONTROL_CONTEXT
251
+ if self._callableAcceptsAccessControlContext and auth and vortexSession:
252
+ raw_ctx = yield maybeDeferred(
253
+ auth.getAccessControlContext, vortexSession, vortexName, vortexUuid
254
+ )
255
+ if not isinstance(raw_ctx, VortexAccessControlContextABC):
256
+ logger.error(
257
+ "Authenticator.getAccessControlContext() returned %r (type %s);"
258
+ " expected a VortexAccessControlContextABC subclass."
259
+ " accessControlContext will be the no-auth sentinel.",
260
+ raw_ctx,
261
+ type(raw_ctx).__name__,
262
+ )
263
+ else:
264
+ accessControlContext = raw_ctx
265
+
183
266
  callable_ = self._wref()
184
267
  if callable_:
185
- return callable_(
268
+ kwargs = dict(
186
269
  payloadEnvelope=payloadEnvelope,
187
270
  vortexUuid=vortexUuid,
188
271
  vortexName=vortexName,
189
- httpSession=httpSession,
190
272
  sendResponse=sendResponse,
273
+ vortexSession=vortexSession,
191
274
  )
275
+ if self._callableAcceptsAccessControlContext:
276
+ kwargs["accessControlContext"] = accessControlContext
277
+ return callable_(**kwargs)
192
278
  else:
193
279
  PayloadIO().remove(self)
194
280