iris-pex-embedded-python 4.0.0b14__py3-none-any.whl → 4.0.1b1__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 +137 -24
- iop/components/business_host.py +43 -26
- iop/components/business_operation.py +25 -10
- iop/components/business_process.py +109 -47
- iop/components/business_service.py +25 -4
- iop/components/common.py +30 -4
- iop/components/polling_business_service.py +23 -3
- iop/messages/decorators.py +4 -0
- iop/migration/utils.py +27 -1
- iop/production/model.py +29 -9
- iop/production/types.py +26 -4
- {iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/METADATA +1 -1
- {iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/RECORD +17 -17
- {iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/WHEEL +0 -0
- {iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/entry_points.txt +0 -0
- {iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/licenses/LICENSE +0 -0
- {iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/top_level.txt +0 -0
iop/__init__.py
CHANGED
|
@@ -117,46 +117,137 @@ class OutboundAdapter(_OutboundAdapter):
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
class BusinessService(_BusinessService):
|
|
120
|
-
"""
|
|
120
|
+
"""Purpose:
|
|
121
|
+
Inbound production entry point for messages entering an IoP production.
|
|
121
122
|
|
|
122
|
-
Use
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
126
149
|
"""
|
|
127
150
|
|
|
128
151
|
pass
|
|
129
152
|
|
|
130
153
|
|
|
131
154
|
class PollingBusinessService(_PollingBusinessServiceMixin, BusinessService):
|
|
132
|
-
"""
|
|
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())
|
|
133
180
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
on_init(). See docs/cookbooks/add-polling-service.md.
|
|
181
|
+
Related:
|
|
182
|
+
docs/cookbooks/add-polling-service.md
|
|
137
183
|
"""
|
|
138
184
|
|
|
139
185
|
pass
|
|
140
186
|
|
|
141
187
|
|
|
142
188
|
class BusinessOperation(_BusinessOperation):
|
|
143
|
-
"""
|
|
189
|
+
"""Purpose:
|
|
190
|
+
Outbound side-effect boundary for production messages.
|
|
144
191
|
|
|
145
|
-
Use
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
149
215
|
"""
|
|
150
216
|
|
|
151
217
|
pass
|
|
152
218
|
|
|
153
219
|
|
|
154
220
|
class BusinessProcess(_BusinessProcess):
|
|
155
|
-
"""
|
|
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.
|
|
156
227
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
160
251
|
"""
|
|
161
252
|
|
|
162
253
|
pass
|
|
@@ -175,12 +266,34 @@ class DuplexProcess(_PrivateSessionProcess):
|
|
|
175
266
|
|
|
176
267
|
|
|
177
268
|
class Message(_Message):
|
|
178
|
-
"""
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
|
184
297
|
"""
|
|
185
298
|
|
|
186
299
|
pass
|
iop/components/business_host.py
CHANGED
|
@@ -108,23 +108,30 @@ class _BusinessHost(_Common):
|
|
|
108
108
|
timeout: int = -1,
|
|
109
109
|
description: str | None = None,
|
|
110
110
|
) -> Any:
|
|
111
|
-
"""
|
|
111
|
+
"""Purpose:
|
|
112
|
+
Send a message to a target component and wait for the response.
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
docs/cookbooks/add-business-process.md.
|
|
114
|
+
Use when:
|
|
115
|
+
The caller needs the target response before continuing.
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
128
135
|
"""
|
|
129
136
|
target = resolve_target(target)
|
|
130
137
|
return self.iris_handle.dispatchSendRequestSync(
|
|
@@ -138,19 +145,29 @@ class _BusinessHost(_Common):
|
|
|
138
145
|
request: Message | Any,
|
|
139
146
|
description: str | None = None,
|
|
140
147
|
) -> None:
|
|
141
|
-
"""
|
|
148
|
+
"""Purpose:
|
|
149
|
+
Send a message to a target component without waiting for a response.
|
|
142
150
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
docs/cookbooks/add-polling-service.md.
|
|
151
|
+
Use when:
|
|
152
|
+
A service or operation should enqueue downstream work and continue.
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
154
171
|
"""
|
|
155
172
|
target = resolve_target(target)
|
|
156
173
|
return self.iris_handle.dispatchSendRequestAsync(target, request, description)
|
|
@@ -19,16 +19,31 @@ class _BusinessOperation(_BusinessHost):
|
|
|
19
19
|
adapter: Any = None
|
|
20
20
|
|
|
21
21
|
def on_message(self, request: Any) -> Any:
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
32
47
|
"""
|
|
33
48
|
warnings.warn(
|
|
34
49
|
f"{self.__class__.__name__} did not override on_message(); "
|
|
@@ -20,16 +20,35 @@ class _BusinessProcess(_BusinessHost):
|
|
|
20
20
|
DISPATCH: list[tuple] = []
|
|
21
21
|
PERSISTENT_PROPERTY_LIST: list[str] | None = None
|
|
22
22
|
|
|
23
|
-
def on_message(self, request: Any) -> Any:
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
def on_message(self, request: Any) -> Any:
|
|
24
|
+
"""Purpose:
|
|
25
|
+
Handle an incoming message sent to a BusinessProcess.
|
|
26
|
+
|
|
27
|
+
Use when:
|
|
28
|
+
The process owns routing, orchestration, transformation, or
|
|
29
|
+
decisions for a request.
|
|
30
|
+
|
|
31
|
+
Lifecycle:
|
|
32
|
+
IRIS invokes this hook for process requests unless dispatch routes
|
|
33
|
+
the message to a @handler or typed one-argument method first. The
|
|
34
|
+
default implementation delegates to on_request(request).
|
|
35
|
+
|
|
36
|
+
Best practices:
|
|
37
|
+
Declare outbound routes with target() and call
|
|
38
|
+
send_request_sync(...) or send_request_async(...).
|
|
39
|
+
|
|
40
|
+
Common mistakes:
|
|
41
|
+
Do not hide routing in raw strings when target() can make routes
|
|
42
|
+
configurable in the production graph.
|
|
43
|
+
|
|
44
|
+
Minimal example:
|
|
45
|
+
def on_message(self, request):
|
|
46
|
+
return self.send_request_sync(self.Output, request)
|
|
47
|
+
|
|
48
|
+
Related:
|
|
49
|
+
docs/cookbooks/add-business-process.md
|
|
50
|
+
"""
|
|
51
|
+
return self.on_request(request)
|
|
33
52
|
|
|
34
53
|
def on_request(self, request: Any) -> Any:
|
|
35
54
|
"""Process initial requests sent to this component.
|
|
@@ -42,38 +61,67 @@ class _BusinessProcess(_BusinessHost):
|
|
|
42
61
|
"""
|
|
43
62
|
return None
|
|
44
63
|
|
|
45
|
-
def on_response(
|
|
64
|
+
def on_response(
|
|
46
65
|
self,
|
|
47
66
|
request: Any,
|
|
48
67
|
response: Any,
|
|
49
68
|
call_request: Any,
|
|
50
69
|
call_response: Any,
|
|
51
70
|
completion_key: str,
|
|
52
|
-
) -> Any:
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
) -> Any:
|
|
72
|
+
"""Purpose:
|
|
73
|
+
Handle one async response received by a BusinessProcess.
|
|
74
|
+
|
|
75
|
+
Use when:
|
|
76
|
+
The process sends async requests and needs to merge, inspect, or
|
|
77
|
+
transform each returned response.
|
|
78
|
+
|
|
79
|
+
Lifecycle:
|
|
80
|
+
IRIS calls on_response(...) after an async target response arrives.
|
|
81
|
+
on_complete(...) can run after all expected responses complete.
|
|
82
|
+
|
|
83
|
+
Best practices:
|
|
84
|
+
Use completion_key to identify which async call returned. Return
|
|
85
|
+
the accumulated or transformed response state.
|
|
86
|
+
|
|
87
|
+
Common mistakes:
|
|
88
|
+
Do not assume responses arrive in request order.
|
|
89
|
+
|
|
90
|
+
Minimal example:
|
|
91
|
+
def on_response(self, request, response, call_request, call_response, completion_key):
|
|
92
|
+
return call_response
|
|
93
|
+
|
|
94
|
+
Related:
|
|
95
|
+
docs/cookbooks/add-business-process.md
|
|
96
|
+
"""
|
|
65
97
|
return response
|
|
66
98
|
|
|
67
|
-
def on_complete(self, request: Any, response: Any) -> Any:
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
def on_complete(self, request: Any, response: Any) -> Any:
|
|
100
|
+
"""Purpose:
|
|
101
|
+
Finish async request orchestration for a BusinessProcess.
|
|
102
|
+
|
|
103
|
+
Use when:
|
|
104
|
+
The process must return or finalize an aggregate response after
|
|
105
|
+
async sends, timers, or response handling.
|
|
106
|
+
|
|
107
|
+
Lifecycle:
|
|
108
|
+
IRIS calls on_complete(request, response) after expected async
|
|
109
|
+
responses have been handled or the completion path is reached.
|
|
110
|
+
|
|
111
|
+
Best practices:
|
|
112
|
+
Return the final response message expected by the original caller.
|
|
113
|
+
|
|
114
|
+
Common mistakes:
|
|
115
|
+
Do not put per-response logic here; use on_response(...) for each
|
|
116
|
+
individual async response.
|
|
117
|
+
|
|
118
|
+
Minimal example:
|
|
119
|
+
def on_complete(self, request, response):
|
|
120
|
+
return response
|
|
121
|
+
|
|
122
|
+
Related:
|
|
123
|
+
docs/cookbooks/add-business-process.md
|
|
124
|
+
"""
|
|
77
125
|
return response
|
|
78
126
|
|
|
79
127
|
@input_serializer_param(0, "response")
|
|
@@ -93,19 +141,33 @@ class _BusinessProcess(_BusinessHost):
|
|
|
93
141
|
description: str | None = None,
|
|
94
142
|
completion_key: str | None = None,
|
|
95
143
|
response_required: bool = True,
|
|
96
|
-
) -> None:
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
144
|
+
) -> None:
|
|
145
|
+
"""Purpose:
|
|
146
|
+
Send a message asynchronously from a BusinessProcess.
|
|
147
|
+
|
|
148
|
+
Use when:
|
|
149
|
+
The process should continue without blocking for a target response,
|
|
150
|
+
or when responses will be handled later by on_response(...).
|
|
151
|
+
|
|
152
|
+
Lifecycle:
|
|
153
|
+
IoP serializes request before dispatching to IRIS. IRIS can call
|
|
154
|
+
on_response(...) and on_complete(...) when response_required is true.
|
|
155
|
+
|
|
156
|
+
Best practices:
|
|
157
|
+
Pass a target() attribute such as self.Output. Use completion_key
|
|
158
|
+
for fan-out or multiple async calls.
|
|
159
|
+
|
|
160
|
+
Common mistakes:
|
|
161
|
+
Do not pass an unresolved component instance or a hard-coded route
|
|
162
|
+
when the route should be configurable.
|
|
163
|
+
|
|
164
|
+
Minimal example:
|
|
165
|
+
self.send_request_async(self.Output, request, completion_key="out")
|
|
166
|
+
|
|
167
|
+
Related:
|
|
168
|
+
docs/cookbooks/add-business-process.md,
|
|
169
|
+
docs/cookbooks/production-settings-and-targets.md
|
|
170
|
+
"""
|
|
109
171
|
# Convert boolean to int for Iris API
|
|
110
172
|
if response_required:
|
|
111
173
|
response_required = 1 # type: ignore
|
|
@@ -42,11 +42,32 @@ class _BusinessService(_BusinessHost):
|
|
|
42
42
|
return
|
|
43
43
|
|
|
44
44
|
def on_message(self, request=None):
|
|
45
|
-
"""
|
|
45
|
+
"""Purpose:
|
|
46
|
+
Handle a message received by a BusinessService.
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Use when:
|
|
49
|
+
A service receives data from an adapter, Director API call, or
|
|
50
|
+
custom entry point and should send work into the production graph.
|
|
51
|
+
|
|
52
|
+
Lifecycle:
|
|
53
|
+
The default on_process_input(message_input) implementation delegates
|
|
54
|
+
to on_message(message_input).
|
|
55
|
+
|
|
56
|
+
Best practices:
|
|
57
|
+
Validate or normalize inbound data, then send a Message to a target
|
|
58
|
+
declared with target().
|
|
59
|
+
|
|
60
|
+
Common mistakes:
|
|
61
|
+
Do not call downstream component methods directly. Use
|
|
62
|
+
send_request_async(...) or send_request_sync(...).
|
|
63
|
+
|
|
64
|
+
Minimal example:
|
|
65
|
+
def on_message(self, request):
|
|
66
|
+
self.send_request_async(self.Output, request)
|
|
67
|
+
|
|
68
|
+
Related:
|
|
69
|
+
docs/cookbooks/add-polling-service.md,
|
|
70
|
+
docs/cookbooks/hl7v2-native-input.md
|
|
50
71
|
"""
|
|
51
72
|
warnings.warn(
|
|
52
73
|
f"{self.__class__.__name__} did not override on_message() or "
|
iop/components/common.py
CHANGED
|
@@ -223,10 +223,36 @@ class _Common:
|
|
|
223
223
|
self._log_to_console = value
|
|
224
224
|
self.logger = LogManager.get_logger(self.__class__.__name__, value)
|
|
225
225
|
|
|
226
|
-
# Lifecycle methods
|
|
227
|
-
def on_init(self) -> None:
|
|
228
|
-
"""
|
|
229
|
-
|
|
226
|
+
# Lifecycle methods
|
|
227
|
+
def on_init(self) -> None:
|
|
228
|
+
"""Purpose:
|
|
229
|
+
Component startup hook.
|
|
230
|
+
|
|
231
|
+
Use when:
|
|
232
|
+
A component needs to initialize clients, caches, credentials,
|
|
233
|
+
counters, or other runtime state after IRIS creates the host.
|
|
234
|
+
|
|
235
|
+
Lifecycle:
|
|
236
|
+
IoP calls on_init() from the internal dispatch startup path. IRIS
|
|
237
|
+
may allocate the Python object without calling __init__().
|
|
238
|
+
|
|
239
|
+
Best practices:
|
|
240
|
+
Keep initialization idempotent and quick. Read production settings
|
|
241
|
+
from self inside this method.
|
|
242
|
+
|
|
243
|
+
Common mistakes:
|
|
244
|
+
Do not put component startup work in __init__(); it may not run in
|
|
245
|
+
the IRIS component lifecycle.
|
|
246
|
+
|
|
247
|
+
Minimal example:
|
|
248
|
+
def on_init(self):
|
|
249
|
+
self.client = make_client(self.Endpoint)
|
|
250
|
+
|
|
251
|
+
Related:
|
|
252
|
+
docs/ai-coding.md,
|
|
253
|
+
docs/cookbooks/index.md
|
|
254
|
+
"""
|
|
255
|
+
pass
|
|
230
256
|
|
|
231
257
|
def on_tear_down(self) -> None:
|
|
232
258
|
"""Clean up component before termination."""
|
|
@@ -9,10 +9,30 @@ class _PollingBusinessServiceMixin:
|
|
|
9
9
|
return "Ens.InboundAdapter"
|
|
10
10
|
|
|
11
11
|
def on_poll(self):
|
|
12
|
-
"""
|
|
12
|
+
"""Purpose:
|
|
13
|
+
Run one scheduled polling cycle.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
Use when:
|
|
16
|
+
A PollingBusinessService fetches or discovers new work from Python.
|
|
17
|
+
|
|
18
|
+
Lifecycle:
|
|
19
|
+
The default IRIS inbound adapter calls on_process_input(), and this
|
|
20
|
+
mixin delegates that call to on_poll().
|
|
21
|
+
|
|
22
|
+
Best practices:
|
|
23
|
+
Do a bounded unit of work, then return. Send produced messages with
|
|
24
|
+
send_request_async(self.Output, message).
|
|
25
|
+
|
|
26
|
+
Common mistakes:
|
|
27
|
+
Do not create an infinite loop inside on_poll(); use the production
|
|
28
|
+
schedule or call interval for repeated execution.
|
|
29
|
+
|
|
30
|
+
Minimal example:
|
|
31
|
+
def on_poll(self):
|
|
32
|
+
self.send_request_async(self.Output, PollRequest())
|
|
33
|
+
|
|
34
|
+
Related:
|
|
35
|
+
docs/cookbooks/add-polling-service.md
|
|
16
36
|
"""
|
|
17
37
|
warnings.warn(
|
|
18
38
|
f"{self.__class__.__name__} did not override on_poll() or "
|
iop/messages/decorators.py
CHANGED
|
@@ -9,6 +9,7 @@ from .dispatch import handler as handler
|
|
|
9
9
|
def input_serializer(fonction: Callable) -> Callable:
|
|
10
10
|
"""Decorator that serializes all input arguments."""
|
|
11
11
|
|
|
12
|
+
@wraps(fonction)
|
|
12
13
|
def _dispatch_serializer(self, *params: Any, **param2: Any) -> Any:
|
|
13
14
|
serialized = [dispatch_serializer(param) for param in params]
|
|
14
15
|
param2 = {key: dispatch_serializer(value) for key, value in param2.items()}
|
|
@@ -41,6 +42,7 @@ def input_serializer_param(position: int, name: str) -> Callable:
|
|
|
41
42
|
def output_deserializer(fonction: Callable) -> Callable:
|
|
42
43
|
"""Decorator that deserializes function output."""
|
|
43
44
|
|
|
45
|
+
@wraps(fonction)
|
|
44
46
|
def _dispatch_deserializer(self, *params: Any, **param2: Any) -> Any:
|
|
45
47
|
return dispatch_deserializer(fonction(self, *params, **param2))
|
|
46
48
|
|
|
@@ -50,6 +52,7 @@ def output_deserializer(fonction: Callable) -> Callable:
|
|
|
50
52
|
def input_deserializer(fonction: Callable) -> Callable:
|
|
51
53
|
"""Decorator that deserializes all input arguments."""
|
|
52
54
|
|
|
55
|
+
@wraps(fonction)
|
|
53
56
|
def _dispatch_deserializer(self, *params: Any, **param2: Any) -> Any:
|
|
54
57
|
serialized = [dispatch_deserializer(param) for param in params]
|
|
55
58
|
param2 = {key: dispatch_deserializer(value) for key, value in param2.items()}
|
|
@@ -61,6 +64,7 @@ def input_deserializer(fonction: Callable) -> Callable:
|
|
|
61
64
|
def output_serializer(fonction: Callable) -> Callable:
|
|
62
65
|
"""Decorator that serializes function output."""
|
|
63
66
|
|
|
67
|
+
@wraps(fonction)
|
|
64
68
|
def _dispatch_serializer(self, *params: Any, **param2: Any) -> Any:
|
|
65
69
|
return dispatch_serializer(fonction(self, *params, **param2))
|
|
66
70
|
|
iop/migration/utils.py
CHANGED
|
@@ -546,7 +546,19 @@ def _build_migration_manifest(
|
|
|
546
546
|
|
|
547
547
|
|
|
548
548
|
def _load_settings(filename):
|
|
549
|
-
"""Load settings module
|
|
549
|
+
"""Load a migration settings module.
|
|
550
|
+
|
|
551
|
+
Purpose:
|
|
552
|
+
Resolve settings.py as a file-based module and keep imports local to the
|
|
553
|
+
settings file directory for this process.
|
|
554
|
+
|
|
555
|
+
Best practices:
|
|
556
|
+
Keep modules imported by settings.py in the same project/package rooted
|
|
557
|
+
near the settings file. Let IoP manage temporary import path setup.
|
|
558
|
+
|
|
559
|
+
Common mistakes:
|
|
560
|
+
Do not require users to set PYTHONPATH. Do not mutate environment
|
|
561
|
+
variables to force imports when module/package layout should be fixed.
|
|
550
562
|
|
|
551
563
|
Returns:
|
|
552
564
|
tuple: (settings_module, path_added_to_sys)
|
|
@@ -723,6 +735,20 @@ def _cleanup_sys_path(path):
|
|
|
723
735
|
|
|
724
736
|
|
|
725
737
|
def import_module_from_path(module_name, file_path):
|
|
738
|
+
"""Import one module from an absolute file path.
|
|
739
|
+
|
|
740
|
+
Purpose:
|
|
741
|
+
Execute a specific settings or component module by file location
|
|
742
|
+
without relying on global PYTHONPATH configuration.
|
|
743
|
+
|
|
744
|
+
Best practices:
|
|
745
|
+
Use absolute paths and stable module/package layout so imports resolve
|
|
746
|
+
from the project directory containing settings.py.
|
|
747
|
+
|
|
748
|
+
Common mistakes:
|
|
749
|
+
Do not patch PYTHONPATH or sys.path globally to make imports pass.
|
|
750
|
+
Keep import fixes in project structure and import statements.
|
|
751
|
+
"""
|
|
726
752
|
if not os.path.isabs(file_path):
|
|
727
753
|
raise ValueError("The file path must be absolute")
|
|
728
754
|
|
iop/production/model.py
CHANGED
|
@@ -42,15 +42,35 @@ _MISSING = object()
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class Production(_DeclarativeProductionMixin):
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
"""Purpose:
|
|
46
|
+
Python authoring DSL for IRIS interoperability production topology.
|
|
47
|
+
|
|
48
|
+
Use when:
|
|
49
|
+
A project should declare services, processes, operations, settings, and
|
|
50
|
+
graph connections in Python.
|
|
51
|
+
|
|
52
|
+
Lifecycle:
|
|
53
|
+
A settings.py module exports PRODUCTIONS = [prod]. Migration renders the
|
|
54
|
+
graph to IRIS; IRIS remains the runtime source of truth.
|
|
55
|
+
|
|
56
|
+
Best practices:
|
|
57
|
+
Declare topology with service(), process(), operation(), target(), and
|
|
58
|
+
connect(). Keep the Production object import-safe.
|
|
59
|
+
|
|
60
|
+
Common mistakes:
|
|
61
|
+
Do not execute migration or runtime side effects at module import time.
|
|
62
|
+
Do not use raw CLASSES entries for graph components.
|
|
63
|
+
|
|
64
|
+
Minimal example:
|
|
65
|
+
prod = Production("Demo.Production")
|
|
66
|
+
svc = prod.service("Demo.Service", MyService)
|
|
67
|
+
op = prod.operation("Demo.Operation", MyOperation)
|
|
68
|
+
prod.connect(svc.Output, op)
|
|
69
|
+
PRODUCTIONS = [prod]
|
|
70
|
+
|
|
71
|
+
Related:
|
|
72
|
+
docs/cookbooks/hello-world-production.md,
|
|
73
|
+
docs/production-graph.md
|
|
54
74
|
"""
|
|
55
75
|
|
|
56
76
|
def __init__(
|
iop/production/types.py
CHANGED
|
@@ -35,11 +35,33 @@ class TargetSetting(Setting):
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def target(**kwargs: Any) -> TargetSetting:
|
|
38
|
-
"""
|
|
38
|
+
"""Purpose:
|
|
39
|
+
Declare a configurable outbound target setting on a component class.
|
|
39
40
|
|
|
40
|
-
Use
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
Use when:
|
|
42
|
+
A service or process sends messages to another production component.
|
|
43
|
+
|
|
44
|
+
Lifecycle:
|
|
45
|
+
The descriptor becomes an IRIS production setting. Production.connect(...)
|
|
46
|
+
binds it to a concrete target component.
|
|
47
|
+
|
|
48
|
+
Best practices:
|
|
49
|
+
Use target() instead of hard-coded component names for routes that
|
|
50
|
+
should be visible and configurable in the production graph.
|
|
51
|
+
|
|
52
|
+
Common mistakes:
|
|
53
|
+
Do not use setting(...) for outbound routing when target() should expose
|
|
54
|
+
a production target.
|
|
55
|
+
|
|
56
|
+
Minimal example:
|
|
57
|
+
class Router(BusinessProcess):
|
|
58
|
+
Output = target()
|
|
59
|
+
|
|
60
|
+
prod.connect(router.Output, operation)
|
|
61
|
+
|
|
62
|
+
Related:
|
|
63
|
+
docs/cookbooks/production-settings-and-targets.md,
|
|
64
|
+
docs/production-graph.md
|
|
43
65
|
"""
|
|
44
66
|
return TargetSetting(**kwargs)
|
|
45
67
|
|
{iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/RECORD
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
iop/__init__.py,sha256=
|
|
1
|
+
iop/__init__.py,sha256=4QGYNWs4t-Zt4-WfjHXmKRosWS5jZ5A27vl2hiObCVc,10621
|
|
2
2
|
iop/__main__.py,sha256=2rUNM7vaYK0W57Jj72rm7GORyIFLMXKwPq3QNLwYBmY,100
|
|
3
3
|
iop/cli/__init__.py,sha256=Y78pBdA-ULzq1c_9xQZsFBlzm3x5xgxSzkvrH5DoM7I,31
|
|
4
4
|
iop/cli/formatting.py,sha256=OLgHA1d8_paAuINQyKkYgihWMLoBhPn7kSXW5whRlIs,1606
|
|
@@ -36,23 +36,23 @@ iop/cls/IOP/Service/Remote/Handler.cls,sha256=JfsXse2jvoVvQfW8_rVEt2DCQJ9SVqReCc
|
|
|
36
36
|
iop/cls/IOP/Service/Remote/Rest/v1.cls,sha256=33FEBVZhHqysYaLL7l0314kJu1eZXyJlvAk2l4Q-B5g,20277
|
|
37
37
|
iop/components/__init__.py,sha256=uRF_YCn_ukkgzFrW0nLLMmC2jJ1dMk-ME7HX9EEEVic,880
|
|
38
38
|
iop/components/async_request.py,sha256=AU36pf6NIQMM9ZYR5h6LeQry0EfXXqDaKAjsP16_hWE,2411
|
|
39
|
-
iop/components/business_host.py,sha256=
|
|
40
|
-
iop/components/business_operation.py,sha256=
|
|
41
|
-
iop/components/business_process.py,sha256=
|
|
42
|
-
iop/components/business_service.py,sha256=
|
|
43
|
-
iop/components/common.py,sha256=
|
|
39
|
+
iop/components/business_host.py,sha256=5EXwzZzfEVYLFlzi519GXKclTz2m5FMcqtl10lLzUyw,12089
|
|
40
|
+
iop/components/business_operation.py,sha256=KycRNz-zuyJs2VneTBVcppyd3rKLekrISjbubCbzHxY,2905
|
|
41
|
+
iop/components/business_process.py,sha256=1xTYwqhvJaXC-BvQ5WoGOud5QwxT9EPpvDPDX4zThXg,9839
|
|
42
|
+
iop/components/business_service.py,sha256=fwSdHGFuC55_qSjFBpW-cLZmrK3Cm8Yxgk1KvxuQv2Y,4718
|
|
43
|
+
iop/components/common.py,sha256=tsP2APRBnks1hVazS9dX0Q8KCoANbUsnaFTlzqOIkVY,19063
|
|
44
44
|
iop/components/debugpy.py,sha256=sNFKaF9MtWBNAqxH4ubSIVqjsIpsHGjRK2jzBU-O59Y,6011
|
|
45
45
|
iop/components/generator_request.py,sha256=NOllwJnY06kk_eC7cQXDQ8NBFKfQakyz3A_AgYJ5f5Y,1351
|
|
46
46
|
iop/components/inbound_adapter.py,sha256=H36HcHHn7fbzUuAMG-f2CwCesOeKnE6FZPuVGCUCEXo,1130
|
|
47
47
|
iop/components/log_manager.py,sha256=nIBMp4sz2Bi5tQml-LKj8cheiGk1LjNh5BiwqUupuZw,3241
|
|
48
48
|
iop/components/outbound_adapter.py,sha256=Bm6KeaLR0fO3z5o9cmJxcMdIiOvdExik-Oc272WZtHg,731
|
|
49
|
-
iop/components/polling_business_service.py,sha256=
|
|
49
|
+
iop/components/polling_business_service.py,sha256=1EkEW8KVx6wW7DCXnrExe2z07NcmuqJN0C6P5hxVNRM,1562
|
|
50
50
|
iop/components/private_session_duplex.py,sha256=IZ50ThI074qwFF1wFyhFCsSJK0huyuoIeVxwlGyweeQ,5157
|
|
51
51
|
iop/components/private_session_process.py,sha256=edugk8iLTjaHMsz_fBuj9VE8aL66IgOz-mMlqPyk3Zc,1731
|
|
52
52
|
iop/components/settings.py,sha256=USAKAisdersKlAEIk8HDu_TuYtkO-Sc3an3PXjPcDRA,6296
|
|
53
53
|
iop/messages/__init__.py,sha256=k1AKplpjdqSxM5Gl7bpWAfw93xUgigovlI4SKhwKHRI,355
|
|
54
54
|
iop/messages/base.py,sha256=ZrVXEz7JrYHnt5b3M7avxcJCB0_OZxm1uWm9gQ4_eAw,1906
|
|
55
|
-
iop/messages/decorators.py,sha256=
|
|
55
|
+
iop/messages/decorators.py,sha256=CWk08k8U2aTJv7Dx1n2jp8sjZZSCD8NJPoifKLB3W2g,2488
|
|
56
56
|
iop/messages/dispatch.py,sha256=Mgz0OHTQDU02Z8qPxl_lks-nlAvW6ADcJkTS4faYE-Q,10459
|
|
57
57
|
iop/messages/persistent.py,sha256=ibRhRTqzwGcAqem744KZzHiouUlc8-J3ftB4zyzQYx4,16906
|
|
58
58
|
iop/messages/serialization.py,sha256=BW0o-7GaceMW7RMOzOJ8g9rKEv28NYnYFwtTLmAf7Yg,9255
|
|
@@ -61,7 +61,7 @@ iop/migration/__init__.py,sha256=wp-RvjLKYW_Pi2AJLfiWe1bACxENzEgjXtP9UMNiSVY,36
|
|
|
61
61
|
iop/migration/io.py,sha256=hAehtApFZFgGzjzgwBmDMxdA1xoRhE-okOcEzvgFdQ4,1822
|
|
62
62
|
iop/migration/manifest.py,sha256=Bs4eC1_i-ZnovlgSW9FKu5WBwy_6Zwy5ZAcsi5A1szU,19351
|
|
63
63
|
iop/migration/plans.py,sha256=5kx3aqQiPDJ3F8hU9pp9_mOO1_N6GA0Ps7--G1Iv6z8,2549
|
|
64
|
-
iop/migration/utils.py,sha256=
|
|
64
|
+
iop/migration/utils.py,sha256=qJrp-Q3WjYxtzwTu7TqQppjUvQSYCUN-RJ5KPAu4M9M,37880
|
|
65
65
|
iop/production/__init__.py,sha256=95VmJgC3lWtHpUJijLqyKKxFZa65OcRni8803kb4aHE,2148
|
|
66
66
|
iop/production/actions.py,sha256=TLVAjPTPq21oLj9c-IG10qMNJ2hvPzueviMy_c1a0ZU,17199
|
|
67
67
|
iop/production/common.py,sha256=IoUg9jkFzSQwDsHcXvkVAC4qxpWKs3kTfXFm_EP07EU,3091
|
|
@@ -71,13 +71,13 @@ iop/production/declarative.py,sha256=IpHYO46mu7k9wnq3DpFreoALPoX1Vi8DrJb-85O_-bQ
|
|
|
71
71
|
iop/production/diff.py,sha256=NYpcSVklaGpaZOyl933W8eLsDWcwjlf6Upbp4pVGSMU,9975
|
|
72
72
|
iop/production/import_.py,sha256=twBra6j2peolFadv8WyDwZZOd9NznzAkPsk2RbOfZQM,10833
|
|
73
73
|
iop/production/inspection.py,sha256=PPv9DiDS4OrnKROqV7JPbuY8Y0JR-m8syPhap3GgE4g,3363
|
|
74
|
-
iop/production/model.py,sha256=
|
|
74
|
+
iop/production/model.py,sha256=CpI5OBEBBw9z40Lws1rKVL9Mx_P4fhKmwYPoqEUntKU,41705
|
|
75
75
|
iop/production/planning.py,sha256=nCYrXLKlHBlUaQZPzPfqtIERIv62F2sg4gJ0b8mtcKk,23375
|
|
76
76
|
iop/production/reconstruction.py,sha256=5vVteTEM6IOiQUfpe4AUQg6Nt4oaaIDR61-1l6Vu8hk,12532
|
|
77
77
|
iop/production/rendering.py,sha256=UVHLICY4zWEP3W7-99TpB2WeLBNREC2N8rYSnBa_iZM,20240
|
|
78
78
|
iop/production/runtime.py,sha256=6grX1u20OhHnvhzC3yPDTFEoDs_TN4mfs5MxzFzZMls,2637
|
|
79
79
|
iop/production/source_inference.py,sha256=Wl3E9-1hWb4qxD1jwrrZTLSgqN-0vGhOrJtmzDWPCao,21613
|
|
80
|
-
iop/production/types.py,sha256=
|
|
80
|
+
iop/production/types.py,sha256=L_wyXO8deOnqRFin74SX_K8UWILFu-VzIO5pKNa6VXY,16580
|
|
81
81
|
iop/production/validation.py,sha256=14BfjrLXW1AOuP8AdBrs-CY6zynaDc8_AUD9frsYAIU,13753
|
|
82
82
|
iop/runtime/__init__.py,sha256=wcyWMRuPCYE_CLd1NmgmI1M-IbFs2Gz2yIj2-EW_2AA,70
|
|
83
83
|
iop/runtime/director.py,sha256=KW8BvkATK8XuIUPNBD9rMww9r3Zg4K3H54868eOgvl0,12867
|
|
@@ -91,9 +91,9 @@ iop/runtime/remote/director.py,sha256=bkie751GrYLgGs7ApRCowHpSnJA8KupxCOrFDQmjyO
|
|
|
91
91
|
iop/runtime/remote/migration.py,sha256=Iboz-Ye7i9xif9bn433GazCF-gtD-HJW3wiUwWS5P6w,1831
|
|
92
92
|
iop/runtime/remote/settings.py,sha256=P1Hsgld5o57HpSZgPVZzobyjlUmxJj6bstiWvR0JXnc,2144
|
|
93
93
|
iop/runtime/remote/setup.py,sha256=Tr80F3JJTy7oSUso2jyx0iA_KZayd4MYt9vL0BvPpMA,2600
|
|
94
|
-
iris_pex_embedded_python-4.0.
|
|
95
|
-
iris_pex_embedded_python-4.0.
|
|
96
|
-
iris_pex_embedded_python-4.0.
|
|
97
|
-
iris_pex_embedded_python-4.0.
|
|
98
|
-
iris_pex_embedded_python-4.0.
|
|
99
|
-
iris_pex_embedded_python-4.0.
|
|
94
|
+
iris_pex_embedded_python-4.0.1b1.dist-info/licenses/LICENSE,sha256=rZSiBFId_sfbJ6RL0GjjPX-InNLkNS9ou7eQsikciI8,1089
|
|
95
|
+
iris_pex_embedded_python-4.0.1b1.dist-info/METADATA,sha256=wiQ5-SOMCjBKmcfgxkbkpCVsMM4bW9SgdRCaN--_PGM,5283
|
|
96
|
+
iris_pex_embedded_python-4.0.1b1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
97
|
+
iris_pex_embedded_python-4.0.1b1.dist-info/entry_points.txt,sha256=fzS8ZFsPe8cnpEx208X60DNugCPQmuSloPeg4pyyJDY,42
|
|
98
|
+
iris_pex_embedded_python-4.0.1b1.dist-info/top_level.txt,sha256=BM54-OXQ6l2BDtmesWNXckh033s9gcjoO7bfjbwZbxU,4
|
|
99
|
+
iris_pex_embedded_python-4.0.1b1.dist-info/RECORD,,
|
{iris_pex_embedded_python-4.0.0b14.dist-info → iris_pex_embedded_python-4.0.1b1.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|