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.
Files changed (119) hide show
  1. iop/__init__.py +276 -18
  2. iop/__main__.py +3 -3
  3. iop/cli/__init__.py +1 -0
  4. iop/cli/formatting.py +45 -0
  5. iop/{_cli.py → cli/main.py} +238 -216
  6. iop/cli/parser.py +165 -0
  7. iop/cli/types.py +73 -0
  8. iop/cls/IOP/Common.cls +11 -2
  9. iop/cls/IOP/PrivateSession/Duplex.cls +1 -1
  10. iop/cls/IOP/Service/Remote/Rest/v1.cls +152 -5
  11. iop/cls/IOP/Utils.cls +814 -7
  12. iop/cls/IOP/Wrapper.cls +1 -1
  13. iop/components/__init__.py +16 -0
  14. iop/{_async_request.py → components/async_request.py} +7 -7
  15. iop/components/business_host.py +363 -0
  16. iop/components/business_operation.py +84 -0
  17. iop/{_business_process.py → components/business_process.py} +145 -125
  18. iop/components/business_service.py +106 -0
  19. iop/{_common.py → components/common.py} +160 -164
  20. iop/{_debugpy.py → components/debugpy.py} +11 -11
  21. iop/{_generator_request.py → components/generator_request.py} +4 -4
  22. iop/{_inbound_adapter.py → components/inbound_adapter.py} +10 -18
  23. iop/{_log_manager.py → components/log_manager.py} +1 -1
  24. iop/{_outbound_adapter.py → components/outbound_adapter.py} +1 -1
  25. iop/components/polling_business_service.py +48 -0
  26. iop/{_private_session_duplex.py → components/private_session_duplex.py} +7 -9
  27. iop/{_private_session_process.py → components/private_session_process.py} +2 -2
  28. iop/{_settings.py → components/settings.py} +23 -3
  29. iop/messages/__init__.py +7 -0
  30. iop/messages/base.py +61 -0
  31. iop/{_decorators.py → messages/decorators.py} +8 -2
  32. iop/messages/dispatch.py +351 -0
  33. iop/{_persistent_message.py → messages/persistent.py} +24 -19
  34. iop/{_serialization.py → messages/serialization.py} +34 -21
  35. iop/{_message_validator.py → messages/validation.py} +15 -13
  36. iop/migration/__init__.py +1 -0
  37. iop/migration/io.py +61 -0
  38. iop/migration/manifest.py +534 -0
  39. iop/migration/plans.py +80 -0
  40. iop/migration/utils.py +1126 -0
  41. iop/production/__init__.py +55 -0
  42. iop/production/actions.py +525 -0
  43. iop/production/common.py +103 -0
  44. iop/production/component.py +234 -0
  45. iop/production/declarations.py +190 -0
  46. iop/production/declarative.py +214 -0
  47. iop/production/diff.py +327 -0
  48. iop/production/import_.py +296 -0
  49. iop/production/inspection.py +92 -0
  50. iop/production/model.py +1143 -0
  51. iop/production/planning.py +678 -0
  52. iop/production/reconstruction.py +341 -0
  53. iop/production/rendering.py +610 -0
  54. iop/production/runtime.py +91 -0
  55. iop/production/source_inference.py +729 -0
  56. iop/production/types.py +546 -0
  57. iop/production/validation.py +452 -0
  58. iop/runtime/__init__.py +1 -0
  59. iop/runtime/director.py +415 -0
  60. iop/runtime/environment.py +59 -0
  61. iop/{_iris.py → runtime/iris.py} +2 -2
  62. iop/runtime/local.py +145 -0
  63. iop/{_director_protocol.py → runtime/protocol.py} +25 -8
  64. iop/runtime/remote/__init__.py +12 -0
  65. iop/runtime/remote/client.py +90 -0
  66. iop/runtime/remote/director.py +283 -0
  67. iop/runtime/remote/migration.py +57 -0
  68. iop/runtime/remote/settings.py +69 -0
  69. iop/runtime/remote/setup.py +72 -0
  70. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/METADATA +11 -2
  71. iris_pex_embedded_python-4.0.0.dist-info/RECORD +99 -0
  72. iris_pex_embedded_python-4.0.0.dist-info/entry_points.txt +2 -0
  73. iris_pex_embedded_python-4.0.0.dist-info/top_level.txt +1 -0
  74. grongier/__init__.py +0 -0
  75. grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -8
  76. grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -13
  77. grongier/cls/Grongier/PEX/BusinessService.cls +0 -8
  78. grongier/cls/Grongier/PEX/Common.cls +0 -10
  79. grongier/cls/Grongier/PEX/Director.cls +0 -10
  80. grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -4
  81. grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -13
  82. grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -4
  83. grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -8
  84. grongier/cls/Grongier/PEX/Message.cls +0 -13
  85. grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -8
  86. grongier/cls/Grongier/PEX/PickleMessage.cls +0 -13
  87. grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -8
  88. grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -14
  89. grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -14
  90. grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -14
  91. grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -14
  92. grongier/cls/Grongier/PEX/Test.cls +0 -10
  93. grongier/cls/Grongier/PEX/Utils.cls +0 -10
  94. grongier/cls/Grongier/Service/WSGI.cls +0 -4
  95. grongier/pex/__init__.py +0 -75
  96. grongier/pex/__main__.py +0 -4
  97. grongier/pex/_business_host.py +0 -1
  98. grongier/pex/_cli.py +0 -4
  99. grongier/pex/_common.py +0 -1
  100. grongier/pex/_director.py +0 -1
  101. grongier/pex/_utils.py +0 -1
  102. grongier/pex/wsgi/handlers.py +0 -104
  103. iop/_business_host.py +0 -322
  104. iop/_business_operation.py +0 -77
  105. iop/_business_service.py +0 -67
  106. iop/_director.py +0 -333
  107. iop/_dispatch.py +0 -194
  108. iop/_local.py +0 -106
  109. iop/_message.py +0 -45
  110. iop/_polling_business_service.py +0 -6
  111. iop/_remote.py +0 -473
  112. iop/_utils.py +0 -912
  113. iop/cls/IOP/Service/WSGI.cls +0 -310
  114. iop/wsgi/handlers.py +0 -112
  115. iris_pex_embedded_python-3.7.2b2.dist-info/RECORD +0 -96
  116. iris_pex_embedded_python-3.7.2b2.dist-info/entry_points.txt +0 -2
  117. iris_pex_embedded_python-3.7.2b2.dist-info/top_level.txt +0 -2
  118. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/WHEEL +0 -0
  119. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -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)
@@ -1,8 +1,8 @@
1
1
  import os
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
 
4
4
 
5
- def get_iris(namespace: Optional[str] = None) -> Any:
5
+ def get_iris(namespace: str | None = None) -> Any:
6
6
  if namespace:
7
7
  os.environ["IRISNAMESPACE"] = namespace
8
8
  import iris
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, Optional, Protocol, runtime_checkable
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: Optional[str] = None) -> None: ...
26
+ def start_production(self, production_name: str | None = None) -> None: ...
27
27
  def start_production_with_log(
28
- self, production_name: Optional[str] = None
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: Optional[str],
53
+ target: str | None,
49
54
  message=None,
50
- classname: Optional[str] = None,
51
- body: "str | dict | None" = None,
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: Optional[str] = None) -> None: ...
77
+ def setup(self, path: str | None = None) -> None: ...
65
78
 
66
79
  # ------------------------------------------------------------------
67
80
  # Migrate
68
81
  # ------------------------------------------------------------------
69
82
 
70
- def migrate(self, path: str) -> None: ...
83
+ def migrate(
84
+ self,
85
+ path: str,
86
+ strict_production_validation: bool = False,
87
+ ) -> None: ...
71
88
 
72
89
  # ------------------------------------------------------------------
73
90
  # Metadata