iris-pex-embedded-python 3.7.2b2__py3-none-any.whl → 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iop/__init__.py +276 -18
- iop/__main__.py +3 -3
- iop/cli/__init__.py +1 -0
- iop/cli/formatting.py +45 -0
- iop/{_cli.py → cli/main.py} +238 -216
- iop/cli/parser.py +165 -0
- iop/cli/types.py +73 -0
- iop/cls/IOP/Common.cls +11 -2
- iop/cls/IOP/PrivateSession/Duplex.cls +1 -1
- iop/cls/IOP/Service/Remote/Rest/v1.cls +152 -5
- iop/cls/IOP/Utils.cls +814 -7
- iop/cls/IOP/Wrapper.cls +1 -1
- iop/components/__init__.py +16 -0
- iop/{_async_request.py → components/async_request.py} +7 -7
- iop/components/business_host.py +363 -0
- iop/components/business_operation.py +84 -0
- iop/{_business_process.py → components/business_process.py} +145 -125
- iop/components/business_service.py +106 -0
- iop/{_common.py → components/common.py} +160 -164
- iop/{_debugpy.py → components/debugpy.py} +11 -11
- iop/{_generator_request.py → components/generator_request.py} +4 -4
- iop/{_inbound_adapter.py → components/inbound_adapter.py} +10 -18
- iop/{_log_manager.py → components/log_manager.py} +1 -1
- iop/{_outbound_adapter.py → components/outbound_adapter.py} +1 -1
- iop/components/polling_business_service.py +48 -0
- iop/{_private_session_duplex.py → components/private_session_duplex.py} +7 -9
- iop/{_private_session_process.py → components/private_session_process.py} +2 -2
- iop/{_settings.py → components/settings.py} +23 -3
- iop/messages/__init__.py +7 -0
- iop/messages/base.py +61 -0
- iop/{_decorators.py → messages/decorators.py} +8 -2
- iop/messages/dispatch.py +351 -0
- iop/{_persistent_message.py → messages/persistent.py} +24 -19
- iop/{_serialization.py → messages/serialization.py} +34 -21
- iop/{_message_validator.py → messages/validation.py} +15 -13
- iop/migration/__init__.py +1 -0
- iop/migration/io.py +61 -0
- iop/migration/manifest.py +534 -0
- iop/migration/plans.py +80 -0
- iop/migration/utils.py +1126 -0
- iop/production/__init__.py +55 -0
- iop/production/actions.py +525 -0
- iop/production/common.py +103 -0
- iop/production/component.py +234 -0
- iop/production/declarations.py +190 -0
- iop/production/declarative.py +214 -0
- iop/production/diff.py +327 -0
- iop/production/import_.py +296 -0
- iop/production/inspection.py +92 -0
- iop/production/model.py +1143 -0
- iop/production/planning.py +678 -0
- iop/production/reconstruction.py +341 -0
- iop/production/rendering.py +610 -0
- iop/production/runtime.py +91 -0
- iop/production/source_inference.py +729 -0
- iop/production/types.py +546 -0
- iop/production/validation.py +452 -0
- iop/runtime/__init__.py +1 -0
- iop/runtime/director.py +415 -0
- iop/runtime/environment.py +59 -0
- iop/{_iris.py → runtime/iris.py} +2 -2
- iop/runtime/local.py +145 -0
- iop/{_director_protocol.py → runtime/protocol.py} +25 -8
- iop/runtime/remote/__init__.py +12 -0
- iop/runtime/remote/client.py +90 -0
- iop/runtime/remote/director.py +283 -0
- iop/runtime/remote/migration.py +57 -0
- iop/runtime/remote/settings.py +69 -0
- iop/runtime/remote/setup.py +72 -0
- {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/METADATA +11 -2
- iris_pex_embedded_python-4.0.0.dist-info/RECORD +99 -0
- iris_pex_embedded_python-4.0.0.dist-info/entry_points.txt +2 -0
- iris_pex_embedded_python-4.0.0.dist-info/top_level.txt +1 -0
- grongier/__init__.py +0 -0
- grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -8
- grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -13
- grongier/cls/Grongier/PEX/BusinessService.cls +0 -8
- grongier/cls/Grongier/PEX/Common.cls +0 -10
- grongier/cls/Grongier/PEX/Director.cls +0 -10
- grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -4
- grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -13
- grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -4
- grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -8
- grongier/cls/Grongier/PEX/Message.cls +0 -13
- grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -8
- grongier/cls/Grongier/PEX/PickleMessage.cls +0 -13
- grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -8
- grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -14
- grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -14
- grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -14
- grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -14
- grongier/cls/Grongier/PEX/Test.cls +0 -10
- grongier/cls/Grongier/PEX/Utils.cls +0 -10
- grongier/cls/Grongier/Service/WSGI.cls +0 -4
- grongier/pex/__init__.py +0 -75
- grongier/pex/__main__.py +0 -4
- grongier/pex/_business_host.py +0 -1
- grongier/pex/_cli.py +0 -4
- grongier/pex/_common.py +0 -1
- grongier/pex/_director.py +0 -1
- grongier/pex/_utils.py +0 -1
- grongier/pex/wsgi/handlers.py +0 -104
- iop/_business_host.py +0 -322
- iop/_business_operation.py +0 -77
- iop/_business_service.py +0 -67
- iop/_director.py +0 -333
- iop/_dispatch.py +0 -194
- iop/_local.py +0 -106
- iop/_message.py +0 -45
- iop/_polling_business_service.py +0 -6
- iop/_remote.py +0 -473
- iop/_utils.py +0 -912
- iop/cls/IOP/Service/WSGI.cls +0 -310
- iop/wsgi/handlers.py +0 -112
- iris_pex_embedded_python-3.7.2b2.dist-info/RECORD +0 -96
- iris_pex_embedded_python-3.7.2b2.dist-info/entry_points.txt +0 -2
- iris_pex_embedded_python-3.7.2b2.dist-info/top_level.txt +0 -2
- {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/WHEEL +0 -0
- {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/licenses/LICENSE +0 -0
iop/_director.py
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import datetime
|
|
3
|
-
import functools
|
|
4
|
-
import signal
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
|
|
7
|
-
from . import _iris
|
|
8
|
-
from ._dispatch import dispatch_deserializer, dispatch_serializer
|
|
9
|
-
from ._utils import _Utils
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class SigintHandler:
|
|
14
|
-
sigint: bool = False
|
|
15
|
-
sigint_log: bool = False
|
|
16
|
-
log_only: bool = False
|
|
17
|
-
|
|
18
|
-
def signal_handler(self, signal, frame):
|
|
19
|
-
if self.sigint or self.log_only:
|
|
20
|
-
self.sigint_log = True
|
|
21
|
-
self.sigint = True
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class _Director:
|
|
25
|
-
"""The Directorclass is used for nonpolling business services, that is, business services which are not automatically
|
|
26
|
-
called by the production framework (through the inbound adapter) at the call interval.
|
|
27
|
-
Instead these business services are created by a custom application by calling the Director.CreateBusinessService() method.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
_bs = {}
|
|
31
|
-
|
|
32
|
-
def get_business_service(self, target, force_session_id=False):
|
|
33
|
-
"""get the business service
|
|
34
|
-
Parameters:
|
|
35
|
-
target: the name of the business service
|
|
36
|
-
force_session_id: if True, force the session id to be a new one
|
|
37
|
-
"""
|
|
38
|
-
if target not in self._bs or self._bs[target] is None:
|
|
39
|
-
self._bs[target] = _Director.create_python_business_service(target)
|
|
40
|
-
if force_session_id:
|
|
41
|
-
self._bs[target].iris_handle._SessionId = ""
|
|
42
|
-
self._bs[target].iris_handle.ForceSessionId()
|
|
43
|
-
return self._bs[target]
|
|
44
|
-
|
|
45
|
-
@staticmethod
|
|
46
|
-
def CreateBusinessService(target):
|
|
47
|
-
"""DEPRECATED : use create_business_service
|
|
48
|
-
The CreateBusinessService() method initiates the specifiied business service.
|
|
49
|
-
|
|
50
|
-
Parameters:
|
|
51
|
-
connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
|
|
52
|
-
target: a string that specifies the name of the business service in the production definition.
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
an object that contains an instance of IRISBusinessService
|
|
56
|
-
"""
|
|
57
|
-
return _Director.create_business_service(target)
|
|
58
|
-
|
|
59
|
-
@staticmethod
|
|
60
|
-
def create_business_service(target):
|
|
61
|
-
"""The create_business_service() method initiates the specified business service.
|
|
62
|
-
|
|
63
|
-
Parameters:
|
|
64
|
-
connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
|
|
65
|
-
target: a string that specifies the name of the business service in the production definition.
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
an object that contains an instance of IRISBusinessService
|
|
69
|
-
"""
|
|
70
|
-
iris_object = (
|
|
71
|
-
_iris.get_iris().cls("IOP.Director").dispatchCreateBusinessService(target)
|
|
72
|
-
)
|
|
73
|
-
return iris_object
|
|
74
|
-
|
|
75
|
-
@staticmethod
|
|
76
|
-
def create_python_business_service(target):
|
|
77
|
-
"""The create_business_service() method initiates the specified business service.
|
|
78
|
-
|
|
79
|
-
Parameters:
|
|
80
|
-
connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
|
|
81
|
-
target: a string that specifies the name of the business service in the production definition.
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
an object that contains an instance of IRISBusinessService
|
|
85
|
-
"""
|
|
86
|
-
iris_object = (
|
|
87
|
-
_iris.get_iris().cls("IOP.Director").dispatchCreateBusinessService(target)
|
|
88
|
-
)
|
|
89
|
-
return iris_object.GetClass()
|
|
90
|
-
|
|
91
|
-
### List of function to manage the production
|
|
92
|
-
### start production
|
|
93
|
-
@staticmethod
|
|
94
|
-
def start_production_with_log(production_name=None):
|
|
95
|
-
if production_name is None or production_name == "":
|
|
96
|
-
production_name = _Director.get_default_production()
|
|
97
|
-
# create two async task
|
|
98
|
-
loop = asyncio.get_event_loop()
|
|
99
|
-
# add signal handler
|
|
100
|
-
handler = SigintHandler()
|
|
101
|
-
loop.add_signal_handler(
|
|
102
|
-
signal.SIGINT,
|
|
103
|
-
functools.partial(handler.signal_handler, signal.SIGINT, loop),
|
|
104
|
-
)
|
|
105
|
-
loop.run_until_complete(
|
|
106
|
-
asyncio.gather(
|
|
107
|
-
_Director._start_production_async(production_name, handler),
|
|
108
|
-
_Director._log_production_async(handler),
|
|
109
|
-
)
|
|
110
|
-
)
|
|
111
|
-
loop.close()
|
|
112
|
-
|
|
113
|
-
@staticmethod
|
|
114
|
-
async def _start_production_async(production_name: str, handler: SigintHandler):
|
|
115
|
-
_Director.start_production(production_name)
|
|
116
|
-
while True:
|
|
117
|
-
if handler.sigint:
|
|
118
|
-
_Director.stop_production()
|
|
119
|
-
break
|
|
120
|
-
await asyncio.sleep(1)
|
|
121
|
-
|
|
122
|
-
@staticmethod
|
|
123
|
-
def start_production(production_name=None):
|
|
124
|
-
if production_name is None or production_name == "":
|
|
125
|
-
production_name = _Director.get_default_production()
|
|
126
|
-
_iris.get_iris().cls("Ens.Director").StartProduction(production_name)
|
|
127
|
-
|
|
128
|
-
### stop production
|
|
129
|
-
@staticmethod
|
|
130
|
-
def stop_production():
|
|
131
|
-
_iris.get_iris().cls("Ens.Director").StopProduction()
|
|
132
|
-
|
|
133
|
-
### restart production
|
|
134
|
-
@staticmethod
|
|
135
|
-
def restart_production():
|
|
136
|
-
_iris.get_iris().cls("Ens.Director").RestartProduction()
|
|
137
|
-
|
|
138
|
-
### shutdown production
|
|
139
|
-
@staticmethod
|
|
140
|
-
def shutdown_production():
|
|
141
|
-
_iris.get_iris().cls("Ens.Director").StopProduction(10, 1)
|
|
142
|
-
|
|
143
|
-
### update production
|
|
144
|
-
@staticmethod
|
|
145
|
-
def update_production():
|
|
146
|
-
_iris.get_iris().cls("Ens.Director").UpdateProduction()
|
|
147
|
-
|
|
148
|
-
### list production
|
|
149
|
-
@staticmethod
|
|
150
|
-
def list_productions():
|
|
151
|
-
return _iris.get_iris().cls("IOP.Director").dispatchListProductions()
|
|
152
|
-
|
|
153
|
-
### status production
|
|
154
|
-
@staticmethod
|
|
155
|
-
def status_production():
|
|
156
|
-
dikt = _iris.get_iris().cls("IOP.Director").StatusProduction()
|
|
157
|
-
if dikt["Production"] is None or dikt["Production"] == "":
|
|
158
|
-
dikt["Production"] = _Director.get_default_production()
|
|
159
|
-
return dikt
|
|
160
|
-
|
|
161
|
-
### set default production
|
|
162
|
-
@staticmethod
|
|
163
|
-
def set_default_production(production_name=""):
|
|
164
|
-
# set ^Ens.Configuration("SuperUser","LastProduction")
|
|
165
|
-
glb = _iris.get_iris().gref("^Ens.Configuration")
|
|
166
|
-
glb["csp", "LastProduction"] = production_name
|
|
167
|
-
|
|
168
|
-
### get default production
|
|
169
|
-
@staticmethod
|
|
170
|
-
def get_default_production():
|
|
171
|
-
glb = _iris.get_iris().gref("^Ens.Configuration")
|
|
172
|
-
default_production_name = glb["csp", "LastProduction"]
|
|
173
|
-
if default_production_name is None or default_production_name == "":
|
|
174
|
-
default_production_name = "Not defined"
|
|
175
|
-
return default_production_name
|
|
176
|
-
|
|
177
|
-
@staticmethod
|
|
178
|
-
def format_log(row: list) -> str:
|
|
179
|
-
# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
|
180
|
-
# ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
|
|
181
|
-
# yield all except stack aand tracecat
|
|
182
|
-
# in first position, the timelogged
|
|
183
|
-
# cast the result to string
|
|
184
|
-
# convert Type to its string value
|
|
185
|
-
# Assert,Error,Warning,Info,Trace,Alert
|
|
186
|
-
typ = row[11]
|
|
187
|
-
if typ == 1:
|
|
188
|
-
typ = "Assert"
|
|
189
|
-
elif typ == 2:
|
|
190
|
-
typ = "Error"
|
|
191
|
-
elif typ == 3:
|
|
192
|
-
typ = "Warning"
|
|
193
|
-
elif typ == 4:
|
|
194
|
-
typ = "Info"
|
|
195
|
-
elif typ == 5:
|
|
196
|
-
typ = "Trace"
|
|
197
|
-
elif typ == 6:
|
|
198
|
-
typ = "Alert"
|
|
199
|
-
return (
|
|
200
|
-
str(row[9])
|
|
201
|
-
+ " "
|
|
202
|
-
+ typ
|
|
203
|
-
+ " "
|
|
204
|
-
+ str(row[1])
|
|
205
|
-
+ " "
|
|
206
|
-
+ str(row[2])
|
|
207
|
-
+ " "
|
|
208
|
-
+ str(row[3])
|
|
209
|
-
+ " "
|
|
210
|
-
+ str(row[4])
|
|
211
|
-
+ " "
|
|
212
|
-
+ str(row[5])
|
|
213
|
-
+ " "
|
|
214
|
-
+ str(row[6])
|
|
215
|
-
+ " "
|
|
216
|
-
+ str(row[8])
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
@staticmethod
|
|
220
|
-
def read_top_log(top) -> list:
|
|
221
|
-
sql = """
|
|
222
|
-
SELECT top ?
|
|
223
|
-
ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
|
|
224
|
-
FROM Ens_Util.Log
|
|
225
|
-
order by id desc
|
|
226
|
-
"""
|
|
227
|
-
result = []
|
|
228
|
-
stmt = _iris.get_iris().sql.prepare(sql)
|
|
229
|
-
rs = stmt.execute(top)
|
|
230
|
-
for row in rs:
|
|
231
|
-
result.append(_Director.format_log(row))
|
|
232
|
-
return result
|
|
233
|
-
|
|
234
|
-
@staticmethod
|
|
235
|
-
def read_log() -> list:
|
|
236
|
-
sql = """
|
|
237
|
-
SELECT
|
|
238
|
-
ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
|
|
239
|
-
FROM Ens_Util.Log
|
|
240
|
-
where TimeLogged >= ?
|
|
241
|
-
order by id desc
|
|
242
|
-
"""
|
|
243
|
-
result = []
|
|
244
|
-
stmt = _iris.get_iris().sql.prepare(sql)
|
|
245
|
-
time = datetime.datetime.now() - datetime.timedelta(seconds=1)
|
|
246
|
-
# convert to utc time
|
|
247
|
-
time = time.astimezone(datetime.timezone.utc)
|
|
248
|
-
rs = stmt.execute(time.isoformat(sep=" "))
|
|
249
|
-
for row in rs:
|
|
250
|
-
result.append(_Director.format_log(row))
|
|
251
|
-
return result
|
|
252
|
-
|
|
253
|
-
@staticmethod
|
|
254
|
-
async def _log_production_async(handler):
|
|
255
|
-
"""Log production
|
|
256
|
-
if ctrl+c is pressed, the log is stopped
|
|
257
|
-
"""
|
|
258
|
-
while True:
|
|
259
|
-
for row in reversed(_Director.read_log()):
|
|
260
|
-
print(row)
|
|
261
|
-
if handler.sigint_log:
|
|
262
|
-
break
|
|
263
|
-
await asyncio.sleep(1)
|
|
264
|
-
|
|
265
|
-
@staticmethod
|
|
266
|
-
def log_production_top(top=10):
|
|
267
|
-
"""
|
|
268
|
-
Log the top N logs of the production
|
|
269
|
-
Parameters:
|
|
270
|
-
top: the number of log to display
|
|
271
|
-
"""
|
|
272
|
-
for row in reversed(_Director.read_top_log(top)):
|
|
273
|
-
print(row)
|
|
274
|
-
|
|
275
|
-
@staticmethod
|
|
276
|
-
def log_production():
|
|
277
|
-
"""Log production
|
|
278
|
-
if ctrl+c is pressed, the log is stopped
|
|
279
|
-
"""
|
|
280
|
-
loop = asyncio.get_event_loop()
|
|
281
|
-
handler = SigintHandler(log_only=True)
|
|
282
|
-
loop.add_signal_handler(
|
|
283
|
-
signal.SIGINT,
|
|
284
|
-
functools.partial(handler.signal_handler, signal.SIGINT, loop),
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
for row in reversed(_Director.read_top_log(10)):
|
|
288
|
-
print(row)
|
|
289
|
-
|
|
290
|
-
loop.run_until_complete(_Director._log_production_async(handler))
|
|
291
|
-
loop.close()
|
|
292
|
-
|
|
293
|
-
@staticmethod
|
|
294
|
-
def test_component(target, message=None, classname=None, body=None):
|
|
295
|
-
"""
|
|
296
|
-
Test a component
|
|
297
|
-
Parameters:
|
|
298
|
-
target: the name of the component
|
|
299
|
-
classname: the name of the class to test
|
|
300
|
-
body: the body of the message
|
|
301
|
-
"""
|
|
302
|
-
iris = _iris.get_iris()
|
|
303
|
-
if not message:
|
|
304
|
-
message = iris.cls("Ens.Request")._New()
|
|
305
|
-
if classname:
|
|
306
|
-
# if classname start with 'iris.' then create an iris object
|
|
307
|
-
if classname.startswith("iris."):
|
|
308
|
-
# strip the iris. prefix
|
|
309
|
-
classname = classname[5:]
|
|
310
|
-
if body:
|
|
311
|
-
message = iris.cls(classname)._New(body)
|
|
312
|
-
else:
|
|
313
|
-
message = iris.cls(classname)._New()
|
|
314
|
-
# else create a python object
|
|
315
|
-
else:
|
|
316
|
-
# python message are casted to Grongier.PEX.Message
|
|
317
|
-
message = iris.cls("IOP.Message")._New()
|
|
318
|
-
message.classname = classname
|
|
319
|
-
if body:
|
|
320
|
-
message.json = body
|
|
321
|
-
else:
|
|
322
|
-
message.json = _Utils.string_to_stream("{}")
|
|
323
|
-
# serialize the message
|
|
324
|
-
serial_message = dispatch_serializer(message)
|
|
325
|
-
response = iris.cls("IOP.Utils").dispatchTestComponent(target, serial_message)
|
|
326
|
-
try:
|
|
327
|
-
deserialized_response = dispatch_deserializer(response)
|
|
328
|
-
except ImportError:
|
|
329
|
-
# can't import the class, return the string
|
|
330
|
-
deserialized_response = (
|
|
331
|
-
f"{response.classname} : {_Utils.stream_to_string(response.jstr)}"
|
|
332
|
-
)
|
|
333
|
-
return deserialized_response
|
iop/_dispatch.py
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
from inspect import signature, Parameter
|
|
2
|
-
from typing import Any, List, Tuple
|
|
3
|
-
|
|
4
|
-
from ._serialization import (
|
|
5
|
-
serialize_message,
|
|
6
|
-
serialize_pickle_message,
|
|
7
|
-
deserialize_message,
|
|
8
|
-
deserialize_pickle_message,
|
|
9
|
-
serialize_message_generator,
|
|
10
|
-
serialize_pickle_message_generator,
|
|
11
|
-
)
|
|
12
|
-
from ._message_validator import (
|
|
13
|
-
is_message_instance,
|
|
14
|
-
is_pickle_message_instance,
|
|
15
|
-
is_iris_object_instance,
|
|
16
|
-
)
|
|
17
|
-
from ._persistent_message import (
|
|
18
|
-
deserialize_persistent_message,
|
|
19
|
-
get_iris_object_classname,
|
|
20
|
-
is_persistent_message_instance,
|
|
21
|
-
serialize_persistent_message,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
_MESSAGE_CLASSES = {
|
|
26
|
-
"IOP.Message",
|
|
27
|
-
"Grongier.PEX.Message",
|
|
28
|
-
"IOP.Generator.Message.Start",
|
|
29
|
-
}
|
|
30
|
-
_PICKLE_MESSAGE_CLASSES = {
|
|
31
|
-
"IOP.PickleMessage",
|
|
32
|
-
"Grongier.PEX.PickleMessage",
|
|
33
|
-
"IOP.Generator.Message.StartPickle",
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def dispatch_serializer(message: Any, is_generator: bool = False) -> Any:
|
|
38
|
-
"""Serializes the message based on its type.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
message: The message to serialize
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
The serialized message
|
|
45
|
-
|
|
46
|
-
Raises:
|
|
47
|
-
TypeError: If message is invalid type
|
|
48
|
-
"""
|
|
49
|
-
if message is not None:
|
|
50
|
-
if is_persistent_message_instance(message):
|
|
51
|
-
return serialize_persistent_message(message, is_generator=is_generator)
|
|
52
|
-
elif is_message_instance(message):
|
|
53
|
-
if is_generator:
|
|
54
|
-
return serialize_message_generator(message)
|
|
55
|
-
return serialize_message(message)
|
|
56
|
-
elif is_pickle_message_instance(message):
|
|
57
|
-
if is_generator:
|
|
58
|
-
return serialize_pickle_message_generator(message)
|
|
59
|
-
return serialize_pickle_message(message)
|
|
60
|
-
elif is_iris_object_instance(message):
|
|
61
|
-
return message
|
|
62
|
-
|
|
63
|
-
if message == "" or message is None:
|
|
64
|
-
return message
|
|
65
|
-
|
|
66
|
-
if hasattr(message, "__iter__"):
|
|
67
|
-
raise TypeError(
|
|
68
|
-
"You may have tried to invoke a generator function without using the 'send_generator_request' method. Please use that method to handle generator functions."
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
raise TypeError(
|
|
72
|
-
"The message must be an instance of a class that is a subclass of Message or IRISObject %Persistent class."
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def dispatch_deserializer(serial: Any) -> Any:
|
|
77
|
-
"""Deserializes the message based on its type.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
serial: The serialized message
|
|
81
|
-
|
|
82
|
-
Returns:
|
|
83
|
-
The deserialized message
|
|
84
|
-
"""
|
|
85
|
-
if serial is None or not type(serial).__module__.startswith("iris"):
|
|
86
|
-
return serial
|
|
87
|
-
|
|
88
|
-
iris_classname = get_iris_object_classname(serial)
|
|
89
|
-
|
|
90
|
-
if iris_classname in _MESSAGE_CLASSES:
|
|
91
|
-
return deserialize_message(serial)
|
|
92
|
-
|
|
93
|
-
if iris_classname in _PICKLE_MESSAGE_CLASSES:
|
|
94
|
-
return deserialize_pickle_message(serial)
|
|
95
|
-
|
|
96
|
-
deserialized = deserialize_persistent_message(serial, iris_classname=iris_classname)
|
|
97
|
-
if deserialized is not serial:
|
|
98
|
-
return deserialized
|
|
99
|
-
|
|
100
|
-
if serial._IsA("IOP.Message") or serial._IsA("Grongier.PEX.Message"):
|
|
101
|
-
return deserialize_message(serial)
|
|
102
|
-
|
|
103
|
-
if serial._IsA("IOP.PickleMessage") or serial._IsA("Grongier.PEX.PickleMessage"):
|
|
104
|
-
return deserialize_pickle_message(serial)
|
|
105
|
-
|
|
106
|
-
return serial
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def dispach_message(host: Any, request: Any) -> Any:
|
|
110
|
-
"""Dispatches the message to the appropriate method.
|
|
111
|
-
|
|
112
|
-
Args:
|
|
113
|
-
request: The request object
|
|
114
|
-
|
|
115
|
-
Returns:
|
|
116
|
-
The response object
|
|
117
|
-
"""
|
|
118
|
-
call = "on_message"
|
|
119
|
-
|
|
120
|
-
module = request.__class__.__module__
|
|
121
|
-
classname = request.__class__.__name__
|
|
122
|
-
|
|
123
|
-
for msg, method in host.DISPATCH:
|
|
124
|
-
if msg == module + "." + classname:
|
|
125
|
-
call = method
|
|
126
|
-
|
|
127
|
-
return getattr(host, call)(request)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def create_dispatch(host: Any) -> None:
|
|
131
|
-
"""Creates a dispatch table mapping class names to their handler methods.
|
|
132
|
-
The dispatch table consists of tuples of (fully_qualified_class_name, method_name).
|
|
133
|
-
Only methods that take a single typed parameter are considered as handlers.
|
|
134
|
-
"""
|
|
135
|
-
dispatch = _declared_dispatch(host)
|
|
136
|
-
|
|
137
|
-
for method_name in get_callable_methods(host):
|
|
138
|
-
handler_info = get_handler_info(host, method_name)
|
|
139
|
-
if handler_info and handler_info not in dispatch:
|
|
140
|
-
dispatch.append(handler_info)
|
|
141
|
-
|
|
142
|
-
host.DISPATCH = dispatch
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def _declared_dispatch(host: Any) -> List[Tuple[str, str]]:
|
|
146
|
-
if "DISPATCH" in getattr(host, "__dict__", {}):
|
|
147
|
-
return list(host.__dict__["DISPATCH"])
|
|
148
|
-
|
|
149
|
-
class_dispatch = host.__class__.__dict__.get("DISPATCH")
|
|
150
|
-
if class_dispatch is not None:
|
|
151
|
-
return list(class_dispatch)
|
|
152
|
-
|
|
153
|
-
return []
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def get_callable_methods(host: Any) -> List[str]:
|
|
157
|
-
"""Returns a list of callable method names that don't start with underscore."""
|
|
158
|
-
return [
|
|
159
|
-
func
|
|
160
|
-
for func in dir(host)
|
|
161
|
-
if callable(getattr(host, func)) and not func.startswith("_")
|
|
162
|
-
]
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def get_handler_info(host: Any, method_name: str) -> Tuple[str, str] | None:
|
|
166
|
-
"""Analyzes a method to determine if it's a valid message handler.
|
|
167
|
-
Returns a tuple of (fully_qualified_class_name, method_name) if valid,
|
|
168
|
-
None otherwise.
|
|
169
|
-
"""
|
|
170
|
-
try:
|
|
171
|
-
params = signature(getattr(host, method_name)).parameters
|
|
172
|
-
if len(params) != 1:
|
|
173
|
-
return None
|
|
174
|
-
|
|
175
|
-
param: Parameter = next(iter(params.values()))
|
|
176
|
-
annotation = param.annotation
|
|
177
|
-
|
|
178
|
-
if isinstance(annotation, str):
|
|
179
|
-
# return it as is, assuming it's a fully qualified class name
|
|
180
|
-
return annotation, method_name
|
|
181
|
-
|
|
182
|
-
if is_iris_object_instance(annotation):
|
|
183
|
-
return (
|
|
184
|
-
f"{type(annotation).__module__}.{type(annotation).__name__}",
|
|
185
|
-
method_name,
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
if annotation == Parameter.empty or not isinstance(annotation, type):
|
|
189
|
-
return None
|
|
190
|
-
|
|
191
|
-
return f"{annotation.__module__}.{annotation.__name__}", method_name
|
|
192
|
-
|
|
193
|
-
except ValueError:
|
|
194
|
-
return None
|
iop/_local.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"""Local director: thin instance-method wrapper around static _Director calls.
|
|
2
|
-
|
|
3
|
-
This gives the CLI a uniform interface so it can swap between
|
|
4
|
-
_LocalDirector and _RemoteDirector without any branching.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import os
|
|
11
|
-
from typing import Optional
|
|
12
|
-
|
|
13
|
-
from ._director import _Director
|
|
14
|
-
from ._utils import _Utils
|
|
15
|
-
from ._director_protocol import DirectorProtocol as _DirectorProtocol # noqa: F401 --- IGNORE ---
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class _LocalDirector(_DirectorProtocol):
|
|
19
|
-
"""Local director: thin instance-method wrapper around static _Director calls."""
|
|
20
|
-
|
|
21
|
-
# ------------------------------------------------------------------
|
|
22
|
-
# Production lifecycle
|
|
23
|
-
# ------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
def get_default_production(self) -> str:
|
|
26
|
-
return _Director.get_default_production()
|
|
27
|
-
|
|
28
|
-
def set_default_production(self, production_name: str = "") -> None:
|
|
29
|
-
_Director.set_default_production(production_name)
|
|
30
|
-
|
|
31
|
-
def list_productions(self) -> dict:
|
|
32
|
-
return _Director.list_productions()
|
|
33
|
-
|
|
34
|
-
def status_production(self) -> dict:
|
|
35
|
-
return _Director.status_production()
|
|
36
|
-
|
|
37
|
-
def start_production(self, production_name: Optional[str] = None) -> None:
|
|
38
|
-
_Director.start_production(production_name)
|
|
39
|
-
|
|
40
|
-
def start_production_with_log(self, production_name: Optional[str] = None) -> None:
|
|
41
|
-
_Director.start_production_with_log(production_name)
|
|
42
|
-
|
|
43
|
-
def stop_production(self) -> None:
|
|
44
|
-
_Director.stop_production()
|
|
45
|
-
|
|
46
|
-
def shutdown_production(self) -> None:
|
|
47
|
-
_Director.shutdown_production()
|
|
48
|
-
|
|
49
|
-
def restart_production(self) -> None:
|
|
50
|
-
_Director.restart_production()
|
|
51
|
-
|
|
52
|
-
def update_production(self) -> None:
|
|
53
|
-
_Director.update_production()
|
|
54
|
-
|
|
55
|
-
# ------------------------------------------------------------------
|
|
56
|
-
# Logging
|
|
57
|
-
# ------------------------------------------------------------------
|
|
58
|
-
|
|
59
|
-
def log_production_top(self, top: int = 10) -> None:
|
|
60
|
-
_Director.log_production_top(top)
|
|
61
|
-
|
|
62
|
-
def log_production(self) -> None:
|
|
63
|
-
_Director.log_production()
|
|
64
|
-
|
|
65
|
-
# ------------------------------------------------------------------
|
|
66
|
-
# Test
|
|
67
|
-
# ------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
def test_component(
|
|
70
|
-
self,
|
|
71
|
-
target: Optional[str],
|
|
72
|
-
message=None,
|
|
73
|
-
classname: Optional[str] = None,
|
|
74
|
-
body: "str | dict | None" = None,
|
|
75
|
-
restart: bool = True, # ignored locally — included to satisfy DirectorProtocol
|
|
76
|
-
):
|
|
77
|
-
return _Director.test_component(target, message, classname, body)
|
|
78
|
-
|
|
79
|
-
# ------------------------------------------------------------------
|
|
80
|
-
# Export
|
|
81
|
-
# ------------------------------------------------------------------
|
|
82
|
-
|
|
83
|
-
def export_production(self, production_name: str) -> dict:
|
|
84
|
-
return json.loads(_Utils.export_production(production_name))
|
|
85
|
-
|
|
86
|
-
# ------------------------------------------------------------------
|
|
87
|
-
# Init / setup
|
|
88
|
-
# ------------------------------------------------------------------
|
|
89
|
-
|
|
90
|
-
def setup(self, path: Optional[str] = None) -> None:
|
|
91
|
-
_Utils.setup(path)
|
|
92
|
-
|
|
93
|
-
# ------------------------------------------------------------------
|
|
94
|
-
# Migrate
|
|
95
|
-
# ------------------------------------------------------------------
|
|
96
|
-
|
|
97
|
-
def migrate(self, path: str) -> None:
|
|
98
|
-
_Utils.migrate(path, mode="LOCAL", namespace=self.namespace)
|
|
99
|
-
|
|
100
|
-
# ------------------------------------------------------------------
|
|
101
|
-
# Metadata
|
|
102
|
-
# ------------------------------------------------------------------
|
|
103
|
-
|
|
104
|
-
@property
|
|
105
|
-
def namespace(self) -> str:
|
|
106
|
-
return os.getenv("IRISNAMESPACE", "not set")
|
iop/_message.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from typing import Any, Optional
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class _Message:
|
|
7
|
-
"""The abstract class that is the superclass for persistent messages sent from one component to another.
|
|
8
|
-
This class has no properties or methods. Users subclass Message and add properties.
|
|
9
|
-
The IOP framework provides the persistence to objects derived from the Message class.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
_iris_id: Optional[str] = None
|
|
13
|
-
|
|
14
|
-
def get_iris_id(self) -> Optional[str]:
|
|
15
|
-
"""Get the IRIS ID of the message."""
|
|
16
|
-
return self._iris_id
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class _PickleMessage(_Message):
|
|
20
|
-
"""The abstract class that is the superclass for persistent messages sent from one component to another.
|
|
21
|
-
This class has no properties or methods. Users subclass Message and add properties.
|
|
22
|
-
The IOP framework provides the persistence to objects derived from the Message class.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class _PydanticMessage(BaseModel):
|
|
29
|
-
"""Base class for Pydantic-based messages that can be serialized to IRIS."""
|
|
30
|
-
|
|
31
|
-
_iris_id: Optional[str] = None
|
|
32
|
-
|
|
33
|
-
def __init__(self, **data: Any):
|
|
34
|
-
super().__init__(**data)
|
|
35
|
-
|
|
36
|
-
def get_iris_id(self) -> Optional[str]:
|
|
37
|
-
"""Get the IRIS ID of the message."""
|
|
38
|
-
return self._iris_id
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class _PydanticPickleMessage(_PydanticMessage):
|
|
42
|
-
"""Base class for Pydantic-based messages that can be serialized to IRIS."""
|
|
43
|
-
|
|
44
|
-
def __init__(self, **data: Any):
|
|
45
|
-
super().__init__(**data)
|