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/__init__.py
CHANGED
|
@@ -1,26 +1,107 @@
|
|
|
1
|
-
from iop.
|
|
2
|
-
from iop.
|
|
3
|
-
from iop.
|
|
4
|
-
from iop.
|
|
5
|
-
from iop.
|
|
6
|
-
from iop.
|
|
1
|
+
from iop.components.business_operation import _BusinessOperation
|
|
2
|
+
from iop.components.business_process import _BusinessProcess
|
|
3
|
+
from iop.components.business_service import _BusinessService
|
|
4
|
+
from iop.components.inbound_adapter import _InboundAdapter
|
|
5
|
+
from iop.components.outbound_adapter import _OutboundAdapter
|
|
6
|
+
from iop.components.polling_business_service import _PollingBusinessServiceMixin
|
|
7
|
+
from iop.components.private_session_duplex import _PrivateSessionDuplex
|
|
8
|
+
from iop.components.private_session_process import _PrivateSessionProcess
|
|
9
|
+
from iop.components.settings import Category as Category
|
|
10
|
+
from iop.components.settings import Setting as Setting
|
|
11
|
+
from iop.components.settings import controls as controls
|
|
12
|
+
from iop.components.settings import setting as setting
|
|
13
|
+
from iop.messages.base import (
|
|
7
14
|
_Message,
|
|
8
15
|
_PickleMessage,
|
|
9
16
|
_PydanticMessage,
|
|
10
17
|
_PydanticPickleMessage,
|
|
11
18
|
)
|
|
12
|
-
from iop.
|
|
13
|
-
from iop.
|
|
14
|
-
from iop.
|
|
15
|
-
from iop.
|
|
16
|
-
from iop.
|
|
17
|
-
from iop.
|
|
18
|
-
from iop.
|
|
19
|
-
from iop.
|
|
20
|
-
from iop.
|
|
21
|
-
from iop.
|
|
22
|
-
from iop.
|
|
23
|
-
from iop.
|
|
19
|
+
from iop.messages.decorators import handler as handler
|
|
20
|
+
from iop.messages.persistent import Field as Field
|
|
21
|
+
from iop.messages.persistent import Model as Model
|
|
22
|
+
from iop.messages.persistent import _PersistentMessage
|
|
23
|
+
from iop.migration.utils import _Utils
|
|
24
|
+
from iop.migration.utils import bind_component as bind_component
|
|
25
|
+
from iop.migration.utils import list_bindings as list_bindings
|
|
26
|
+
from iop.migration.utils import register_component as register_component
|
|
27
|
+
from iop.migration.utils import unbind_component as unbind_component
|
|
28
|
+
from iop.migration.utils import unregister_component as unregister_component
|
|
29
|
+
from iop.production import ComponentItem as ComponentItem
|
|
30
|
+
from iop.production import ComponentRef as ComponentRef
|
|
31
|
+
from iop.production import OperationItem as OperationItem
|
|
32
|
+
from iop.production import ProcessItem as ProcessItem
|
|
33
|
+
from iop.production import Production as Production
|
|
34
|
+
from iop.production import ProductionApplyResult as ProductionApplyResult
|
|
35
|
+
from iop.production import ProductionChangePlan as ProductionChangePlan
|
|
36
|
+
from iop.production import ProductionDiff as ProductionDiff
|
|
37
|
+
from iop.production import ProductionDiffEntry as ProductionDiffEntry
|
|
38
|
+
from iop.production import ProductionGraph as ProductionGraph
|
|
39
|
+
from iop.production import ProductionPlanOperation as ProductionPlanOperation
|
|
40
|
+
from iop.production import ProductionValidationError as ProductionValidationError
|
|
41
|
+
from iop.production import ProductionValidationIssue as ProductionValidationIssue
|
|
42
|
+
from iop.production import ProductionValidationReport as ProductionValidationReport
|
|
43
|
+
from iop.production import ProductionValidationWarning as ProductionValidationWarning
|
|
44
|
+
from iop.production import ProductionVerifyResult as ProductionVerifyResult
|
|
45
|
+
from iop.production import Route as Route
|
|
46
|
+
from iop.production import ServiceItem as ServiceItem
|
|
47
|
+
from iop.production import TargetSetting as TargetSetting
|
|
48
|
+
from iop.production import TargetSettingRef as TargetSettingRef
|
|
49
|
+
from iop.production import target as target
|
|
50
|
+
from iop.runtime.director import _Director
|
|
51
|
+
from iop.runtime.protocol import DirectorProtocol as DirectorProtocol
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"BusinessOperation",
|
|
55
|
+
"BusinessProcess",
|
|
56
|
+
"BusinessService",
|
|
57
|
+
"Category",
|
|
58
|
+
"ComponentItem",
|
|
59
|
+
"ComponentRef",
|
|
60
|
+
"Director",
|
|
61
|
+
"DirectorProtocol",
|
|
62
|
+
"DuplexOperation",
|
|
63
|
+
"DuplexProcess",
|
|
64
|
+
"DuplexService",
|
|
65
|
+
"Field",
|
|
66
|
+
"InboundAdapter",
|
|
67
|
+
"Message",
|
|
68
|
+
"Model",
|
|
69
|
+
"OperationItem",
|
|
70
|
+
"OutboundAdapter",
|
|
71
|
+
"PersistentMessage",
|
|
72
|
+
"PickleMessage",
|
|
73
|
+
"PollingBusinessService",
|
|
74
|
+
"ProcessItem",
|
|
75
|
+
"Production",
|
|
76
|
+
"ProductionApplyResult",
|
|
77
|
+
"ProductionChangePlan",
|
|
78
|
+
"ProductionDiff",
|
|
79
|
+
"ProductionDiffEntry",
|
|
80
|
+
"ProductionGraph",
|
|
81
|
+
"ProductionPlanOperation",
|
|
82
|
+
"ProductionVerifyResult",
|
|
83
|
+
"ProductionValidationError",
|
|
84
|
+
"ProductionValidationIssue",
|
|
85
|
+
"ProductionValidationReport",
|
|
86
|
+
"ProductionValidationWarning",
|
|
87
|
+
"PydanticMessage",
|
|
88
|
+
"PydanticPickleMessage",
|
|
89
|
+
"Route",
|
|
90
|
+
"ServiceItem",
|
|
91
|
+
"Setting",
|
|
92
|
+
"TargetSetting",
|
|
93
|
+
"TargetSettingRef",
|
|
94
|
+
"Utils",
|
|
95
|
+
"bind_component",
|
|
96
|
+
"controls",
|
|
97
|
+
"handler",
|
|
98
|
+
"list_bindings",
|
|
99
|
+
"register_component",
|
|
100
|
+
"setting",
|
|
101
|
+
"target",
|
|
102
|
+
"unbind_component",
|
|
103
|
+
"unregister_component",
|
|
104
|
+
]
|
|
24
105
|
|
|
25
106
|
|
|
26
107
|
class Utils(_Utils):
|
|
@@ -36,18 +117,139 @@ class OutboundAdapter(_OutboundAdapter):
|
|
|
36
117
|
|
|
37
118
|
|
|
38
119
|
class BusinessService(_BusinessService):
|
|
120
|
+
"""Purpose:
|
|
121
|
+
Inbound production entry point for messages entering an IoP production.
|
|
122
|
+
|
|
123
|
+
Use when:
|
|
124
|
+
External data, an adapter, or custom code must send a message into the
|
|
125
|
+
production graph.
|
|
126
|
+
|
|
127
|
+
Lifecycle:
|
|
128
|
+
IRIS calls on_process_input(); the default implementation delegates to
|
|
129
|
+
on_process_input(request).
|
|
130
|
+
|
|
131
|
+
Best practices:
|
|
132
|
+
Declare outbound routes with target() and wire them in a Production
|
|
133
|
+
graph. Use PollingBusinessService for scheduled Python polling.
|
|
134
|
+
|
|
135
|
+
Common mistakes:
|
|
136
|
+
Do not put startup work in __init__(); use on_init(). Do not instantiate
|
|
137
|
+
downstream components directly.
|
|
138
|
+
|
|
139
|
+
Minimal example:
|
|
140
|
+
class FileIn(BusinessService):
|
|
141
|
+
Output = target()
|
|
142
|
+
|
|
143
|
+
def on_process_input(self, request):
|
|
144
|
+
self.send_request_async(self.Output, request)
|
|
145
|
+
|
|
146
|
+
Related:
|
|
147
|
+
docs/cookbooks/add-polling-service.md,
|
|
148
|
+
docs/cookbooks/hl7v2-native-input.md
|
|
149
|
+
"""
|
|
150
|
+
|
|
39
151
|
pass
|
|
40
152
|
|
|
41
153
|
|
|
42
154
|
class PollingBusinessService(_PollingBusinessServiceMixin, BusinessService):
|
|
155
|
+
"""Purpose:
|
|
156
|
+
Scheduled Python service called by the default IRIS inbound adapter.
|
|
157
|
+
|
|
158
|
+
Use when:
|
|
159
|
+
A production must poll an API, directory, queue, database, or other
|
|
160
|
+
source from Python.
|
|
161
|
+
|
|
162
|
+
Lifecycle:
|
|
163
|
+
IRIS calls on_process_input(); the mixin delegates that call to
|
|
164
|
+
on_poll().
|
|
165
|
+
|
|
166
|
+
Best practices:
|
|
167
|
+
Put one polling cycle in on_poll(). Declare outbound routes with
|
|
168
|
+
target() and send messages with send_request_async(...).
|
|
169
|
+
|
|
170
|
+
Common mistakes:
|
|
171
|
+
Do not block forever inside on_poll(). Do not put startup work in
|
|
172
|
+
__init__(); use on_init().
|
|
173
|
+
|
|
174
|
+
Minimal example:
|
|
175
|
+
class ApiPoller(PollingBusinessService):
|
|
176
|
+
Output = target()
|
|
177
|
+
|
|
178
|
+
def on_poll(self):
|
|
179
|
+
self.send_request_async(self.Output, MyRequest())
|
|
180
|
+
|
|
181
|
+
Related:
|
|
182
|
+
docs/cookbooks/add-polling-service.md
|
|
183
|
+
"""
|
|
184
|
+
|
|
43
185
|
pass
|
|
44
186
|
|
|
45
187
|
|
|
46
188
|
class BusinessOperation(_BusinessOperation):
|
|
189
|
+
"""Purpose:
|
|
190
|
+
Outbound side-effect boundary for production messages.
|
|
191
|
+
|
|
192
|
+
Use when:
|
|
193
|
+
A production must call an external API, write a file, update a database,
|
|
194
|
+
submit FHIR resources, or perform another side effect.
|
|
195
|
+
|
|
196
|
+
Lifecycle:
|
|
197
|
+
IRIS calls on_message(request). IoP can dispatch to @handler methods,
|
|
198
|
+
typed one-argument methods, or the on_message fallback.
|
|
199
|
+
|
|
200
|
+
Best practices:
|
|
201
|
+
Keep external-system code here. Return a response message when callers
|
|
202
|
+
expect synchronous results.
|
|
203
|
+
|
|
204
|
+
Common mistakes:
|
|
205
|
+
Do not put routing orchestration in an operation when a BusinessProcess
|
|
206
|
+
should own the decision.
|
|
207
|
+
|
|
208
|
+
Minimal example:
|
|
209
|
+
class SubmitOrder(BusinessOperation):
|
|
210
|
+
def on_message(self, request):
|
|
211
|
+
return SubmitResult(ok=True)
|
|
212
|
+
|
|
213
|
+
Related:
|
|
214
|
+
docs/cookbooks/add-business-operation.md
|
|
215
|
+
"""
|
|
216
|
+
|
|
47
217
|
pass
|
|
48
218
|
|
|
49
219
|
|
|
50
220
|
class BusinessProcess(_BusinessProcess):
|
|
221
|
+
"""Purpose:
|
|
222
|
+
Routing, orchestration, decision, and transformation component.
|
|
223
|
+
|
|
224
|
+
Use when:
|
|
225
|
+
A production needs branching, enrichment, transformation, fan-out,
|
|
226
|
+
request/reply orchestration, or response aggregation.
|
|
227
|
+
|
|
228
|
+
Lifecycle:
|
|
229
|
+
IRIS calls on_message(request). For async requests, IRIS can later call
|
|
230
|
+
on_response(...) and on_complete(...).
|
|
231
|
+
|
|
232
|
+
Best practices:
|
|
233
|
+
Declare outbound routes with target() and wire them with
|
|
234
|
+
Production.connect(...). Use @handler(MessageType) or typed methods for
|
|
235
|
+
multiple message types.
|
|
236
|
+
|
|
237
|
+
Common mistakes:
|
|
238
|
+
Do not hard-code target component names when target() can expose a
|
|
239
|
+
configurable route.
|
|
240
|
+
|
|
241
|
+
Minimal example:
|
|
242
|
+
class Router(BusinessProcess):
|
|
243
|
+
Accepted = target()
|
|
244
|
+
|
|
245
|
+
def on_message(self, request):
|
|
246
|
+
return self.send_request_sync(self.Accepted, request)
|
|
247
|
+
|
|
248
|
+
Related:
|
|
249
|
+
docs/cookbooks/add-business-process.md,
|
|
250
|
+
docs/cookbooks/production-settings-and-targets.md
|
|
251
|
+
"""
|
|
252
|
+
|
|
51
253
|
pass
|
|
52
254
|
|
|
53
255
|
|
|
@@ -64,22 +266,78 @@ class DuplexProcess(_PrivateSessionProcess):
|
|
|
64
266
|
|
|
65
267
|
|
|
66
268
|
class Message(_Message):
|
|
269
|
+
"""Purpose:
|
|
270
|
+
Python-only JSON-serialized message contract.
|
|
271
|
+
|
|
272
|
+
Use when:
|
|
273
|
+
IoP components exchange structured Python data and IRIS does not need a
|
|
274
|
+
native persistent message body.
|
|
275
|
+
|
|
276
|
+
Lifecycle:
|
|
277
|
+
IoP serializes dataclass fields into IOP.Message and restores the Python
|
|
278
|
+
class on receipt.
|
|
279
|
+
|
|
280
|
+
Best practices:
|
|
281
|
+
Decorate subclasses with @dataclass. Use PydanticMessage when runtime
|
|
282
|
+
validation is more important.
|
|
283
|
+
|
|
284
|
+
Common mistakes:
|
|
285
|
+
Do not use Message without @dataclass. Do not register Message classes
|
|
286
|
+
in CLASSES; use PersistentMessage for native IRIS message bodies.
|
|
287
|
+
|
|
288
|
+
Minimal example:
|
|
289
|
+
@dataclass
|
|
290
|
+
class OrderRequest(Message):
|
|
291
|
+
order_id: str
|
|
292
|
+
|
|
293
|
+
Related:
|
|
294
|
+
docs/cookbooks/add-business-process.md,
|
|
295
|
+
docs/cookbooks/add-business-operation.md,
|
|
296
|
+
docs/getting-started/register-component.md
|
|
297
|
+
"""
|
|
298
|
+
|
|
67
299
|
pass
|
|
68
300
|
|
|
69
301
|
|
|
70
302
|
class PickleMessage(_PickleMessage):
|
|
303
|
+
"""Python-only pickle-serialized message contract.
|
|
304
|
+
|
|
305
|
+
Prefer Message or PydanticMessage for new app code unless JSON-compatible
|
|
306
|
+
fields cannot represent the payload.
|
|
307
|
+
"""
|
|
308
|
+
|
|
71
309
|
pass
|
|
72
310
|
|
|
73
311
|
|
|
74
312
|
class PydanticMessage(_PydanticMessage):
|
|
313
|
+
"""Python-only Pydantic message contract with validation.
|
|
314
|
+
|
|
315
|
+
Use for app messages that benefit from Pydantic validation. Do not decorate
|
|
316
|
+
PydanticMessage classes with @dataclass and do not put these classes in
|
|
317
|
+
CLASSES; see docs/getting-started/register-component.md.
|
|
318
|
+
"""
|
|
319
|
+
|
|
75
320
|
pass
|
|
76
321
|
|
|
77
322
|
|
|
78
323
|
class PydanticPickleMessage(_PydanticPickleMessage):
|
|
324
|
+
"""Pydantic message contract serialized through pickle.
|
|
325
|
+
|
|
326
|
+
Prefer PydanticMessage unless the payload must preserve Python-only object
|
|
327
|
+
shapes that JSON serialization cannot represent.
|
|
328
|
+
"""
|
|
329
|
+
|
|
79
330
|
pass
|
|
80
331
|
|
|
81
332
|
|
|
82
333
|
class PersistentMessage(_PersistentMessage):
|
|
334
|
+
"""Native persistent IRIS message body contract.
|
|
335
|
+
|
|
336
|
+
Use when IRIS needs a generated message class or persistent message body.
|
|
337
|
+
Prefer Message or PydanticMessage for Python-only routing. See
|
|
338
|
+
docs/getting-started/register-component.md and docs/settings.md.
|
|
339
|
+
"""
|
|
340
|
+
|
|
83
341
|
_iop_persistent_message_abstract = True
|
|
84
342
|
|
|
85
343
|
|
iop/__main__.py
CHANGED
iop/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main import main as main
|
iop/cli/formatting.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_test_response(response: Any) -> str:
|
|
9
|
+
"""Pretty-print any test_component() return value."""
|
|
10
|
+
if isinstance(response, dict):
|
|
11
|
+
parts = []
|
|
12
|
+
if response.get("error"):
|
|
13
|
+
return f"Error: {response['error']}"
|
|
14
|
+
if response.get("classname"):
|
|
15
|
+
parts.append(f"classname: {response['classname']}")
|
|
16
|
+
body = response.get("body", "")
|
|
17
|
+
if body:
|
|
18
|
+
try:
|
|
19
|
+
parsed = json.loads(body)
|
|
20
|
+
parts.append("body:\n" + json.dumps(parsed, indent=4))
|
|
21
|
+
except (json.JSONDecodeError, TypeError):
|
|
22
|
+
parts.append(f"body: {body}")
|
|
23
|
+
if response.get("truncated"):
|
|
24
|
+
parts.append("(response body was truncated)")
|
|
25
|
+
return "\n".join(parts) if parts else str(response)
|
|
26
|
+
|
|
27
|
+
if isinstance(response, str):
|
|
28
|
+
if " : " in response:
|
|
29
|
+
classname_part, _, body_part = response.partition(" : ")
|
|
30
|
+
try:
|
|
31
|
+
parsed = json.loads(body_part)
|
|
32
|
+
return (
|
|
33
|
+
f"classname: {classname_part.strip()}\n"
|
|
34
|
+
f"body:\n{json.dumps(parsed, indent=4)}"
|
|
35
|
+
)
|
|
36
|
+
except (json.JSONDecodeError, TypeError):
|
|
37
|
+
pass
|
|
38
|
+
try:
|
|
39
|
+
return json.dumps(json.loads(response), indent=4)
|
|
40
|
+
except (json.JSONDecodeError, TypeError):
|
|
41
|
+
return response
|
|
42
|
+
|
|
43
|
+
if dataclasses.is_dataclass(response):
|
|
44
|
+
return json.dumps(dataclasses.asdict(response), indent=4)
|
|
45
|
+
return str(response)
|