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.
Files changed (119) hide show
  1. iop/__init__.py +276 -18
  2. iop/__main__.py +3 -3
  3. iop/cli/__init__.py +1 -0
  4. iop/cli/formatting.py +45 -0
  5. iop/{_cli.py → cli/main.py} +238 -216
  6. iop/cli/parser.py +165 -0
  7. iop/cli/types.py +73 -0
  8. iop/cls/IOP/Common.cls +11 -2
  9. iop/cls/IOP/PrivateSession/Duplex.cls +1 -1
  10. iop/cls/IOP/Service/Remote/Rest/v1.cls +152 -5
  11. iop/cls/IOP/Utils.cls +814 -7
  12. iop/cls/IOP/Wrapper.cls +1 -1
  13. iop/components/__init__.py +16 -0
  14. iop/{_async_request.py → components/async_request.py} +7 -7
  15. iop/components/business_host.py +363 -0
  16. iop/components/business_operation.py +84 -0
  17. iop/{_business_process.py → components/business_process.py} +145 -125
  18. iop/components/business_service.py +106 -0
  19. iop/{_common.py → components/common.py} +160 -164
  20. iop/{_debugpy.py → components/debugpy.py} +11 -11
  21. iop/{_generator_request.py → components/generator_request.py} +4 -4
  22. iop/{_inbound_adapter.py → components/inbound_adapter.py} +10 -18
  23. iop/{_log_manager.py → components/log_manager.py} +1 -1
  24. iop/{_outbound_adapter.py → components/outbound_adapter.py} +1 -1
  25. iop/components/polling_business_service.py +48 -0
  26. iop/{_private_session_duplex.py → components/private_session_duplex.py} +7 -9
  27. iop/{_private_session_process.py → components/private_session_process.py} +2 -2
  28. iop/{_settings.py → components/settings.py} +23 -3
  29. iop/messages/__init__.py +7 -0
  30. iop/messages/base.py +61 -0
  31. iop/{_decorators.py → messages/decorators.py} +8 -2
  32. iop/messages/dispatch.py +351 -0
  33. iop/{_persistent_message.py → messages/persistent.py} +24 -19
  34. iop/{_serialization.py → messages/serialization.py} +34 -21
  35. iop/{_message_validator.py → messages/validation.py} +15 -13
  36. iop/migration/__init__.py +1 -0
  37. iop/migration/io.py +61 -0
  38. iop/migration/manifest.py +534 -0
  39. iop/migration/plans.py +80 -0
  40. iop/migration/utils.py +1126 -0
  41. iop/production/__init__.py +55 -0
  42. iop/production/actions.py +525 -0
  43. iop/production/common.py +103 -0
  44. iop/production/component.py +234 -0
  45. iop/production/declarations.py +190 -0
  46. iop/production/declarative.py +214 -0
  47. iop/production/diff.py +327 -0
  48. iop/production/import_.py +296 -0
  49. iop/production/inspection.py +92 -0
  50. iop/production/model.py +1143 -0
  51. iop/production/planning.py +678 -0
  52. iop/production/reconstruction.py +341 -0
  53. iop/production/rendering.py +610 -0
  54. iop/production/runtime.py +91 -0
  55. iop/production/source_inference.py +729 -0
  56. iop/production/types.py +546 -0
  57. iop/production/validation.py +452 -0
  58. iop/runtime/__init__.py +1 -0
  59. iop/runtime/director.py +415 -0
  60. iop/runtime/environment.py +59 -0
  61. iop/{_iris.py → runtime/iris.py} +2 -2
  62. iop/runtime/local.py +145 -0
  63. iop/{_director_protocol.py → runtime/protocol.py} +25 -8
  64. iop/runtime/remote/__init__.py +12 -0
  65. iop/runtime/remote/client.py +90 -0
  66. iop/runtime/remote/director.py +283 -0
  67. iop/runtime/remote/migration.py +57 -0
  68. iop/runtime/remote/settings.py +69 -0
  69. iop/runtime/remote/setup.py +72 -0
  70. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/METADATA +11 -2
  71. iris_pex_embedded_python-4.0.0.dist-info/RECORD +99 -0
  72. iris_pex_embedded_python-4.0.0.dist-info/entry_points.txt +2 -0
  73. iris_pex_embedded_python-4.0.0.dist-info/top_level.txt +1 -0
  74. grongier/__init__.py +0 -0
  75. grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -8
  76. grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -13
  77. grongier/cls/Grongier/PEX/BusinessService.cls +0 -8
  78. grongier/cls/Grongier/PEX/Common.cls +0 -10
  79. grongier/cls/Grongier/PEX/Director.cls +0 -10
  80. grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -4
  81. grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -13
  82. grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -4
  83. grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -8
  84. grongier/cls/Grongier/PEX/Message.cls +0 -13
  85. grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -8
  86. grongier/cls/Grongier/PEX/PickleMessage.cls +0 -13
  87. grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -8
  88. grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -14
  89. grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -14
  90. grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -14
  91. grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -14
  92. grongier/cls/Grongier/PEX/Test.cls +0 -10
  93. grongier/cls/Grongier/PEX/Utils.cls +0 -10
  94. grongier/cls/Grongier/Service/WSGI.cls +0 -4
  95. grongier/pex/__init__.py +0 -75
  96. grongier/pex/__main__.py +0 -4
  97. grongier/pex/_business_host.py +0 -1
  98. grongier/pex/_cli.py +0 -4
  99. grongier/pex/_common.py +0 -1
  100. grongier/pex/_director.py +0 -1
  101. grongier/pex/_utils.py +0 -1
  102. grongier/pex/wsgi/handlers.py +0 -104
  103. iop/_business_host.py +0 -322
  104. iop/_business_operation.py +0 -77
  105. iop/_business_service.py +0 -67
  106. iop/_director.py +0 -333
  107. iop/_dispatch.py +0 -194
  108. iop/_local.py +0 -106
  109. iop/_message.py +0 -45
  110. iop/_polling_business_service.py +0 -6
  111. iop/_remote.py +0 -473
  112. iop/_utils.py +0 -912
  113. iop/cls/IOP/Service/WSGI.cls +0 -310
  114. iop/wsgi/handlers.py +0 -112
  115. iris_pex_embedded_python-3.7.2b2.dist-info/RECORD +0 -96
  116. iris_pex_embedded_python-3.7.2b2.dist-info/entry_points.txt +0 -2
  117. iris_pex_embedded_python-3.7.2b2.dist-info/top_level.txt +0 -2
  118. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/WHEEL +0 -0
  119. {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
+ )
@@ -0,0 +1 @@
1
+ """Runtime integrations for local IRIS and remote IOP REST access."""