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
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .common import (
|
|
8
|
+
PRODUCTION_SETTING_FIELDS_BY_IRIS,
|
|
9
|
+
PRODUCTION_SETTING_NAMES,
|
|
10
|
+
SETTING_NAME_ALIASES,
|
|
11
|
+
)
|
|
12
|
+
from .import_ import _as_list, _production_payload, _split_settings
|
|
13
|
+
|
|
14
|
+
_PRODUCTION_PUBLIC_FIELDS = {
|
|
15
|
+
"name",
|
|
16
|
+
"testing_enabled",
|
|
17
|
+
"log_general_trace_events",
|
|
18
|
+
"actor_pool_size",
|
|
19
|
+
"description",
|
|
20
|
+
"shutdown_timeout",
|
|
21
|
+
"update_timeout",
|
|
22
|
+
"alert_notification_manager",
|
|
23
|
+
"alert_notification_operation",
|
|
24
|
+
"alert_notification_recipients",
|
|
25
|
+
"alert_action_window",
|
|
26
|
+
"namespace",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_PRODUCTION_DICT_KEYS = {
|
|
30
|
+
"@Name",
|
|
31
|
+
"@TestingEnabled",
|
|
32
|
+
"@LogGeneralTraceEvents",
|
|
33
|
+
"Description",
|
|
34
|
+
"ActorPoolSize",
|
|
35
|
+
"Setting",
|
|
36
|
+
"Item",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_PRODUCTION_SETTING_NAME_ALIASES = {
|
|
40
|
+
**PRODUCTION_SETTING_NAMES,
|
|
41
|
+
**{iris_name: iris_name for iris_name in PRODUCTION_SETTING_FIELDS_BY_IRIS},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class ProductionValidationIssue:
|
|
47
|
+
kind: str
|
|
48
|
+
path: str
|
|
49
|
+
message: str
|
|
50
|
+
suggestion: str = ""
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> dict[str, str]:
|
|
53
|
+
data = {
|
|
54
|
+
"kind": self.kind,
|
|
55
|
+
"path": self.path,
|
|
56
|
+
"message": self.message,
|
|
57
|
+
}
|
|
58
|
+
if self.suggestion:
|
|
59
|
+
data["suggestion"] = self.suggestion
|
|
60
|
+
return data
|
|
61
|
+
|
|
62
|
+
def to_text(self) -> str:
|
|
63
|
+
text = f"{self.kind} {self.path}: {self.message}"
|
|
64
|
+
if self.suggestion:
|
|
65
|
+
return f"{text} {self.suggestion}"
|
|
66
|
+
return text
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True)
|
|
70
|
+
class ProductionValidationReport:
|
|
71
|
+
production_name: str
|
|
72
|
+
issues: tuple[ProductionValidationIssue, ...] = ()
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def has_issues(self) -> bool:
|
|
76
|
+
return bool(self.issues)
|
|
77
|
+
|
|
78
|
+
def to_dict(self) -> dict[str, Any]:
|
|
79
|
+
return {
|
|
80
|
+
"production": self.production_name,
|
|
81
|
+
"has_issues": self.has_issues,
|
|
82
|
+
"issues": [issue.to_dict() for issue in self.issues],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def to_text(self) -> str:
|
|
86
|
+
lines = [f"Production validation: {self.production_name}"]
|
|
87
|
+
if not self.issues:
|
|
88
|
+
lines.append(" no issues")
|
|
89
|
+
else:
|
|
90
|
+
lines.extend(f" {issue.to_text()}" for issue in self.issues)
|
|
91
|
+
return "\n".join(lines)
|
|
92
|
+
|
|
93
|
+
def __str__(self) -> str:
|
|
94
|
+
return self.to_text()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ProductionValidationWarning(UserWarning):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ProductionValidationError(ValueError):
|
|
102
|
+
def __init__(self, report: ProductionValidationReport):
|
|
103
|
+
self.report = report
|
|
104
|
+
super().__init__(report.to_text())
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def validate_production(
|
|
108
|
+
production: Any,
|
|
109
|
+
*,
|
|
110
|
+
strict: bool = False,
|
|
111
|
+
warn: bool = True,
|
|
112
|
+
) -> ProductionValidationReport:
|
|
113
|
+
report = _build_production_object_report(production)
|
|
114
|
+
return _finalize_report(report, strict=strict, warn=warn)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def validate_production_entry(
|
|
118
|
+
production: Any,
|
|
119
|
+
*,
|
|
120
|
+
strict: bool = False,
|
|
121
|
+
warn: bool = True,
|
|
122
|
+
) -> ProductionValidationReport:
|
|
123
|
+
if _is_production_object(production):
|
|
124
|
+
report = _build_production_object_report(production)
|
|
125
|
+
else:
|
|
126
|
+
report = _build_production_dict_report(production)
|
|
127
|
+
return _finalize_report(report, strict=strict, warn=warn)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _finalize_report(
|
|
131
|
+
report: ProductionValidationReport,
|
|
132
|
+
*,
|
|
133
|
+
strict: bool,
|
|
134
|
+
warn: bool,
|
|
135
|
+
) -> ProductionValidationReport:
|
|
136
|
+
if not report.has_issues:
|
|
137
|
+
return report
|
|
138
|
+
if strict:
|
|
139
|
+
raise ProductionValidationError(report)
|
|
140
|
+
if warn:
|
|
141
|
+
for issue in report.issues:
|
|
142
|
+
warnings.warn(issue.to_text(), ProductionValidationWarning, stacklevel=3)
|
|
143
|
+
return report
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _build_production_object_report(production: Any) -> ProductionValidationReport:
|
|
147
|
+
production_name = str(getattr(production, "name", "Production"))
|
|
148
|
+
issues: list[ProductionValidationIssue] = []
|
|
149
|
+
issues.extend(_unknown_public_attrs(production))
|
|
150
|
+
|
|
151
|
+
for item in getattr(production, "items", ()):
|
|
152
|
+
issues.extend(_validate_component_ref(item))
|
|
153
|
+
|
|
154
|
+
return ProductionValidationReport(production_name, tuple(issues))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _unknown_public_attrs(production: Any) -> list[ProductionValidationIssue]:
|
|
158
|
+
issues = []
|
|
159
|
+
for attr in sorted(getattr(production, "__dict__", {})):
|
|
160
|
+
if attr.startswith("_") or attr in _PRODUCTION_PUBLIC_FIELDS:
|
|
161
|
+
continue
|
|
162
|
+
issues.append(
|
|
163
|
+
ProductionValidationIssue(
|
|
164
|
+
kind="production",
|
|
165
|
+
path=f"production.{attr}",
|
|
166
|
+
message=(
|
|
167
|
+
"Unknown public Production attribute. Only documented "
|
|
168
|
+
"Production fields are serialized."
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
return issues
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _validate_component_ref(item: Any) -> list[ProductionValidationIssue]:
|
|
176
|
+
issues: list[ProductionValidationIssue] = []
|
|
177
|
+
item_name = str(getattr(item, "name", ""))
|
|
178
|
+
component_class = getattr(item, "component_class", None)
|
|
179
|
+
class_name = str(getattr(item, "class_name", "") or "")
|
|
180
|
+
|
|
181
|
+
issues.extend(
|
|
182
|
+
_validate_settings(
|
|
183
|
+
settings=dict(getattr(item, "host_settings", {}) or {}),
|
|
184
|
+
path_prefix=f"items.{item_name}.settings.Host",
|
|
185
|
+
local_class=component_class,
|
|
186
|
+
iris_class_name=class_name,
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
adapter_settings = dict(getattr(item, "adapter_settings", {}) or {})
|
|
191
|
+
if str(getattr(item, "kind", "")) == "process" and adapter_settings:
|
|
192
|
+
issues.append(
|
|
193
|
+
ProductionValidationIssue(
|
|
194
|
+
kind="setting",
|
|
195
|
+
path=f"items.{item_name}.settings.Adapter",
|
|
196
|
+
message="Business process items do not support adapter settings.",
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
adapter_class = getattr(item, "adapter_class", None)
|
|
201
|
+
adapter_class_name = str(getattr(item, "adapter_class_name", "") or "")
|
|
202
|
+
issues.extend(
|
|
203
|
+
_validate_settings(
|
|
204
|
+
settings=adapter_settings,
|
|
205
|
+
path_prefix=f"items.{item_name}.settings.Adapter",
|
|
206
|
+
local_class=adapter_class,
|
|
207
|
+
iris_class_name=adapter_class_name,
|
|
208
|
+
host_class_name=class_name,
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
return issues
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _build_production_dict_report(data: Any) -> ProductionValidationReport:
|
|
215
|
+
issues: list[ProductionValidationIssue] = []
|
|
216
|
+
try:
|
|
217
|
+
production_name, production_data = _production_payload(data)
|
|
218
|
+
except Exception:
|
|
219
|
+
return ProductionValidationReport("Production", tuple(issues))
|
|
220
|
+
|
|
221
|
+
for key in sorted(set(production_data) - _PRODUCTION_DICT_KEYS):
|
|
222
|
+
issues.append(
|
|
223
|
+
ProductionValidationIssue(
|
|
224
|
+
kind="production",
|
|
225
|
+
path=f"production.{key}",
|
|
226
|
+
message="Unknown production dictionary key.",
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
issues.extend(_validate_production_settings(production_data.get("Setting")))
|
|
231
|
+
for item in _as_list(production_data.get("Item", [])):
|
|
232
|
+
if isinstance(item, dict):
|
|
233
|
+
issues.extend(_validate_dict_item(item))
|
|
234
|
+
|
|
235
|
+
return ProductionValidationReport(str(production_name), tuple(issues))
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _validate_production_settings(settings: Any) -> list[ProductionValidationIssue]:
|
|
239
|
+
issues: list[ProductionValidationIssue] = []
|
|
240
|
+
known_iris_names = set(PRODUCTION_SETTING_FIELDS_BY_IRIS)
|
|
241
|
+
for setting in _as_list(settings):
|
|
242
|
+
if not isinstance(setting, dict):
|
|
243
|
+
continue
|
|
244
|
+
target = setting.get("@Target", "")
|
|
245
|
+
if target not in ("", "Production"):
|
|
246
|
+
continue
|
|
247
|
+
setting_name = str(setting.get("@Name", ""))
|
|
248
|
+
if not setting_name or setting_name in known_iris_names:
|
|
249
|
+
continue
|
|
250
|
+
alias = _PRODUCTION_SETTING_NAME_ALIASES.get(setting_name)
|
|
251
|
+
if alias in known_iris_names:
|
|
252
|
+
issues.append(
|
|
253
|
+
ProductionValidationIssue(
|
|
254
|
+
kind="production",
|
|
255
|
+
path=f"production.settings.{setting_name}",
|
|
256
|
+
message=(
|
|
257
|
+
"Production setting name is accepted as a known alias for "
|
|
258
|
+
"validation, but IoP will emit the original name."
|
|
259
|
+
),
|
|
260
|
+
suggestion=f"Use {alias!r}.",
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
continue
|
|
264
|
+
issues.append(
|
|
265
|
+
ProductionValidationIssue(
|
|
266
|
+
kind="production",
|
|
267
|
+
path=f"production.settings.{setting_name}",
|
|
268
|
+
message="Unknown production-level setting.",
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
return issues
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _validate_dict_item(item: dict[str, Any]) -> list[ProductionValidationIssue]:
|
|
275
|
+
item_name = str(item.get("@Name", ""))
|
|
276
|
+
class_ref = item.get("@ClassName", "")
|
|
277
|
+
local_class = class_ref if isinstance(class_ref, type) else None
|
|
278
|
+
iris_class_name = "" if isinstance(class_ref, type) else str(class_ref or "")
|
|
279
|
+
host_settings, adapter_settings, _other_settings = _split_settings(
|
|
280
|
+
item.get("Setting", [])
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
issues = _validate_settings(
|
|
284
|
+
settings=host_settings,
|
|
285
|
+
path_prefix=f"items.{item_name}.settings.Host",
|
|
286
|
+
local_class=local_class,
|
|
287
|
+
iris_class_name=iris_class_name,
|
|
288
|
+
)
|
|
289
|
+
issues.extend(
|
|
290
|
+
_validate_settings(
|
|
291
|
+
settings=adapter_settings,
|
|
292
|
+
path_prefix=f"items.{item_name}.settings.Adapter",
|
|
293
|
+
local_class=None,
|
|
294
|
+
iris_class_name="",
|
|
295
|
+
host_class_name=iris_class_name,
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
return issues
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _validate_settings(
|
|
302
|
+
*,
|
|
303
|
+
settings: dict[str, Any],
|
|
304
|
+
path_prefix: str,
|
|
305
|
+
local_class: type | None,
|
|
306
|
+
iris_class_name: str,
|
|
307
|
+
host_class_name: str = "",
|
|
308
|
+
) -> list[ProductionValidationIssue]:
|
|
309
|
+
if not settings:
|
|
310
|
+
return []
|
|
311
|
+
if local_class is not None:
|
|
312
|
+
local_names = _local_setting_names(local_class)
|
|
313
|
+
if local_names is not None:
|
|
314
|
+
return _validate_names_with_known_set(
|
|
315
|
+
settings,
|
|
316
|
+
path_prefix=path_prefix,
|
|
317
|
+
known_names=local_names,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
iris_target_class = iris_class_name or _adapter_class_name_from_host(host_class_name)
|
|
321
|
+
if iris_target_class:
|
|
322
|
+
report = _validate_names_with_iris_dictionary(
|
|
323
|
+
settings,
|
|
324
|
+
path_prefix=path_prefix,
|
|
325
|
+
iris_class_name=iris_target_class,
|
|
326
|
+
)
|
|
327
|
+
if report is not None:
|
|
328
|
+
return report
|
|
329
|
+
return []
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _local_setting_names(cls: type) -> set[str] | None:
|
|
333
|
+
getter = getattr(cls, "_get_properties", None)
|
|
334
|
+
if not callable(getter):
|
|
335
|
+
return None
|
|
336
|
+
try:
|
|
337
|
+
return {str(prop[0]) for prop in getter() if prop}
|
|
338
|
+
except Exception:
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _validate_names_with_known_set(
|
|
343
|
+
settings: dict[str, Any],
|
|
344
|
+
*,
|
|
345
|
+
path_prefix: str,
|
|
346
|
+
known_names: set[str],
|
|
347
|
+
) -> list[ProductionValidationIssue]:
|
|
348
|
+
issues: list[ProductionValidationIssue] = []
|
|
349
|
+
for setting_name in sorted(settings):
|
|
350
|
+
if setting_name.startswith("%") or setting_name in known_names:
|
|
351
|
+
continue
|
|
352
|
+
alias = SETTING_NAME_ALIASES.get(setting_name)
|
|
353
|
+
if alias in known_names:
|
|
354
|
+
issues.append(_setting_alias_issue(path_prefix, setting_name, alias))
|
|
355
|
+
continue
|
|
356
|
+
issues.append(_unknown_setting_issue(path_prefix, setting_name))
|
|
357
|
+
return issues
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _validate_names_with_iris_dictionary(
|
|
361
|
+
settings: dict[str, Any],
|
|
362
|
+
*,
|
|
363
|
+
path_prefix: str,
|
|
364
|
+
iris_class_name: str,
|
|
365
|
+
) -> list[ProductionValidationIssue] | None:
|
|
366
|
+
iris = _iris_module()
|
|
367
|
+
if iris is None or not _iris_class_exists(iris, iris_class_name):
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
issues: list[ProductionValidationIssue] = []
|
|
371
|
+
for setting_name in sorted(settings):
|
|
372
|
+
if _iris_property_exists(iris, iris_class_name, setting_name):
|
|
373
|
+
continue
|
|
374
|
+
alias = SETTING_NAME_ALIASES.get(setting_name)
|
|
375
|
+
if alias and _iris_property_exists(iris, iris_class_name, alias):
|
|
376
|
+
issues.append(_setting_alias_issue(path_prefix, setting_name, alias))
|
|
377
|
+
continue
|
|
378
|
+
issues.append(_unknown_setting_issue(path_prefix, setting_name))
|
|
379
|
+
return issues
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _adapter_class_name_from_host(host_class_name: str) -> str:
|
|
383
|
+
if not host_class_name:
|
|
384
|
+
return ""
|
|
385
|
+
iris = _iris_module()
|
|
386
|
+
if iris is None:
|
|
387
|
+
return ""
|
|
388
|
+
try:
|
|
389
|
+
parameter = iris._Dictionary.CompiledParameter._OpenId(
|
|
390
|
+
f"{host_class_name}||ADAPTER"
|
|
391
|
+
)
|
|
392
|
+
return str(getattr(parameter, "Default", "") or "")
|
|
393
|
+
except Exception:
|
|
394
|
+
return ""
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _iris_module():
|
|
398
|
+
try:
|
|
399
|
+
import iris # type: ignore
|
|
400
|
+
except Exception:
|
|
401
|
+
return None
|
|
402
|
+
return iris
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _iris_class_exists(iris: Any, class_name: str) -> bool:
|
|
406
|
+
try:
|
|
407
|
+
return bool(iris._Dictionary.CompiledClass._ExistsId(class_name))
|
|
408
|
+
except Exception:
|
|
409
|
+
return False
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _iris_property_exists(iris: Any, class_name: str, property_name: str) -> bool:
|
|
413
|
+
try:
|
|
414
|
+
return bool(
|
|
415
|
+
iris._Dictionary.CompiledProperty._ExistsId(
|
|
416
|
+
f"{class_name}||{property_name}"
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
except Exception:
|
|
420
|
+
return False
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _setting_alias_issue(
|
|
424
|
+
path_prefix: str,
|
|
425
|
+
setting_name: str,
|
|
426
|
+
alias: str,
|
|
427
|
+
) -> ProductionValidationIssue:
|
|
428
|
+
return ProductionValidationIssue(
|
|
429
|
+
kind="setting",
|
|
430
|
+
path=f"{path_prefix}.{setting_name}",
|
|
431
|
+
message=(
|
|
432
|
+
"Setting name is accepted as a known alias for validation, "
|
|
433
|
+
"but IoP will emit the original name."
|
|
434
|
+
),
|
|
435
|
+
suggestion=f"Use {alias!r}.",
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _unknown_setting_issue(path_prefix: str, setting_name: str) -> ProductionValidationIssue:
|
|
440
|
+
return ProductionValidationIssue(
|
|
441
|
+
kind="setting",
|
|
442
|
+
path=f"{path_prefix}.{setting_name}",
|
|
443
|
+
message="Unknown setting name.",
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _is_production_object(value: Any) -> bool:
|
|
448
|
+
return (
|
|
449
|
+
hasattr(value, "to_dict")
|
|
450
|
+
and hasattr(value, "component_registrations")
|
|
451
|
+
and hasattr(value, "name")
|
|
452
|
+
)
|
iop/runtime/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Runtime integrations for local IRIS and remote IOP REST access."""
|