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/cls/IOP/Wrapper.cls
CHANGED
|
@@ -13,7 +13,7 @@ ClassMethod Import(
|
|
|
13
13
|
do $system.Python.Debugging(1)
|
|
14
14
|
if debugPort > 0 {
|
|
15
15
|
|
|
16
|
-
set debugpy = ##class(%SYS.Python).Import("iop.
|
|
16
|
+
set debugpy = ##class(%SYS.Python).Import("iop.components.debugpy")
|
|
17
17
|
do debugpy."debugpy_in_iris"($zu(12),debugPort)
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .business_host import _BusinessHost as _BusinessHost
|
|
2
|
+
from .business_operation import _BusinessOperation as _BusinessOperation
|
|
3
|
+
from .business_process import _BusinessProcess as _BusinessProcess
|
|
4
|
+
from .business_service import _BusinessService as _BusinessService
|
|
5
|
+
from .common import _Common as _Common
|
|
6
|
+
from .inbound_adapter import _InboundAdapter as _InboundAdapter
|
|
7
|
+
from .outbound_adapter import _OutboundAdapter as _OutboundAdapter
|
|
8
|
+
from .polling_business_service import (
|
|
9
|
+
_PollingBusinessServiceMixin as _PollingBusinessServiceMixin,
|
|
10
|
+
)
|
|
11
|
+
from .private_session_duplex import _PrivateSessionDuplex as _PrivateSessionDuplex
|
|
12
|
+
from .private_session_process import _PrivateSessionProcess as _PrivateSessionProcess
|
|
13
|
+
from .settings import Category as Category
|
|
14
|
+
from .settings import Setting as Setting
|
|
15
|
+
from .settings import controls as controls
|
|
16
|
+
from .settings import setting as setting
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
|
-
from . import
|
|
5
|
-
from .
|
|
6
|
-
from
|
|
4
|
+
from ..messages.base import _Message as Message
|
|
5
|
+
from ..messages.dispatch import dispatch_deserializer, dispatch_serializer
|
|
6
|
+
from ..runtime import iris as _iris
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class AsyncRequest(asyncio.Future):
|
|
@@ -16,10 +16,10 @@ class AsyncRequest(asyncio.Future):
|
|
|
16
16
|
def __init__(
|
|
17
17
|
self,
|
|
18
18
|
target: str,
|
|
19
|
-
request:
|
|
19
|
+
request: Message | Any,
|
|
20
20
|
timeout: int = -1,
|
|
21
|
-
description:
|
|
22
|
-
host:
|
|
21
|
+
description: str | None = None,
|
|
22
|
+
host: Any | None = None,
|
|
23
23
|
) -> None:
|
|
24
24
|
super().__init__()
|
|
25
25
|
self.target = target
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import textwrap
|
|
3
|
+
from inspect import getsource
|
|
4
|
+
from typing import Any, cast
|
|
5
|
+
|
|
6
|
+
from ..messages.base import _Message as Message
|
|
7
|
+
from ..messages.decorators import (
|
|
8
|
+
input_deserializer,
|
|
9
|
+
input_serializer_param,
|
|
10
|
+
output_deserializer,
|
|
11
|
+
output_serializer,
|
|
12
|
+
)
|
|
13
|
+
from ..messages.dispatch import (
|
|
14
|
+
dispatch_deserializer,
|
|
15
|
+
dispatch_message,
|
|
16
|
+
dispatch_serializer,
|
|
17
|
+
)
|
|
18
|
+
from ..production import TargetSettingRef, resolve_target
|
|
19
|
+
from ..runtime import iris as _iris
|
|
20
|
+
from .async_request import AsyncRequest
|
|
21
|
+
from .common import _Common
|
|
22
|
+
from .generator_request import _GeneratorRequest
|
|
23
|
+
|
|
24
|
+
_CONNECTION_METHODS = {
|
|
25
|
+
"send_request_sync",
|
|
26
|
+
"send_request_async",
|
|
27
|
+
"send_request_async_ng",
|
|
28
|
+
"send_generator_request",
|
|
29
|
+
}
|
|
30
|
+
_UNRESOLVED = object()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _call_name(node: ast.AST) -> str | None:
|
|
34
|
+
if isinstance(node, ast.Attribute):
|
|
35
|
+
return node.attr
|
|
36
|
+
if isinstance(node, ast.Name):
|
|
37
|
+
return node.id
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _call_target_node(call: ast.Call) -> ast.AST | None:
|
|
42
|
+
for keyword in call.keywords:
|
|
43
|
+
if keyword.arg == "target":
|
|
44
|
+
return keyword.value
|
|
45
|
+
if call.args:
|
|
46
|
+
return call.args[0]
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _resolve_connection_target(host: Any, node: ast.AST) -> Any:
|
|
51
|
+
try:
|
|
52
|
+
value = ast.literal_eval(node)
|
|
53
|
+
except (ValueError, SyntaxError):
|
|
54
|
+
value = _UNRESOLVED
|
|
55
|
+
if isinstance(value, str):
|
|
56
|
+
return value
|
|
57
|
+
|
|
58
|
+
if isinstance(node, ast.Name):
|
|
59
|
+
return getattr(host, node.id, _UNRESOLVED)
|
|
60
|
+
|
|
61
|
+
if isinstance(node, ast.Attribute):
|
|
62
|
+
return _resolve_attribute_target(host, node)
|
|
63
|
+
|
|
64
|
+
return _UNRESOLVED
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _resolve_attribute_target(host: Any, node: ast.Attribute) -> Any:
|
|
68
|
+
chain = []
|
|
69
|
+
current: ast.AST = node
|
|
70
|
+
while isinstance(current, ast.Attribute):
|
|
71
|
+
chain.append(current.attr)
|
|
72
|
+
current = current.value
|
|
73
|
+
|
|
74
|
+
if isinstance(current, ast.Name):
|
|
75
|
+
if current.id == "self":
|
|
76
|
+
value = host
|
|
77
|
+
else:
|
|
78
|
+
value = getattr(host, current.id, _UNRESOLVED)
|
|
79
|
+
else:
|
|
80
|
+
return _UNRESOLVED
|
|
81
|
+
|
|
82
|
+
for attr in reversed(chain):
|
|
83
|
+
if value is _UNRESOLVED:
|
|
84
|
+
return _UNRESOLVED
|
|
85
|
+
value = getattr(value, attr, _UNRESOLVED)
|
|
86
|
+
return value
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class _BusinessHost(_Common):
|
|
90
|
+
"""Base class for business components that defines common methods.
|
|
91
|
+
|
|
92
|
+
This is a superclass for BusinessService, BusinessProcess, and BusinessOperation that
|
|
93
|
+
defines common functionality like message serialization/deserialization and request handling.
|
|
94
|
+
App code should normally call send_request_* with target() attributes such
|
|
95
|
+
as self.Output instead of hard-coded component names. See
|
|
96
|
+
docs/cookbooks/production-settings-and-targets.md.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
buffer: int = 64000
|
|
100
|
+
DISPATCH: list[tuple[str, str]] = []
|
|
101
|
+
|
|
102
|
+
@input_serializer_param(1, "request")
|
|
103
|
+
@output_deserializer
|
|
104
|
+
def send_request_sync(
|
|
105
|
+
self,
|
|
106
|
+
target: str | TargetSettingRef,
|
|
107
|
+
request: Message | Any,
|
|
108
|
+
timeout: int = -1,
|
|
109
|
+
description: str | None = None,
|
|
110
|
+
) -> Any:
|
|
111
|
+
"""Purpose:
|
|
112
|
+
Send a message to a target component and wait for the response.
|
|
113
|
+
|
|
114
|
+
Use when:
|
|
115
|
+
The caller needs the target response before continuing.
|
|
116
|
+
|
|
117
|
+
Lifecycle:
|
|
118
|
+
IoP serializes request, calls the IRIS synchronous dispatch API, and
|
|
119
|
+
deserializes the response before returning.
|
|
120
|
+
|
|
121
|
+
Best practices:
|
|
122
|
+
Pass a target() attribute such as self.Output so the route is
|
|
123
|
+
configurable in the Production graph.
|
|
124
|
+
|
|
125
|
+
Common mistakes:
|
|
126
|
+
Do not use synchronous calls for long-running work unless the caller
|
|
127
|
+
really must block.
|
|
128
|
+
|
|
129
|
+
Minimal example:
|
|
130
|
+
response = self.send_request_sync(self.Output, request)
|
|
131
|
+
|
|
132
|
+
Related:
|
|
133
|
+
docs/cookbooks/add-business-process.md,
|
|
134
|
+
docs/cookbooks/production-settings-and-targets.md
|
|
135
|
+
"""
|
|
136
|
+
target = resolve_target(target)
|
|
137
|
+
return self.iris_handle.dispatchSendRequestSync(
|
|
138
|
+
target, request, timeout, description
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
@input_serializer_param(1, "request")
|
|
142
|
+
def send_request_async(
|
|
143
|
+
self,
|
|
144
|
+
target: str | TargetSettingRef,
|
|
145
|
+
request: Message | Any,
|
|
146
|
+
description: str | None = None,
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Purpose:
|
|
149
|
+
Send a message to a target component without waiting for a response.
|
|
150
|
+
|
|
151
|
+
Use when:
|
|
152
|
+
A service or operation should enqueue downstream work and continue.
|
|
153
|
+
|
|
154
|
+
Lifecycle:
|
|
155
|
+
IoP serializes request and calls the IRIS asynchronous dispatch API.
|
|
156
|
+
|
|
157
|
+
Best practices:
|
|
158
|
+
Pass a target() attribute such as self.Output so the route is
|
|
159
|
+
configurable in the Production graph.
|
|
160
|
+
|
|
161
|
+
Common mistakes:
|
|
162
|
+
Do not use this helper when the caller requires a response; use
|
|
163
|
+
send_request_sync(...) or the BusinessProcess async response flow.
|
|
164
|
+
|
|
165
|
+
Minimal example:
|
|
166
|
+
self.send_request_async(self.Output, request)
|
|
167
|
+
|
|
168
|
+
Related:
|
|
169
|
+
docs/cookbooks/add-polling-service.md,
|
|
170
|
+
docs/cookbooks/production-settings-and-targets.md
|
|
171
|
+
"""
|
|
172
|
+
target = resolve_target(target)
|
|
173
|
+
return self.iris_handle.dispatchSendRequestAsync(target, request, description)
|
|
174
|
+
|
|
175
|
+
async def send_request_async_ng(
|
|
176
|
+
self,
|
|
177
|
+
target: str | TargetSettingRef,
|
|
178
|
+
request: Message | Any,
|
|
179
|
+
timeout: int = -1,
|
|
180
|
+
description: str | None = None,
|
|
181
|
+
) -> Any:
|
|
182
|
+
"""Send message asynchronously to target component with asyncio.
|
|
183
|
+
|
|
184
|
+
Prefer a target() attribute such as self.Output for configurable
|
|
185
|
+
routing. Use this helper when the component method is already async.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
target: Name of target component
|
|
189
|
+
request: Message to send
|
|
190
|
+
timeout: Timeout in seconds, -1 means wait forever
|
|
191
|
+
description: Optional description for logging
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Response from target component
|
|
195
|
+
"""
|
|
196
|
+
target = cast(str, resolve_target(target))
|
|
197
|
+
return await AsyncRequest(target, request, timeout, description, self)
|
|
198
|
+
|
|
199
|
+
def send_generator_request(
|
|
200
|
+
self,
|
|
201
|
+
target: str | TargetSettingRef,
|
|
202
|
+
request: Message | Any,
|
|
203
|
+
timeout: int = -1,
|
|
204
|
+
description: str | None = None,
|
|
205
|
+
) -> _GeneratorRequest:
|
|
206
|
+
"""Send message as a generator request to target component.
|
|
207
|
+
|
|
208
|
+
Prefer a target() attribute such as self.Output for configurable
|
|
209
|
+
routing.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
target: Name of target component
|
|
213
|
+
request: Message to send
|
|
214
|
+
timeout: Timeout in seconds, -1 means wait forever
|
|
215
|
+
description: Optional description for logging
|
|
216
|
+
Returns:
|
|
217
|
+
_GeneratorRequest: An instance of _GeneratorRequest to iterate over responses
|
|
218
|
+
Raises:
|
|
219
|
+
TypeError: If request is not of type Message
|
|
220
|
+
"""
|
|
221
|
+
target = cast(str, resolve_target(target))
|
|
222
|
+
return _GeneratorRequest(self, target, request, timeout, description)
|
|
223
|
+
|
|
224
|
+
def send_multi_request_sync(
|
|
225
|
+
self,
|
|
226
|
+
target_request: list[tuple[str | TargetSettingRef, Message | Any]],
|
|
227
|
+
timeout: int = -1,
|
|
228
|
+
description: str | None = None,
|
|
229
|
+
) -> list[tuple[str, Message | Any, Any, int]]:
|
|
230
|
+
"""Send multiple messages synchronously to target components.
|
|
231
|
+
|
|
232
|
+
Each target can be a target() attribute such as self.Accepted or a
|
|
233
|
+
component name. Prefer target() attributes for configurable production
|
|
234
|
+
graphs.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
target_request: List of tuples (target, request) to send
|
|
238
|
+
timeout: Timeout in seconds, -1 means wait forever
|
|
239
|
+
description: Optional description for logging
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
List of tuples (target, request, response, status)
|
|
243
|
+
|
|
244
|
+
Raises:
|
|
245
|
+
TypeError: If target_request is not a list of tuples
|
|
246
|
+
ValueError: If target_request is empty
|
|
247
|
+
"""
|
|
248
|
+
self._validate_target_request(target_request)
|
|
249
|
+
resolved_target_request: list[tuple[str, Message | Any]] = [
|
|
250
|
+
(resolve_target(target), request) for target, request in target_request
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
call_list = [
|
|
254
|
+
self._create_call_structure(target, request)
|
|
255
|
+
for target, request in resolved_target_request
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
response_list = self.iris_handle.dispatchSendRequestSyncMultiple(
|
|
259
|
+
call_list, timeout
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return [
|
|
263
|
+
(
|
|
264
|
+
resolved_target_request[i][0],
|
|
265
|
+
resolved_target_request[i][1],
|
|
266
|
+
dispatch_deserializer(response_list[i].Response),
|
|
267
|
+
int(response_list[i].ResponseCode),
|
|
268
|
+
)
|
|
269
|
+
for i in range(len(resolved_target_request))
|
|
270
|
+
]
|
|
271
|
+
|
|
272
|
+
def _validate_target_request(
|
|
273
|
+
self, target_request: list[tuple[str | TargetSettingRef, Message | Any]]
|
|
274
|
+
) -> None:
|
|
275
|
+
"""Validate the target_request parameter structure."""
|
|
276
|
+
if not isinstance(target_request, list):
|
|
277
|
+
raise TypeError("target_request must be a list")
|
|
278
|
+
if not target_request:
|
|
279
|
+
raise ValueError("target_request must not be empty")
|
|
280
|
+
if not all(
|
|
281
|
+
isinstance(item, tuple) and len(item) == 2 for item in target_request
|
|
282
|
+
):
|
|
283
|
+
raise TypeError("target_request must contain tuples of (target, request)")
|
|
284
|
+
|
|
285
|
+
def _create_call_structure(
|
|
286
|
+
self, target: str | TargetSettingRef, request: Message | Any
|
|
287
|
+
) -> Any:
|
|
288
|
+
"""Create an Ens.CallStructure object for the request."""
|
|
289
|
+
iris = _iris.get_iris()
|
|
290
|
+
call = iris.cls("Ens.CallStructure")._New()
|
|
291
|
+
call.TargetDispatchName = resolve_target(target)
|
|
292
|
+
call.Request = dispatch_serializer(request)
|
|
293
|
+
return call
|
|
294
|
+
|
|
295
|
+
@staticmethod
|
|
296
|
+
def OnGetConnections() -> list[str] | None:
|
|
297
|
+
"""Return all configured targets for this class.
|
|
298
|
+
|
|
299
|
+
Implement this method to allow connections between components to show up
|
|
300
|
+
in the interoperability UI.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
An IRISList containing all targets for this class. Default is None.
|
|
304
|
+
"""
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
@staticmethod
|
|
308
|
+
def get_adapter_type() -> str | None:
|
|
309
|
+
"""Returns the name of the registered Adapter.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
Name of the registered Adapter
|
|
313
|
+
"""
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
def on_get_connections(self) -> list[str]:
|
|
317
|
+
"""Return targets found in send_request_sync and send_request_async calls.
|
|
318
|
+
|
|
319
|
+
Implement this method to allow connections between components to show up
|
|
320
|
+
in the interoperability UI.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
A list containing all targets for this class.
|
|
324
|
+
"""
|
|
325
|
+
source = textwrap.dedent(getsource(self.__class__))
|
|
326
|
+
tree = ast.parse(source)
|
|
327
|
+
target_list: list[str] = []
|
|
328
|
+
|
|
329
|
+
for node in ast.walk(tree):
|
|
330
|
+
if not isinstance(node, ast.Call):
|
|
331
|
+
continue
|
|
332
|
+
if _call_name(node.func) not in _CONNECTION_METHODS:
|
|
333
|
+
continue
|
|
334
|
+
|
|
335
|
+
target_node = _call_target_node(node)
|
|
336
|
+
if target_node is None:
|
|
337
|
+
continue
|
|
338
|
+
|
|
339
|
+
target = _resolve_connection_target(self, target_node)
|
|
340
|
+
if isinstance(target, str) and target not in target_list:
|
|
341
|
+
target_list.append(target)
|
|
342
|
+
|
|
343
|
+
return target_list
|
|
344
|
+
|
|
345
|
+
@input_deserializer
|
|
346
|
+
def _dispatch_generator_started(self, request: Any) -> Any:
|
|
347
|
+
"""For internal use only."""
|
|
348
|
+
self._gen = dispatch_message(self, request)
|
|
349
|
+
# check if self._gen is a generator
|
|
350
|
+
if not hasattr(self._gen, "__iter__"):
|
|
351
|
+
raise TypeError(
|
|
352
|
+
f"Expected a generator or iterable object, got: {type(self._gen).__name__}"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
return _iris.get_iris().IOP.Generator.Message.Ack._New()
|
|
356
|
+
|
|
357
|
+
@output_serializer
|
|
358
|
+
def _dispatch_generator_poll(self) -> Any:
|
|
359
|
+
"""For internal use only."""
|
|
360
|
+
try:
|
|
361
|
+
return next(self._gen)
|
|
362
|
+
except StopIteration:
|
|
363
|
+
return _iris.get_iris().IOP.Generator.Message.Stop._New()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..messages.decorators import input_deserializer, output_serializer
|
|
6
|
+
from ..messages.dispatch import create_dispatch, dispatch_message
|
|
7
|
+
from .business_host import _BusinessHost
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _BusinessOperation(_BusinessHost):
|
|
11
|
+
"""Business operation component that handles outbound communication.
|
|
12
|
+
|
|
13
|
+
Responsible for sending messages to external systems. Can optionally use an
|
|
14
|
+
adapter to handle the outbound messaging protocol.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
DISPATCH: list[tuple[str, str]] = []
|
|
18
|
+
Adapter: Any = None
|
|
19
|
+
adapter: Any = None
|
|
20
|
+
|
|
21
|
+
def on_message(self, request: Any) -> Any:
|
|
22
|
+
"""Purpose:
|
|
23
|
+
Handle an incoming message sent to a BusinessOperation.
|
|
24
|
+
|
|
25
|
+
Use when:
|
|
26
|
+
The operation must perform an outbound side effect or submit data to
|
|
27
|
+
an external system.
|
|
28
|
+
|
|
29
|
+
Lifecycle:
|
|
30
|
+
IRIS invokes this hook for operation requests unless dispatch routes
|
|
31
|
+
the message to a @handler or typed one-argument method first.
|
|
32
|
+
|
|
33
|
+
Best practices:
|
|
34
|
+
Keep side effects isolated here. Return a response message when the
|
|
35
|
+
caller uses send_request_sync(...).
|
|
36
|
+
|
|
37
|
+
Common mistakes:
|
|
38
|
+
Do not put routing decisions here when a BusinessProcess should
|
|
39
|
+
orchestrate them.
|
|
40
|
+
|
|
41
|
+
Minimal example:
|
|
42
|
+
def on_message(self, request):
|
|
43
|
+
return SubmitResult(ok=True)
|
|
44
|
+
|
|
45
|
+
Related:
|
|
46
|
+
docs/cookbooks/add-business-operation.md
|
|
47
|
+
"""
|
|
48
|
+
warnings.warn(
|
|
49
|
+
f"{self.__class__.__name__} did not override on_message(); "
|
|
50
|
+
"the incoming operation message was ignored. "
|
|
51
|
+
"This default no-op handler will raise NotImplementedError in v5.0.",
|
|
52
|
+
RuntimeWarning,
|
|
53
|
+
stacklevel=2,
|
|
54
|
+
)
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def on_keepalive(self) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Called when the server sends a keepalive message.
|
|
60
|
+
"""
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
def _set_iris_handles(self, handle_current: Any, handle_partner: Any) -> None:
|
|
64
|
+
"""For internal use only."""
|
|
65
|
+
self.iris_handle = handle_current
|
|
66
|
+
if type(handle_partner).__module__.find("iris") == 0:
|
|
67
|
+
if handle_partner._IsA("IOP.OutboundAdapter"):
|
|
68
|
+
module = importlib.import_module(handle_partner.GetModule())
|
|
69
|
+
handle_partner = getattr(module, handle_partner.GetClassname())()
|
|
70
|
+
self.Adapter = self.adapter = handle_partner
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
def _dispatch_on_init(self, host_object: Any) -> None:
|
|
74
|
+
"""For internal use only."""
|
|
75
|
+
self._log_custom_init_warning()
|
|
76
|
+
create_dispatch(self)
|
|
77
|
+
self.on_init()
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
@input_deserializer
|
|
81
|
+
@output_serializer
|
|
82
|
+
def _dispatch_on_message(self, request: Any) -> Any:
|
|
83
|
+
"""For internal use only."""
|
|
84
|
+
return dispatch_message(self, request)
|