iris-pex-embedded-python 3.5.6b10__tar.gz → 3.5.6b11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iris_pex_embedded_python-3.5.6b10/src/iris_pex_embedded_python.egg-info → iris_pex_embedded_python-3.5.6b11}/PKG-INFO +1 -1
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/pyproject.toml +1 -1
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_cli.py +29 -14
- iris_pex_embedded_python-3.5.6b11/src/iop/_director_protocol.py +75 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_local.py +30 -3
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_remote.py +178 -12
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_utils.py +1 -108
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Service/Remote/Rest/v1.cls +1 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11/src/iris_pex_embedded_python.egg-info}/PKG-INFO +1 -1
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iris_pex_embedded_python.egg-info/SOURCES.txt +1 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/LICENSE +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/README.md +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/setup.cfg +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/setup.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/__init__.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/BusinessService.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Common.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Director.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Message.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/PickleMessage.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Test.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/PEX/Utils.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/cls/Grongier/Service/WSGI.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/__init__.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/__main__.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_business_host.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_cli.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_common.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_director.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_utils.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/wsgi/handlers.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/__init__.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/__main__.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_async_request.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_host.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_operation.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_process.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_service.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_common.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_debugpy.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_decorators.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_director.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_dispatch.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_generator_request.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_inbound_adapter.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_iris.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_log_manager.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_message.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_message_validator.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_outbound_adapter.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_private_session_duplex.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_private_session_process.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_serialization.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/BusinessOperation.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/BusinessProcess.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/BusinessService.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Common.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Director.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Duplex/Operation.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Duplex/Process.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Duplex/Service.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Generator/Message/Ack.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Generator/Message/Poll.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Generator/Message/Start.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Generator/Message/StartPickle.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Generator/Message/Stop.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/InboundAdapter.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Message/JSONSchema.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Message.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/OutboundAdapter.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/PickleMessage.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/PrivateSession/Duplex.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/PrivateSession/Message/Ack.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/PrivateSession/Message/Poll.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/PrivateSession/Message/Start.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/PrivateSession/Message/Stop.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Projection.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Service/Remote/Handler.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Service/WSGI.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Test.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Utils.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Wrapper.cls +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/wsgi/handlers.py +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iris_pex_embedded_python.egg-info/dependency_links.txt +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iris_pex_embedded_python.egg-info/entry_points.txt +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iris_pex_embedded_python.egg-info/requires.txt +0 -0
- {iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iris_pex_embedded_python.egg-info/top_level.txt +0 -0
|
@@ -11,7 +11,7 @@ from importlib.metadata import version
|
|
|
11
11
|
|
|
12
12
|
from ._local import _LocalDirector
|
|
13
13
|
from ._remote import _RemoteDirector, get_remote_settings
|
|
14
|
-
from .
|
|
14
|
+
from ._director_protocol import DirectorProtocol
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class CommandType(Enum):
|
|
@@ -67,7 +67,7 @@ class Command:
|
|
|
67
67
|
# or when the -m settings.py file contains REMOTE_SETTINGS.
|
|
68
68
|
# --force-local overrides everything and always uses the local director.
|
|
69
69
|
if self.args.force_local:
|
|
70
|
-
self.director:
|
|
70
|
+
self.director: DirectorProtocol = _LocalDirector()
|
|
71
71
|
self._is_remote = False
|
|
72
72
|
else:
|
|
73
73
|
# Resolve absolute paths for --remote-settings and -m so
|
|
@@ -166,7 +166,13 @@ class Command:
|
|
|
166
166
|
print(json.dumps(dikt, indent=4))
|
|
167
167
|
|
|
168
168
|
def _handle_start(self) -> None:
|
|
169
|
-
|
|
169
|
+
if self.args.start != 'not_set':
|
|
170
|
+
production_name = self.args.start
|
|
171
|
+
else:
|
|
172
|
+
production_name = self.director.get_default_production()
|
|
173
|
+
if not production_name or production_name == "Not defined":
|
|
174
|
+
print("Error: no production name provided and no default production is defined.", file=sys.stderr)
|
|
175
|
+
sys.exit(1)
|
|
170
176
|
if self.args.detach:
|
|
171
177
|
self.director.start_production(production_name)
|
|
172
178
|
print(f"Production {production_name} started")
|
|
@@ -219,7 +225,7 @@ class Command:
|
|
|
219
225
|
if migrate_path is not None:
|
|
220
226
|
if not os.path.isabs(migrate_path):
|
|
221
227
|
migrate_path = os.path.join(os.getcwd(), migrate_path)
|
|
222
|
-
|
|
228
|
+
self.director.migrate(migrate_path)
|
|
223
229
|
|
|
224
230
|
def _handle_log(self) -> None:
|
|
225
231
|
if self.args.log == 'not_set':
|
|
@@ -228,10 +234,8 @@ class Command:
|
|
|
228
234
|
self.director.log_production_top(int(self.args.log))
|
|
229
235
|
|
|
230
236
|
def _handle_init(self) -> None:
|
|
231
|
-
if self.
|
|
232
|
-
|
|
233
|
-
return
|
|
234
|
-
_Utils.setup(None)
|
|
237
|
+
path = None if self.args.init == 'not_set' else self.args.init
|
|
238
|
+
self.director.setup(path)
|
|
235
239
|
|
|
236
240
|
def _handle_help(self) -> None:
|
|
237
241
|
create_parser().print_help()
|
|
@@ -239,9 +243,7 @@ class Command:
|
|
|
239
243
|
print(f"\nMode: REMOTE ({os.environ.get('IOP_URL', 'via IOP_SETTINGS')})")
|
|
240
244
|
try:
|
|
241
245
|
print(f"\nDefault production: {self.director.get_default_production()}")
|
|
242
|
-
|
|
243
|
-
if self._is_remote else os.getenv('IRISNAMESPACE', 'not set'))
|
|
244
|
-
print(f"\nNamespace: {ns}")
|
|
246
|
+
print(f"\nNamespace: {self.director.namespace}")
|
|
245
247
|
except Exception:
|
|
246
248
|
logging.warning("Could not retrieve default production.")
|
|
247
249
|
|
|
@@ -350,12 +352,25 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
350
352
|
return main_parser
|
|
351
353
|
|
|
352
354
|
def main(argv=None) -> None:
|
|
355
|
+
import requests as _requests
|
|
353
356
|
parser = create_parser()
|
|
354
357
|
args = parser.parse_args(argv)
|
|
355
358
|
cmd_args = CommandArgs(**vars(args))
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
+
|
|
360
|
+
try:
|
|
361
|
+
command = Command(cmd_args)
|
|
362
|
+
command.execute()
|
|
363
|
+
except _requests.exceptions.ConnectionError as exc:
|
|
364
|
+
url = os.environ.get("IOP_URL", "")
|
|
365
|
+
msg = f"Connection error: could not reach {url!r}" if url else f"Connection error: {exc}"
|
|
366
|
+
print(f"Error: {msg}", file=sys.stderr)
|
|
367
|
+
sys.exit(1)
|
|
368
|
+
except _requests.exceptions.HTTPError as exc:
|
|
369
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
370
|
+
sys.exit(1)
|
|
371
|
+
except RuntimeError as exc:
|
|
372
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
373
|
+
sys.exit(1)
|
|
359
374
|
sys.exit(0)
|
|
360
375
|
|
|
361
376
|
if __name__ == '__main__':
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Structural interface shared by _LocalDirector and _RemoteDirector.
|
|
2
|
+
|
|
3
|
+
Use ``DirectorProtocol`` for type annotations instead of the concrete union
|
|
4
|
+
``_LocalDirector | _RemoteDirector``. Any object that implements all the
|
|
5
|
+
methods below will satisfy the protocol at type-check time (no inheritance
|
|
6
|
+
required).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Optional, Protocol, runtime_checkable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@runtime_checkable
|
|
15
|
+
class DirectorProtocol(Protocol):
|
|
16
|
+
"""Interface that both _LocalDirector and _RemoteDirector must satisfy."""
|
|
17
|
+
|
|
18
|
+
# ------------------------------------------------------------------
|
|
19
|
+
# Production lifecycle
|
|
20
|
+
# ------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
def get_default_production(self) -> str: ...
|
|
23
|
+
def set_default_production(self, production_name: str = "") -> None: ...
|
|
24
|
+
def list_productions(self) -> dict: ...
|
|
25
|
+
def status_production(self) -> dict: ...
|
|
26
|
+
def start_production(self, production_name: Optional[str] = None) -> None: ...
|
|
27
|
+
def start_production_with_log(self, production_name: Optional[str] = None) -> None: ...
|
|
28
|
+
def stop_production(self) -> None: ...
|
|
29
|
+
def shutdown_production(self) -> None: ...
|
|
30
|
+
def restart_production(self) -> None: ...
|
|
31
|
+
def update_production(self) -> None: ...
|
|
32
|
+
|
|
33
|
+
# ------------------------------------------------------------------
|
|
34
|
+
# Logging
|
|
35
|
+
# ------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
def log_production_top(self, top: int = 10) -> None: ...
|
|
38
|
+
def log_production(self) -> None: ...
|
|
39
|
+
|
|
40
|
+
# ------------------------------------------------------------------
|
|
41
|
+
# Test
|
|
42
|
+
# ------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
def test_component(
|
|
45
|
+
self,
|
|
46
|
+
target: Optional[str],
|
|
47
|
+
message=None,
|
|
48
|
+
classname: Optional[str] = None,
|
|
49
|
+
body: "str | dict | None" = None,
|
|
50
|
+
) -> Any: ...
|
|
51
|
+
|
|
52
|
+
# ------------------------------------------------------------------
|
|
53
|
+
# Export
|
|
54
|
+
# ------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
def export_production(self, production_name: str) -> dict: ...
|
|
57
|
+
|
|
58
|
+
# ------------------------------------------------------------------
|
|
59
|
+
# Init / setup
|
|
60
|
+
# ------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
def setup(self, path: Optional[str] = None) -> None: ...
|
|
63
|
+
|
|
64
|
+
# ------------------------------------------------------------------
|
|
65
|
+
# Migrate
|
|
66
|
+
# ------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
def migrate(self, path: str) -> None: ...
|
|
69
|
+
|
|
70
|
+
# ------------------------------------------------------------------
|
|
71
|
+
# Metadata
|
|
72
|
+
# ------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def namespace(self) -> str: ...
|
|
@@ -6,13 +6,17 @@ _LocalDirector and _RemoteDirector without any branching.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
9
11
|
from typing import Optional
|
|
10
12
|
|
|
11
13
|
from ._director import _Director
|
|
12
14
|
from ._utils import _Utils
|
|
15
|
+
from ._director_protocol import DirectorProtocol as _DirectorProtocol # noqa: F401 --- IGNORE ---
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
class _LocalDirector:
|
|
18
|
+
class _LocalDirector(_DirectorProtocol):
|
|
19
|
+
"""Local director: thin instance-method wrapper around static _Director calls."""
|
|
16
20
|
|
|
17
21
|
# ------------------------------------------------------------------
|
|
18
22
|
# Production lifecycle
|
|
@@ -67,7 +71,8 @@ class _LocalDirector:
|
|
|
67
71
|
target: Optional[str],
|
|
68
72
|
message=None,
|
|
69
73
|
classname: Optional[str] = None,
|
|
70
|
-
body:
|
|
74
|
+
body: "str | dict | None" = None,
|
|
75
|
+
restart: bool = True, # ignored locally — included to satisfy DirectorProtocol
|
|
71
76
|
):
|
|
72
77
|
return _Director.test_component(target, message, classname, body)
|
|
73
78
|
|
|
@@ -76,4 +81,26 @@ class _LocalDirector:
|
|
|
76
81
|
# ------------------------------------------------------------------
|
|
77
82
|
|
|
78
83
|
def export_production(self, production_name: str) -> dict:
|
|
79
|
-
return _Utils.export_production(production_name)
|
|
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)
|
|
99
|
+
|
|
100
|
+
# ------------------------------------------------------------------
|
|
101
|
+
# Metadata
|
|
102
|
+
# ------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def namespace(self) -> str:
|
|
106
|
+
return os.getenv('IRISNAMESPACE', 'not set')
|
|
@@ -16,17 +16,19 @@ import json
|
|
|
16
16
|
import os
|
|
17
17
|
import signal
|
|
18
18
|
import time
|
|
19
|
-
from typing import Any, Dict, List, Optional
|
|
19
|
+
from typing import Any, Dict, List, Optional
|
|
20
20
|
|
|
21
21
|
import requests
|
|
22
22
|
import urllib3
|
|
23
23
|
|
|
24
|
+
from ._director_protocol import DirectorProtocol as _DirectorProtocol # noqa: F401 --- IGNORE ---
|
|
24
25
|
|
|
25
|
-
class _RemoteDirector:
|
|
26
|
-
"""Implements
|
|
26
|
+
class _RemoteDirector(_DirectorProtocol):
|
|
27
|
+
"""Implements DirectorProtocol over the IOP REST API."""
|
|
27
28
|
|
|
28
29
|
def __init__(self, remote_settings: Dict[str, Any]) -> None:
|
|
29
|
-
self.
|
|
30
|
+
self._url = remote_settings["url"].rstrip("/")
|
|
31
|
+
self._base = self._url + "/api/iop"
|
|
30
32
|
self._auth = (
|
|
31
33
|
remote_settings.get("username", ""),
|
|
32
34
|
remote_settings.get("password", ""),
|
|
@@ -40,13 +42,27 @@ class _RemoteDirector:
|
|
|
40
42
|
# Internal helpers
|
|
41
43
|
# ------------------------------------------------------------------
|
|
42
44
|
|
|
45
|
+
@staticmethod
|
|
46
|
+
def _raise_for_status(resp: requests.Response) -> None:
|
|
47
|
+
"""Like resp.raise_for_status() but includes the response body error message."""
|
|
48
|
+
if not resp.ok:
|
|
49
|
+
try:
|
|
50
|
+
body = resp.json()
|
|
51
|
+
error = body.get("error") or body.get("message") or resp.text
|
|
52
|
+
except Exception:
|
|
53
|
+
error = resp.text or resp.reason
|
|
54
|
+
raise requests.exceptions.HTTPError(
|
|
55
|
+
f"{resp.status_code} {resp.reason}: {error}",
|
|
56
|
+
response=resp,
|
|
57
|
+
)
|
|
58
|
+
|
|
43
59
|
def _get(self, path: str, params: Optional[dict] = None) -> Any:
|
|
44
60
|
p = {"namespace": self._namespace, **(params or {})}
|
|
45
61
|
resp = requests.get(
|
|
46
62
|
f"{self._base}{path}", params=p, auth=self._auth,
|
|
47
63
|
verify=self._verify, timeout=30,
|
|
48
64
|
)
|
|
49
|
-
|
|
65
|
+
self._raise_for_status(resp)
|
|
50
66
|
return resp.json()
|
|
51
67
|
|
|
52
68
|
def _post(self, path: str, body: Optional[dict] = None) -> Any:
|
|
@@ -55,7 +71,7 @@ class _RemoteDirector:
|
|
|
55
71
|
params={"namespace": self._namespace},
|
|
56
72
|
auth=self._auth, verify=self._verify, timeout=30,
|
|
57
73
|
)
|
|
58
|
-
|
|
74
|
+
self._raise_for_status(resp)
|
|
59
75
|
return resp.json()
|
|
60
76
|
|
|
61
77
|
def _put(self, path: str, body: Optional[dict] = None) -> Any:
|
|
@@ -64,7 +80,7 @@ class _RemoteDirector:
|
|
|
64
80
|
params={"namespace": self._namespace},
|
|
65
81
|
auth=self._auth, verify=self._verify, timeout=30,
|
|
66
82
|
)
|
|
67
|
-
|
|
83
|
+
self._raise_for_status(resp)
|
|
68
84
|
return resp.json()
|
|
69
85
|
|
|
70
86
|
def _check_error(self, data: Any) -> Any:
|
|
@@ -131,11 +147,11 @@ class _RemoteDirector:
|
|
|
131
147
|
def shutdown_production(self) -> None:
|
|
132
148
|
self._check_error(self._post("/kill"))
|
|
133
149
|
|
|
134
|
-
def restart_production(self) ->
|
|
135
|
-
|
|
150
|
+
def restart_production(self) -> None:
|
|
151
|
+
self._check_error(self._post("/restart"))
|
|
136
152
|
|
|
137
|
-
def update_production(self) ->
|
|
138
|
-
|
|
153
|
+
def update_production(self) -> None:
|
|
154
|
+
self._check_error(self._post("/update"))
|
|
139
155
|
|
|
140
156
|
# ------------------------------------------------------------------
|
|
141
157
|
# Logging
|
|
@@ -190,7 +206,7 @@ class _RemoteDirector:
|
|
|
190
206
|
target: Optional[str],
|
|
191
207
|
message=None, # ignored remotely — not serialisable over HTTP
|
|
192
208
|
classname: Optional[str] = None,
|
|
193
|
-
body:
|
|
209
|
+
body: "str | dict | None" = None,
|
|
194
210
|
restart: bool = True,
|
|
195
211
|
) -> dict:
|
|
196
212
|
"""Returns a dict: {"classname": "...", "body": "...", "truncated": false}.
|
|
@@ -223,6 +239,156 @@ class _RemoteDirector:
|
|
|
223
239
|
self._get("/export", {"production": production_name})
|
|
224
240
|
)
|
|
225
241
|
|
|
242
|
+
# ------------------------------------------------------------------
|
|
243
|
+
# Migrate
|
|
244
|
+
# ------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
def migrate(self, path: str) -> None:
|
|
247
|
+
"""Upload .py and .cls files from *path*'s folder to remote IRIS via the IOP migrate API.
|
|
248
|
+
|
|
249
|
+
*path* must be an absolute path to a ``settings.py`` file whose directory
|
|
250
|
+
(and sub-directories) will be walked for ``.py`` / ``.cls`` files.
|
|
251
|
+
``REMOTE_SETTINGS`` fields (package, namespace, remote_folder) are read
|
|
252
|
+
from that same settings file when present; otherwise the director's own
|
|
253
|
+
namespace / defaults are used.
|
|
254
|
+
"""
|
|
255
|
+
import importlib.util
|
|
256
|
+
|
|
257
|
+
folder = os.path.dirname(path)
|
|
258
|
+
|
|
259
|
+
# Try to read optional keys from the settings file
|
|
260
|
+
package = 'python'
|
|
261
|
+
remote_folder = ''
|
|
262
|
+
try:
|
|
263
|
+
spec = importlib.util.spec_from_file_location('_iop_migrate_settings', path)
|
|
264
|
+
mod = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
|
|
265
|
+
spec.loader.exec_module(mod) # type: ignore[union-attr]
|
|
266
|
+
rs = getattr(mod, 'REMOTE_SETTINGS', {})
|
|
267
|
+
package = rs.get('package', package)
|
|
268
|
+
remote_folder = rs.get('remote_folder', remote_folder)
|
|
269
|
+
except Exception:
|
|
270
|
+
pass
|
|
271
|
+
|
|
272
|
+
body: List[dict] = []
|
|
273
|
+
for dirpath, _, filenames in os.walk(folder):
|
|
274
|
+
for fname in sorted(filenames):
|
|
275
|
+
if not (fname.endswith('.py') or fname.endswith('.cls')):
|
|
276
|
+
continue
|
|
277
|
+
full = os.path.join(dirpath, fname)
|
|
278
|
+
rel = os.path.relpath(full, folder).replace(os.sep, '/')
|
|
279
|
+
with open(full, encoding='utf-8') as fh:
|
|
280
|
+
body.append({'name': rel, 'data': fh.read()})
|
|
281
|
+
|
|
282
|
+
payload = {
|
|
283
|
+
'namespace': self._namespace,
|
|
284
|
+
'package': package,
|
|
285
|
+
'remote_folder': remote_folder,
|
|
286
|
+
'body': body,
|
|
287
|
+
}
|
|
288
|
+
resp = requests.put(
|
|
289
|
+
f"{self._base}/migrate",
|
|
290
|
+
json=payload,
|
|
291
|
+
params={"namespace": self._namespace},
|
|
292
|
+
auth=self._auth,
|
|
293
|
+
verify=self._verify,
|
|
294
|
+
timeout=30,
|
|
295
|
+
)
|
|
296
|
+
self._raise_for_status(resp)
|
|
297
|
+
# Server returns $$$OK (the integer 1) on success — not meaningful to print
|
|
298
|
+
|
|
299
|
+
# ------------------------------------------------------------------
|
|
300
|
+
# Init / setup — uploads .cls files via the Atelier API
|
|
301
|
+
# ------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
def setup(self, path: Optional[str] = None) -> None:
|
|
304
|
+
"""Upload and compile IOP .cls files to remote IRIS via the Atelier REST API.
|
|
305
|
+
|
|
306
|
+
When *path* is ``None`` the bundled ``iop/cls/`` directory (and the
|
|
307
|
+
optional ``grongier/cls/`` directory for retrocompatibility) is used.
|
|
308
|
+
Any explicit *path* must point to a directory containing ``.cls`` files.
|
|
309
|
+
"""
|
|
310
|
+
import importlib.resources
|
|
311
|
+
|
|
312
|
+
paths_to_upload: List[str] = []
|
|
313
|
+
if path is None:
|
|
314
|
+
try:
|
|
315
|
+
paths_to_upload.append(
|
|
316
|
+
str(importlib.resources.files('iop').joinpath('cls'))
|
|
317
|
+
)
|
|
318
|
+
except ModuleNotFoundError:
|
|
319
|
+
pass
|
|
320
|
+
try: # retrocompatibility with the grongier.pex package
|
|
321
|
+
paths_to_upload.append(
|
|
322
|
+
str(importlib.resources.files('grongier').joinpath('cls'))
|
|
323
|
+
)
|
|
324
|
+
except ModuleNotFoundError:
|
|
325
|
+
pass
|
|
326
|
+
else:
|
|
327
|
+
paths_to_upload.append(path)
|
|
328
|
+
|
|
329
|
+
atelier_base = f"{self._url}/api/atelier/v1"
|
|
330
|
+
doc_names: List[str] = []
|
|
331
|
+
|
|
332
|
+
for cls_root in paths_to_upload:
|
|
333
|
+
for dirpath, _, filenames in os.walk(cls_root):
|
|
334
|
+
for fname in sorted(filenames):
|
|
335
|
+
if not fname.endswith('.cls'):
|
|
336
|
+
continue
|
|
337
|
+
full_path = os.path.join(dirpath, fname)
|
|
338
|
+
doc_name = os.path.relpath(full_path, cls_root).replace(os.sep, '/')
|
|
339
|
+
with open(full_path, encoding='utf-8') as fh:
|
|
340
|
+
content = fh.read().splitlines()
|
|
341
|
+
resp = requests.put(
|
|
342
|
+
f"{atelier_base}/{self._namespace}/doc/{doc_name}",
|
|
343
|
+
json={"enc": False, "content": content},
|
|
344
|
+
params={"ignoreConflict": "1"},
|
|
345
|
+
auth=self._auth,
|
|
346
|
+
verify=self._verify,
|
|
347
|
+
timeout=30,
|
|
348
|
+
)
|
|
349
|
+
self._raise_for_status(resp)
|
|
350
|
+
result = resp.json()
|
|
351
|
+
doc_status = (result.get('result') or {}).get('status') or ''
|
|
352
|
+
if doc_status:
|
|
353
|
+
raise RuntimeError(f"Error uploading {doc_name}: {doc_status}")
|
|
354
|
+
doc_names.append(doc_name)
|
|
355
|
+
print(f"Uploaded: {doc_name}")
|
|
356
|
+
|
|
357
|
+
if not doc_names:
|
|
358
|
+
raise RuntimeError("No .cls files found to upload.")
|
|
359
|
+
|
|
360
|
+
# Compile all uploaded documents in one request
|
|
361
|
+
resp = requests.post(
|
|
362
|
+
f"{atelier_base}/{self._namespace}/action/compile",
|
|
363
|
+
json=doc_names,
|
|
364
|
+
params={"flags": "cuk"},
|
|
365
|
+
auth=self._auth,
|
|
366
|
+
verify=self._verify,
|
|
367
|
+
timeout=120,
|
|
368
|
+
)
|
|
369
|
+
self._raise_for_status(resp)
|
|
370
|
+
result = resp.json()
|
|
371
|
+
for line in result.get('console', []):
|
|
372
|
+
if line:
|
|
373
|
+
print(line)
|
|
374
|
+
errors = result.get('status', {}).get('errors', [])
|
|
375
|
+
if errors:
|
|
376
|
+
raise RuntimeError(f"Compilation errors: {errors}")
|
|
377
|
+
print(
|
|
378
|
+
"\n.cls files uploaded and compiled successfully."
|
|
379
|
+
"\nNext step: ensure the 'iop' Python package is installed on the IRIS server:"
|
|
380
|
+
"\n python3 -m pip install iris-pex-embedded-python"
|
|
381
|
+
"\nThis is required for full IOP Support; without it, only the migrate() and export_production() methods will work remotely."
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# ------------------------------------------------------------------
|
|
385
|
+
# Metadata
|
|
386
|
+
# ------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def namespace(self) -> str:
|
|
390
|
+
return self._namespace
|
|
391
|
+
|
|
226
392
|
|
|
227
393
|
# ------------------------------------------------------------------
|
|
228
394
|
# Shared helpers
|
|
@@ -6,25 +6,14 @@ import importlib.resources
|
|
|
6
6
|
import json
|
|
7
7
|
import inspect
|
|
8
8
|
import ast
|
|
9
|
-
from typing import Any, Dict, Optional,
|
|
9
|
+
from typing import Any, Dict, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
import xmltodict
|
|
12
|
-
import requests
|
|
13
12
|
from pydantic import TypeAdapter
|
|
14
13
|
|
|
15
14
|
from . import _iris
|
|
16
15
|
from ._message import _Message, _PydanticMessage
|
|
17
16
|
|
|
18
|
-
class RemoteSettings(TypedDict, total=False):
|
|
19
|
-
"""Typed dictionary for remote migration settings."""
|
|
20
|
-
url: str # Required: the host url to connect to
|
|
21
|
-
namespace: str # Optional: the namespace to use (default: 'USER')
|
|
22
|
-
package: str # Optional: the package to use (default: 'python')
|
|
23
|
-
remote_folder: str # Optional: the folder to use (default: '')
|
|
24
|
-
username: str # Optional: the username to use to connect (default: '')
|
|
25
|
-
password: str # Optional: the password to use to connect (default: '')
|
|
26
|
-
verify_ssl: bool # Optional: verify SSL certificates (default: True, set to False for self-signed certs)
|
|
27
|
-
|
|
28
17
|
class _Utils():
|
|
29
18
|
@staticmethod
|
|
30
19
|
def raise_on_error(sc):
|
|
@@ -265,102 +254,6 @@ class _Utils():
|
|
|
265
254
|
|
|
266
255
|
return module
|
|
267
256
|
|
|
268
|
-
@staticmethod
|
|
269
|
-
def migrate_remote(filename=None, force_local=False):
|
|
270
|
-
"""
|
|
271
|
-
Read a settings file from the filename
|
|
272
|
-
If the settings.py file has a key 'REMOTE_SETTINGS' then it will use the value of that key
|
|
273
|
-
as the remote host to connect to.
|
|
274
|
-
the REMOTE_SETTINGS is a RemoteSettings dictionary with the following keys:
|
|
275
|
-
* 'url': the host url to connect to (mandatory)
|
|
276
|
-
* 'namespace': the namespace to use (optional, default is 'USER')
|
|
277
|
-
* 'package': the package to use (optional, default is 'python')
|
|
278
|
-
* 'remote_folder': the folder to use (optional, default is '')
|
|
279
|
-
* 'username': the username to use to connect (optional, default is '')
|
|
280
|
-
* 'password': the password to use to connect (optional, default is '')
|
|
281
|
-
* 'verify_ssl': verify SSL certificates (optional, default is True)
|
|
282
|
-
|
|
283
|
-
The remote host is a rest API that will be used to register the components
|
|
284
|
-
The payload will be a json object with the following keys:
|
|
285
|
-
* 'namespace': the namespace to use
|
|
286
|
-
* 'package': the package to use
|
|
287
|
-
* 'body': the body of the request, it will be a json object with the following keys:
|
|
288
|
-
* 'name': name of the file
|
|
289
|
-
* 'data': the data of the file, it will be an UTF-8 encoded string
|
|
290
|
-
|
|
291
|
-
'body' will be constructed with all the files in the folder if the folder is not empty else use root folder of settings.py
|
|
292
|
-
|
|
293
|
-
Args:
|
|
294
|
-
filename: Path to the settings file
|
|
295
|
-
force_local: If True, skip remote migration even if REMOTE_SETTINGS is present
|
|
296
|
-
"""
|
|
297
|
-
settings, path = _Utils._load_settings(filename)
|
|
298
|
-
remote_settings: Optional[RemoteSettings] = getattr(settings, 'REMOTE_SETTINGS', None) if settings else None
|
|
299
|
-
|
|
300
|
-
if not remote_settings or force_local:
|
|
301
|
-
_Utils.migrate(filename)
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
# Validate required fields
|
|
305
|
-
if 'url' not in remote_settings:
|
|
306
|
-
raise ValueError("REMOTE_SETTINGS must contain 'url' field")
|
|
307
|
-
|
|
308
|
-
# prepare the payload with defaults
|
|
309
|
-
payload = {
|
|
310
|
-
'namespace': remote_settings.get('namespace', 'USER'),
|
|
311
|
-
'package': remote_settings.get('package', 'python'),
|
|
312
|
-
'remote_folder': remote_settings.get('remote_folder', ''),
|
|
313
|
-
'body': []
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
# get the folder to register
|
|
317
|
-
folder = _Utils._get_folder_path(filename, path)
|
|
318
|
-
|
|
319
|
-
# iterate over all files in the folder
|
|
320
|
-
for root, _, files in os.walk(folder):
|
|
321
|
-
for file in files:
|
|
322
|
-
if file.endswith('.py') or file.endswith('.cls'):
|
|
323
|
-
file_path = os.path.join(root, file)
|
|
324
|
-
relative_path = os.path.relpath(file_path, folder)
|
|
325
|
-
# Normalize path separators for cross-platform compatibility
|
|
326
|
-
relative_path = relative_path.replace(os.sep, '/')
|
|
327
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
328
|
-
data = f.read()
|
|
329
|
-
payload['body'].append({
|
|
330
|
-
'name': relative_path,
|
|
331
|
-
'data': data
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
# Get SSL verification setting (default to True for security)
|
|
335
|
-
verify_ssl = remote_settings.get('verify_ssl', True)
|
|
336
|
-
|
|
337
|
-
# Disable SSL warnings if verify_ssl is False
|
|
338
|
-
if not verify_ssl:
|
|
339
|
-
import urllib3
|
|
340
|
-
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
341
|
-
|
|
342
|
-
# send the request to the remote settings
|
|
343
|
-
try:
|
|
344
|
-
response = requests.put(
|
|
345
|
-
url=f"{remote_settings['url']}/api/iop/migrate",
|
|
346
|
-
json=payload,
|
|
347
|
-
headers={
|
|
348
|
-
'Content-Type': 'application/json',
|
|
349
|
-
'Accept': 'application/json'
|
|
350
|
-
},
|
|
351
|
-
auth=(remote_settings.get('username', ''), remote_settings.get('password', '')),
|
|
352
|
-
timeout=10,
|
|
353
|
-
verify=verify_ssl
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
print(f"Response from remote migration:\n{response.text}")
|
|
357
|
-
|
|
358
|
-
response.raise_for_status() # Raise an error for bad responses
|
|
359
|
-
except requests.exceptions.SSLError as e:
|
|
360
|
-
print(f"SSL Error: {e}")
|
|
361
|
-
print("If you're using a self-signed certificate, set 'verify_ssl': False in REMOTE_SETTINGS")
|
|
362
|
-
raise
|
|
363
|
-
|
|
364
257
|
@staticmethod
|
|
365
258
|
def migrate(filename=None):
|
|
366
259
|
"""
|
|
@@ -81,6 +81,7 @@ ClassMethod PostStart() As %Status
|
|
|
81
81
|
Set production = dyna.%Get("production")
|
|
82
82
|
If ns '= "" { Do ..NamespaceCheck(ns) New $NAMESPACE Set $NAMESPACE = ns }
|
|
83
83
|
If production = "" { Set production = $Get(^Ens.Configuration("csp","LastProduction")) }
|
|
84
|
+
If production = "" { Throw $$$ERROR("NoProductionSpecified") }
|
|
84
85
|
$$$ThrowOnError(##class(Ens.Director).StartProduction(production))
|
|
85
86
|
Return ..%WriteResponse({"status": "started", "production": (production)}.%ToJSON())
|
|
86
87
|
} Catch ex {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/__init__.py
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_cli.py
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/grongier/pex/_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_async_request.py
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_host.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_process.py
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_business_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_decorators.py
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_director.py
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_dispatch.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_inbound_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_log_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_outbound_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/_serialization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Common.cls
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Director.cls
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Message.cls
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Test.cls
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Utils.cls
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/cls/IOP/Wrapper.cls
RENAMED
|
File without changes
|
{iris_pex_embedded_python-3.5.6b10 → iris_pex_embedded_python-3.5.6b11}/src/iop/wsgi/handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|