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/runtime/director.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"""Local IRIS director functions and deprecated _Director facade."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
import functools
|
|
6
|
+
import signal
|
|
7
|
+
import warnings
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from functools import wraps
|
|
10
|
+
|
|
11
|
+
from ..messages.dispatch import dispatch_deserializer, dispatch_serializer
|
|
12
|
+
from ..messages.serialization import MessageClassImportError
|
|
13
|
+
from ..migration import utils as _migration_utils
|
|
14
|
+
from ..runtime import iris as _iris
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class SigintHandler:
|
|
19
|
+
sigint: bool = False
|
|
20
|
+
sigint_log: bool = False
|
|
21
|
+
log_only: bool = False
|
|
22
|
+
|
|
23
|
+
def signal_handler(self, signal, frame):
|
|
24
|
+
if self.sigint or self.log_only:
|
|
25
|
+
self.sigint_log = True
|
|
26
|
+
self.sigint = True
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_business_service(target):
|
|
30
|
+
"""The create_business_service() method initiates the specified business service.
|
|
31
|
+
|
|
32
|
+
Parameters:
|
|
33
|
+
connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
|
|
34
|
+
target: a string that specifies the name of the business service in the production definition.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
an object that contains an instance of IRISBusinessService
|
|
38
|
+
"""
|
|
39
|
+
iris_object = (
|
|
40
|
+
_iris.get_iris().cls("IOP.Director").dispatchCreateBusinessService(target)
|
|
41
|
+
)
|
|
42
|
+
return iris_object
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def create_python_business_service(target):
|
|
46
|
+
"""The create_business_service() method initiates the specified business service.
|
|
47
|
+
|
|
48
|
+
Parameters:
|
|
49
|
+
connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
|
|
50
|
+
target: a string that specifies the name of the business service in the production definition.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
an object that contains an instance of IRISBusinessService
|
|
54
|
+
"""
|
|
55
|
+
iris_object = (
|
|
56
|
+
_iris.get_iris().cls("IOP.Director").dispatchCreateBusinessService(target)
|
|
57
|
+
)
|
|
58
|
+
return iris_object.GetClass()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
### List of function to manage the production
|
|
62
|
+
### start production
|
|
63
|
+
def start_production_with_log(production_name=None):
|
|
64
|
+
if production_name is None or production_name == "":
|
|
65
|
+
production_name = get_default_production()
|
|
66
|
+
# create two async task
|
|
67
|
+
loop = asyncio.get_event_loop()
|
|
68
|
+
# add signal handler
|
|
69
|
+
handler = SigintHandler()
|
|
70
|
+
loop.add_signal_handler(
|
|
71
|
+
signal.SIGINT,
|
|
72
|
+
functools.partial(handler.signal_handler, signal.SIGINT, loop),
|
|
73
|
+
)
|
|
74
|
+
loop.run_until_complete(
|
|
75
|
+
asyncio.gather(
|
|
76
|
+
_start_production_async(production_name, handler),
|
|
77
|
+
_log_production_async(handler),
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
loop.close()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def _start_production_async(production_name: str, handler: SigintHandler):
|
|
84
|
+
start_production(production_name)
|
|
85
|
+
while True:
|
|
86
|
+
if handler.sigint:
|
|
87
|
+
stop_production()
|
|
88
|
+
break
|
|
89
|
+
await asyncio.sleep(1)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def start_production(production_name=None):
|
|
93
|
+
if production_name is None or production_name == "":
|
|
94
|
+
production_name = get_default_production()
|
|
95
|
+
status = _iris.get_iris().cls("Ens.Director").StartProduction(production_name)
|
|
96
|
+
_migration_utils.raise_on_error(status)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
### stop production
|
|
100
|
+
def stop_production():
|
|
101
|
+
_iris.get_iris().cls("Ens.Director").StopProduction()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
### restart production
|
|
105
|
+
def restart_production():
|
|
106
|
+
_iris.get_iris().cls("Ens.Director").RestartProduction()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
### shutdown production
|
|
110
|
+
def shutdown_production():
|
|
111
|
+
_iris.get_iris().cls("Ens.Director").StopProduction(10, 1)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
### update production
|
|
115
|
+
def update_production():
|
|
116
|
+
_iris.get_iris().cls("Ens.Director").UpdateProduction()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
### start production component
|
|
120
|
+
def start_component(component_name):
|
|
121
|
+
status = (
|
|
122
|
+
_iris.get_iris()
|
|
123
|
+
.cls("Ens.Director")
|
|
124
|
+
.EnableConfigItem(
|
|
125
|
+
component_name,
|
|
126
|
+
1,
|
|
127
|
+
1,
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
_migration_utils.raise_on_error(status)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
### stop production component
|
|
134
|
+
def stop_component(component_name):
|
|
135
|
+
status = (
|
|
136
|
+
_iris.get_iris()
|
|
137
|
+
.cls("Ens.Director")
|
|
138
|
+
.EnableConfigItem(
|
|
139
|
+
component_name,
|
|
140
|
+
0,
|
|
141
|
+
1,
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
_migration_utils.raise_on_error(status)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
### restart production component
|
|
148
|
+
def restart_component(component_name):
|
|
149
|
+
stop_component(component_name)
|
|
150
|
+
start_component(component_name)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
### list production
|
|
154
|
+
def list_productions():
|
|
155
|
+
return _iris.get_iris().cls("IOP.Director").dispatchListProductions()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
### status production
|
|
159
|
+
def status_production():
|
|
160
|
+
dikt = _iris.get_iris().cls("IOP.Director").StatusProduction()
|
|
161
|
+
if dikt["Production"] is None or dikt["Production"] == "":
|
|
162
|
+
dikt["Production"] = get_default_production()
|
|
163
|
+
return dikt
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
### set default production
|
|
167
|
+
def set_default_production(production_name=""):
|
|
168
|
+
# set ^Ens.Configuration("SuperUser","LastProduction")
|
|
169
|
+
glb = _iris.get_iris().gref("^Ens.Configuration")
|
|
170
|
+
glb["csp", "LastProduction"] = production_name
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
### get default production
|
|
174
|
+
def get_default_production():
|
|
175
|
+
glb = _iris.get_iris().gref("^Ens.Configuration")
|
|
176
|
+
default_production_name = glb["csp", "LastProduction"]
|
|
177
|
+
if default_production_name is None or default_production_name == "":
|
|
178
|
+
default_production_name = "Not defined"
|
|
179
|
+
return default_production_name
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def format_log(row: list) -> str:
|
|
183
|
+
# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
|
184
|
+
# ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
|
|
185
|
+
# yield all except stack aand tracecat
|
|
186
|
+
# in first position, the timelogged
|
|
187
|
+
# cast the result to string
|
|
188
|
+
# convert Type to its string value
|
|
189
|
+
# Assert,Error,Warning,Info,Trace,Alert
|
|
190
|
+
typ = row[11]
|
|
191
|
+
if typ == 1:
|
|
192
|
+
typ = "Assert"
|
|
193
|
+
elif typ == 2:
|
|
194
|
+
typ = "Error"
|
|
195
|
+
elif typ == 3:
|
|
196
|
+
typ = "Warning"
|
|
197
|
+
elif typ == 4:
|
|
198
|
+
typ = "Info"
|
|
199
|
+
elif typ == 5:
|
|
200
|
+
typ = "Trace"
|
|
201
|
+
elif typ == 6:
|
|
202
|
+
typ = "Alert"
|
|
203
|
+
return (
|
|
204
|
+
str(row[9])
|
|
205
|
+
+ " "
|
|
206
|
+
+ typ
|
|
207
|
+
+ " "
|
|
208
|
+
+ str(row[1])
|
|
209
|
+
+ " "
|
|
210
|
+
+ str(row[2])
|
|
211
|
+
+ " "
|
|
212
|
+
+ str(row[3])
|
|
213
|
+
+ " "
|
|
214
|
+
+ str(row[4])
|
|
215
|
+
+ " "
|
|
216
|
+
+ str(row[5])
|
|
217
|
+
+ " "
|
|
218
|
+
+ str(row[6])
|
|
219
|
+
+ " "
|
|
220
|
+
+ str(row[8])
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def read_top_log(top) -> list:
|
|
225
|
+
sql = """
|
|
226
|
+
SELECT top ?
|
|
227
|
+
ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
|
|
228
|
+
FROM Ens_Util.Log
|
|
229
|
+
order by id desc
|
|
230
|
+
"""
|
|
231
|
+
result = []
|
|
232
|
+
stmt = _iris.get_iris().sql.prepare(sql)
|
|
233
|
+
rs = stmt.execute(top)
|
|
234
|
+
for row in rs:
|
|
235
|
+
result.append(format_log(row))
|
|
236
|
+
return result
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def read_log() -> list:
|
|
240
|
+
sql = """
|
|
241
|
+
SELECT
|
|
242
|
+
ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
|
|
243
|
+
FROM Ens_Util.Log
|
|
244
|
+
where TimeLogged >= ?
|
|
245
|
+
order by id desc
|
|
246
|
+
"""
|
|
247
|
+
result = []
|
|
248
|
+
stmt = _iris.get_iris().sql.prepare(sql)
|
|
249
|
+
time = datetime.datetime.now() - datetime.timedelta(seconds=1)
|
|
250
|
+
# convert to utc time
|
|
251
|
+
time = time.astimezone(datetime.timezone.utc)
|
|
252
|
+
rs = stmt.execute(time.isoformat(sep=" "))
|
|
253
|
+
for row in rs:
|
|
254
|
+
result.append(format_log(row))
|
|
255
|
+
return result
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
async def _log_production_async(handler):
|
|
259
|
+
"""Log production
|
|
260
|
+
if ctrl+c is pressed, the log is stopped
|
|
261
|
+
"""
|
|
262
|
+
while True:
|
|
263
|
+
for row in reversed(read_log()):
|
|
264
|
+
print(row)
|
|
265
|
+
if handler.sigint_log:
|
|
266
|
+
break
|
|
267
|
+
await asyncio.sleep(1)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def log_production_top(top=10):
|
|
271
|
+
"""
|
|
272
|
+
Log the top N logs of the production
|
|
273
|
+
Parameters:
|
|
274
|
+
top: the number of log to display
|
|
275
|
+
"""
|
|
276
|
+
for row in reversed(read_top_log(top)):
|
|
277
|
+
print(row)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def log_production():
|
|
281
|
+
"""Log production
|
|
282
|
+
if ctrl+c is pressed, the log is stopped
|
|
283
|
+
"""
|
|
284
|
+
loop = asyncio.get_event_loop()
|
|
285
|
+
handler = SigintHandler(log_only=True)
|
|
286
|
+
loop.add_signal_handler(
|
|
287
|
+
signal.SIGINT,
|
|
288
|
+
functools.partial(handler.signal_handler, signal.SIGINT, loop),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
for row in reversed(read_top_log(10)):
|
|
292
|
+
print(row)
|
|
293
|
+
|
|
294
|
+
loop.run_until_complete(_log_production_async(handler))
|
|
295
|
+
loop.close()
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_component(target, message=None, classname=None, body=None):
|
|
299
|
+
"""
|
|
300
|
+
Test a component
|
|
301
|
+
Parameters:
|
|
302
|
+
target: the name of the component
|
|
303
|
+
classname: the name of the class to test
|
|
304
|
+
body: the body of the message
|
|
305
|
+
"""
|
|
306
|
+
iris = _iris.get_iris()
|
|
307
|
+
if not message:
|
|
308
|
+
message = iris.cls("Ens.Request")._New()
|
|
309
|
+
if classname:
|
|
310
|
+
# if classname start with 'iris.' then create an iris object
|
|
311
|
+
if classname.startswith("iris."):
|
|
312
|
+
# strip the iris. prefix
|
|
313
|
+
classname = classname[5:]
|
|
314
|
+
if body:
|
|
315
|
+
message = iris.cls(classname)._New(body)
|
|
316
|
+
else:
|
|
317
|
+
message = iris.cls(classname)._New()
|
|
318
|
+
# else create a python object
|
|
319
|
+
else:
|
|
320
|
+
message = iris.cls("IOP.Message")._New()
|
|
321
|
+
message.classname = classname
|
|
322
|
+
if body:
|
|
323
|
+
message.json = body
|
|
324
|
+
else:
|
|
325
|
+
message.json = _migration_utils.string_to_stream("{}")
|
|
326
|
+
# serialize the message
|
|
327
|
+
serial_message = dispatch_serializer(message)
|
|
328
|
+
response = iris.cls("IOP.Utils").dispatchTestComponent(target, serial_message)
|
|
329
|
+
try:
|
|
330
|
+
deserialized_response = dispatch_deserializer(response)
|
|
331
|
+
except MessageClassImportError:
|
|
332
|
+
if response._IsA("IOP.Message"):
|
|
333
|
+
body = (
|
|
334
|
+
_migration_utils.stream_to_string(response.json)
|
|
335
|
+
if response.type == "Stream"
|
|
336
|
+
else response.json
|
|
337
|
+
)
|
|
338
|
+
deserialized_response = {
|
|
339
|
+
"classname": response.classname,
|
|
340
|
+
"body": body,
|
|
341
|
+
"truncated": False,
|
|
342
|
+
}
|
|
343
|
+
else:
|
|
344
|
+
raise
|
|
345
|
+
return deserialized_response
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _deprecated_static(name: str):
|
|
349
|
+
target = globals()[name]
|
|
350
|
+
if asyncio.iscoroutinefunction(target):
|
|
351
|
+
|
|
352
|
+
@wraps(target)
|
|
353
|
+
async def async_wrapper(*args, **kwargs):
|
|
354
|
+
warnings.warn(
|
|
355
|
+
f"iop.runtime.director._Director.{name}() and iop.Director.{name}() are deprecated; "
|
|
356
|
+
f"use iop.runtime.director.{name}() instead. This facade will be removed in v5.0.",
|
|
357
|
+
DeprecationWarning,
|
|
358
|
+
stacklevel=2,
|
|
359
|
+
)
|
|
360
|
+
return await globals()[name](*args, **kwargs)
|
|
361
|
+
|
|
362
|
+
return staticmethod(async_wrapper)
|
|
363
|
+
|
|
364
|
+
@wraps(target)
|
|
365
|
+
def wrapper(*args, **kwargs):
|
|
366
|
+
warnings.warn(
|
|
367
|
+
f"iop.runtime.director._Director.{name}() and iop.Director.{name}() are deprecated; "
|
|
368
|
+
f"use iop.runtime.director.{name}() instead. This facade will be removed in v5.0.",
|
|
369
|
+
DeprecationWarning,
|
|
370
|
+
stacklevel=2,
|
|
371
|
+
)
|
|
372
|
+
return globals()[name](*args, **kwargs)
|
|
373
|
+
|
|
374
|
+
return staticmethod(wrapper)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class _Director:
|
|
378
|
+
"""Compatibility facade for the local IRIS director API."""
|
|
379
|
+
|
|
380
|
+
_bs = {}
|
|
381
|
+
|
|
382
|
+
def get_business_service(self, target, force_session_id=False):
|
|
383
|
+
"""Get and cache a Python business service by production item name."""
|
|
384
|
+
if target not in self._bs or self._bs[target] is None:
|
|
385
|
+
self._bs[target] = create_python_business_service(target)
|
|
386
|
+
if force_session_id:
|
|
387
|
+
self._bs[target].iris_handle._SessionId = ""
|
|
388
|
+
self._bs[target].iris_handle.ForceSessionId()
|
|
389
|
+
return self._bs[target]
|
|
390
|
+
|
|
391
|
+
create_business_service = _deprecated_static("create_business_service")
|
|
392
|
+
create_python_business_service = _deprecated_static(
|
|
393
|
+
"create_python_business_service"
|
|
394
|
+
)
|
|
395
|
+
start_production_with_log = _deprecated_static("start_production_with_log")
|
|
396
|
+
_start_production_async = _deprecated_static("_start_production_async")
|
|
397
|
+
start_production = _deprecated_static("start_production")
|
|
398
|
+
stop_production = _deprecated_static("stop_production")
|
|
399
|
+
restart_production = _deprecated_static("restart_production")
|
|
400
|
+
shutdown_production = _deprecated_static("shutdown_production")
|
|
401
|
+
update_production = _deprecated_static("update_production")
|
|
402
|
+
start_component = _deprecated_static("start_component")
|
|
403
|
+
stop_component = _deprecated_static("stop_component")
|
|
404
|
+
restart_component = _deprecated_static("restart_component")
|
|
405
|
+
list_productions = _deprecated_static("list_productions")
|
|
406
|
+
status_production = _deprecated_static("status_production")
|
|
407
|
+
set_default_production = _deprecated_static("set_default_production")
|
|
408
|
+
get_default_production = _deprecated_static("get_default_production")
|
|
409
|
+
format_log = _deprecated_static("format_log")
|
|
410
|
+
read_top_log = _deprecated_static("read_top_log")
|
|
411
|
+
read_log = _deprecated_static("read_log")
|
|
412
|
+
_log_production_async = _deprecated_static("_log_production_async")
|
|
413
|
+
log_production_top = _deprecated_static("log_production_top")
|
|
414
|
+
log_production = _deprecated_static("log_production")
|
|
415
|
+
test_component = _deprecated_static("test_component")
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Iterator
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@contextmanager
|
|
11
|
+
def temporary_env(name: str, value: str | None) -> Iterator[None]:
|
|
12
|
+
"""Temporarily set an environment variable, restoring the previous value."""
|
|
13
|
+
missing = object()
|
|
14
|
+
previous = os.environ.get(name, missing)
|
|
15
|
+
if value is not None:
|
|
16
|
+
os.environ[name] = value
|
|
17
|
+
try:
|
|
18
|
+
yield
|
|
19
|
+
finally:
|
|
20
|
+
if previous is missing:
|
|
21
|
+
os.environ.pop(name, None)
|
|
22
|
+
else:
|
|
23
|
+
os.environ[name] = str(previous)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def normalize_path(path: str) -> str:
|
|
27
|
+
return os.path.abspath(os.path.normpath(path))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def prepend_sys_path(path: str) -> str:
|
|
31
|
+
"""Move *path* to the front of sys.path and invalidate import caches."""
|
|
32
|
+
normalized = normalize_path(path)
|
|
33
|
+
while normalized in sys.path:
|
|
34
|
+
sys.path.remove(normalized)
|
|
35
|
+
sys.path.insert(0, normalized)
|
|
36
|
+
importlib.invalidate_caches()
|
|
37
|
+
return normalized
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def remove_sys_path(path: str | None) -> None:
|
|
41
|
+
if not path:
|
|
42
|
+
return
|
|
43
|
+
normalized = normalize_path(path)
|
|
44
|
+
while normalized in sys.path:
|
|
45
|
+
sys.path.remove(normalized)
|
|
46
|
+
importlib.invalidate_caches()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@contextmanager
|
|
50
|
+
def temporary_sys_path(path: str | None) -> Iterator[str | None]:
|
|
51
|
+
"""Temporarily prepend *path* to sys.path, then remove it."""
|
|
52
|
+
if not path:
|
|
53
|
+
yield None
|
|
54
|
+
return
|
|
55
|
+
normalized = prepend_sys_path(path)
|
|
56
|
+
try:
|
|
57
|
+
yield normalized
|
|
58
|
+
finally:
|
|
59
|
+
remove_sys_path(normalized)
|
iop/{_iris.py → runtime/iris.py}
RENAMED
iop/runtime/local.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Local director: thin instance-method wrapper around local IRIS 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
|
+
|
|
12
|
+
from ..migration import utils as _migration_utils
|
|
13
|
+
from . import director as _director
|
|
14
|
+
from .protocol import DirectorProtocol as _DirectorProtocol # noqa: F401 --- IGNORE ---
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _LocalDirector(_DirectorProtocol):
|
|
18
|
+
"""Local director: thin instance-method wrapper around local IRIS calls."""
|
|
19
|
+
|
|
20
|
+
# ------------------------------------------------------------------
|
|
21
|
+
# Production lifecycle
|
|
22
|
+
# ------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
def get_default_production(self) -> str:
|
|
25
|
+
return _director.get_default_production()
|
|
26
|
+
|
|
27
|
+
def set_default_production(self, production_name: str = "") -> None:
|
|
28
|
+
_director.set_default_production(production_name)
|
|
29
|
+
|
|
30
|
+
def list_productions(self) -> dict:
|
|
31
|
+
return _director.list_productions()
|
|
32
|
+
|
|
33
|
+
def status_production(self) -> dict:
|
|
34
|
+
return _director.status_production()
|
|
35
|
+
|
|
36
|
+
def start_production(self, production_name: str | None = None) -> None:
|
|
37
|
+
_director.start_production(production_name)
|
|
38
|
+
|
|
39
|
+
def start_production_with_log(self, production_name: str | None = None) -> None:
|
|
40
|
+
_director.start_production_with_log(production_name)
|
|
41
|
+
|
|
42
|
+
def stop_production(self) -> None:
|
|
43
|
+
_director.stop_production()
|
|
44
|
+
|
|
45
|
+
def shutdown_production(self) -> None:
|
|
46
|
+
_director.shutdown_production()
|
|
47
|
+
|
|
48
|
+
def restart_production(self) -> None:
|
|
49
|
+
_director.restart_production()
|
|
50
|
+
|
|
51
|
+
def update_production(self) -> None:
|
|
52
|
+
_director.update_production()
|
|
53
|
+
|
|
54
|
+
def start_component(self, component_name: str) -> None:
|
|
55
|
+
_director.start_component(component_name)
|
|
56
|
+
|
|
57
|
+
def stop_component(self, component_name: str) -> None:
|
|
58
|
+
_director.stop_component(component_name)
|
|
59
|
+
|
|
60
|
+
def restart_component(self, component_name: str) -> None:
|
|
61
|
+
_director.restart_component(component_name)
|
|
62
|
+
|
|
63
|
+
def list_bindings(self, unused_only: bool = False) -> list[dict]:
|
|
64
|
+
return _migration_utils.list_component_bindings(unused_only=unused_only)
|
|
65
|
+
|
|
66
|
+
def unbind_component(self, iris_classname: str) -> None:
|
|
67
|
+
_migration_utils.unregister_component(iris_classname)
|
|
68
|
+
|
|
69
|
+
# ------------------------------------------------------------------
|
|
70
|
+
# Logging
|
|
71
|
+
# ------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
def log_production_top(self, top: int = 10) -> None:
|
|
74
|
+
_director.log_production_top(top)
|
|
75
|
+
|
|
76
|
+
def log_production(self) -> None:
|
|
77
|
+
_director.log_production()
|
|
78
|
+
|
|
79
|
+
# ------------------------------------------------------------------
|
|
80
|
+
# Test
|
|
81
|
+
# ------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
def test_component(
|
|
84
|
+
self,
|
|
85
|
+
target: str | None,
|
|
86
|
+
message=None,
|
|
87
|
+
classname: str | None = None,
|
|
88
|
+
body: str | dict | None = None,
|
|
89
|
+
restart: bool = True, # ignored locally — included to satisfy DirectorProtocol
|
|
90
|
+
):
|
|
91
|
+
return _director.test_component(target, message, classname, body)
|
|
92
|
+
|
|
93
|
+
# ------------------------------------------------------------------
|
|
94
|
+
# Export
|
|
95
|
+
# ------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
def export_production(self, production_name: str) -> dict:
|
|
98
|
+
return json.loads(_migration_utils.export_production(production_name))
|
|
99
|
+
|
|
100
|
+
def export_production_connections(self, production_name: str) -> dict:
|
|
101
|
+
return _migration_utils.export_production_connections(production_name)
|
|
102
|
+
|
|
103
|
+
def export_production_queue_info(self, production_name: str) -> dict:
|
|
104
|
+
return _migration_utils.export_production_queue_info(production_name)
|
|
105
|
+
|
|
106
|
+
def apply_production_plan(
|
|
107
|
+
self,
|
|
108
|
+
plan: dict,
|
|
109
|
+
allow_destructive: bool = False,
|
|
110
|
+
) -> dict:
|
|
111
|
+
return _migration_utils.apply_production_plan(
|
|
112
|
+
plan,
|
|
113
|
+
allow_destructive=allow_destructive,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# ------------------------------------------------------------------
|
|
117
|
+
# Init / setup
|
|
118
|
+
# ------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
def setup(self, path: str | None = None) -> None:
|
|
121
|
+
_migration_utils.setup(path)
|
|
122
|
+
|
|
123
|
+
# ------------------------------------------------------------------
|
|
124
|
+
# Migrate
|
|
125
|
+
# ------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
def migrate(
|
|
128
|
+
self,
|
|
129
|
+
path: str,
|
|
130
|
+
strict_production_validation: bool = False,
|
|
131
|
+
) -> None:
|
|
132
|
+
_migration_utils.migrate(
|
|
133
|
+
path,
|
|
134
|
+
mode="LOCAL",
|
|
135
|
+
namespace=self.namespace,
|
|
136
|
+
strict_production_validation=strict_production_validation,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# ------------------------------------------------------------------
|
|
140
|
+
# Metadata
|
|
141
|
+
# ------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def namespace(self) -> str:
|
|
145
|
+
return os.getenv("IRISNAMESPACE", "not set")
|
|
@@ -8,7 +8,7 @@ required).
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
from typing import Any,
|
|
11
|
+
from typing import Any, Protocol, runtime_checkable
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@runtime_checkable
|
|
@@ -23,14 +23,19 @@ class DirectorProtocol(Protocol):
|
|
|
23
23
|
def set_default_production(self, production_name: str = "") -> None: ...
|
|
24
24
|
def list_productions(self) -> dict: ...
|
|
25
25
|
def status_production(self) -> dict: ...
|
|
26
|
-
def start_production(self, production_name:
|
|
26
|
+
def start_production(self, production_name: str | None = None) -> None: ...
|
|
27
27
|
def start_production_with_log(
|
|
28
|
-
self, production_name:
|
|
28
|
+
self, production_name: str | None = None
|
|
29
29
|
) -> None: ...
|
|
30
30
|
def stop_production(self) -> None: ...
|
|
31
31
|
def shutdown_production(self) -> None: ...
|
|
32
32
|
def restart_production(self) -> None: ...
|
|
33
33
|
def update_production(self) -> None: ...
|
|
34
|
+
def start_component(self, component_name: str) -> None: ...
|
|
35
|
+
def stop_component(self, component_name: str) -> None: ...
|
|
36
|
+
def restart_component(self, component_name: str) -> None: ...
|
|
37
|
+
def list_bindings(self, unused_only: bool = False) -> list[dict]: ...
|
|
38
|
+
def unbind_component(self, iris_classname: str) -> None: ...
|
|
34
39
|
|
|
35
40
|
# ------------------------------------------------------------------
|
|
36
41
|
# Logging
|
|
@@ -45,10 +50,11 @@ class DirectorProtocol(Protocol):
|
|
|
45
50
|
|
|
46
51
|
def test_component(
|
|
47
52
|
self,
|
|
48
|
-
target:
|
|
53
|
+
target: str | None,
|
|
49
54
|
message=None,
|
|
50
|
-
classname:
|
|
51
|
-
body:
|
|
55
|
+
classname: str | None = None,
|
|
56
|
+
body: str | dict | None = None,
|
|
57
|
+
restart: bool = True,
|
|
52
58
|
) -> Any: ...
|
|
53
59
|
|
|
54
60
|
# ------------------------------------------------------------------
|
|
@@ -56,18 +62,29 @@ class DirectorProtocol(Protocol):
|
|
|
56
62
|
# ------------------------------------------------------------------
|
|
57
63
|
|
|
58
64
|
def export_production(self, production_name: str) -> dict: ...
|
|
65
|
+
def export_production_connections(self, production_name: str) -> dict: ...
|
|
66
|
+
def export_production_queue_info(self, production_name: str) -> dict: ...
|
|
67
|
+
def apply_production_plan(
|
|
68
|
+
self,
|
|
69
|
+
plan: dict,
|
|
70
|
+
allow_destructive: bool = False,
|
|
71
|
+
) -> dict: ...
|
|
59
72
|
|
|
60
73
|
# ------------------------------------------------------------------
|
|
61
74
|
# Init / setup
|
|
62
75
|
# ------------------------------------------------------------------
|
|
63
76
|
|
|
64
|
-
def setup(self, path:
|
|
77
|
+
def setup(self, path: str | None = None) -> None: ...
|
|
65
78
|
|
|
66
79
|
# ------------------------------------------------------------------
|
|
67
80
|
# Migrate
|
|
68
81
|
# ------------------------------------------------------------------
|
|
69
82
|
|
|
70
|
-
def migrate(
|
|
83
|
+
def migrate(
|
|
84
|
+
self,
|
|
85
|
+
path: str,
|
|
86
|
+
strict_production_validation: bool = False,
|
|
87
|
+
) -> None: ...
|
|
71
88
|
|
|
72
89
|
# ------------------------------------------------------------------
|
|
73
90
|
# Metadata
|