iris-pex-embedded-python 4.0.0b4__py3-none-any.whl → 4.0.0b6__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 +12 -0
- iop/cli/main.py +3 -0
- iop/cli/parser.py +1 -1
- iop/cls/IOP/Utils.cls +10 -0
- iop/components/settings.py +11 -1
- iop/migration/utils.py +15 -5
- iop/production/__init__.py +10 -0
- iop/production/common.py +5 -0
- iop/production/declarations.py +194 -0
- iop/production/declarative.py +154 -0
- iop/production/model.py +165 -36
- iop/production/reconstruction.py +3 -4
- iop/production/rendering.py +296 -18
- iop/production/types.py +10 -1
- iop/production/validation.py +7 -8
- {iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/METADATA +1 -1
- {iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/RECORD +21 -19
- {iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/WHEEL +0 -0
- {iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/entry_points.txt +0 -0
- {iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/licenses/LICENSE +0 -0
- {iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/top_level.txt +0 -0
iop/__init__.py
CHANGED
|
@@ -25,8 +25,11 @@ from iop.migration.utils import list_bindings as list_bindings
|
|
|
25
25
|
from iop.migration.utils import register_component as register_component
|
|
26
26
|
from iop.migration.utils import unbind_component as unbind_component
|
|
27
27
|
from iop.migration.utils import unregister_component as unregister_component
|
|
28
|
+
from iop.production import ComponentItem as ComponentItem
|
|
28
29
|
from iop.production import ComponentRef as ComponentRef
|
|
30
|
+
from iop.production import OperationItem as OperationItem
|
|
29
31
|
from iop.production import Port as Port
|
|
32
|
+
from iop.production import ProcessItem as ProcessItem
|
|
30
33
|
from iop.production import Production as Production
|
|
31
34
|
from iop.production import ProductionDiff as ProductionDiff
|
|
32
35
|
from iop.production import ProductionDiffEntry as ProductionDiffEntry
|
|
@@ -35,6 +38,9 @@ from iop.production import ProductionValidationError as ProductionValidationErro
|
|
|
35
38
|
from iop.production import ProductionValidationIssue as ProductionValidationIssue
|
|
36
39
|
from iop.production import ProductionValidationReport as ProductionValidationReport
|
|
37
40
|
from iop.production import ProductionValidationWarning as ProductionValidationWarning
|
|
41
|
+
from iop.production import Route as Route
|
|
42
|
+
from iop.production import ServiceItem as ServiceItem
|
|
43
|
+
from iop.production import TargetSetting as TargetSetting
|
|
38
44
|
from iop.production import target as target
|
|
39
45
|
from iop.runtime.director import _Director
|
|
40
46
|
from iop.runtime.protocol import DirectorProtocol as DirectorProtocol
|
|
@@ -44,6 +50,7 @@ __all__ = [
|
|
|
44
50
|
"BusinessProcess",
|
|
45
51
|
"BusinessService",
|
|
46
52
|
"Category",
|
|
53
|
+
"ComponentItem",
|
|
47
54
|
"ComponentRef",
|
|
48
55
|
"Director",
|
|
49
56
|
"DirectorProtocol",
|
|
@@ -54,11 +61,13 @@ __all__ = [
|
|
|
54
61
|
"InboundAdapter",
|
|
55
62
|
"Message",
|
|
56
63
|
"Model",
|
|
64
|
+
"OperationItem",
|
|
57
65
|
"OutboundAdapter",
|
|
58
66
|
"PersistentMessage",
|
|
59
67
|
"PickleMessage",
|
|
60
68
|
"PollingBusinessService",
|
|
61
69
|
"Port",
|
|
70
|
+
"ProcessItem",
|
|
62
71
|
"Production",
|
|
63
72
|
"ProductionDiff",
|
|
64
73
|
"ProductionDiffEntry",
|
|
@@ -69,7 +78,10 @@ __all__ = [
|
|
|
69
78
|
"ProductionValidationWarning",
|
|
70
79
|
"PydanticMessage",
|
|
71
80
|
"PydanticPickleMessage",
|
|
81
|
+
"Route",
|
|
82
|
+
"ServiceItem",
|
|
72
83
|
"Setting",
|
|
84
|
+
"TargetSetting",
|
|
73
85
|
"Utils",
|
|
74
86
|
"bind_component",
|
|
75
87
|
"controls",
|
iop/cli/main.py
CHANGED
|
@@ -230,6 +230,9 @@ class Command:
|
|
|
230
230
|
if export_format == "python":
|
|
231
231
|
print(production.to_python(), end="")
|
|
232
232
|
return
|
|
233
|
+
if export_format == "class":
|
|
234
|
+
print(production.to_class(), end="")
|
|
235
|
+
return
|
|
233
236
|
if export_format == "graph":
|
|
234
237
|
print(production.graph())
|
|
235
238
|
return
|
iop/cli/parser.py
CHANGED
|
@@ -105,7 +105,7 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
105
105
|
export.add_argument(
|
|
106
106
|
"--format",
|
|
107
107
|
dest="export_format",
|
|
108
|
-
choices=("json", "python", "graph"),
|
|
108
|
+
choices=("json", "python", "class", "graph"),
|
|
109
109
|
default="json",
|
|
110
110
|
help="export format for -e/--export",
|
|
111
111
|
)
|
iop/cls/IOP/Utils.cls
CHANGED
|
@@ -595,6 +595,7 @@ ClassMethod CreateProductionFromJSON(
|
|
|
595
595
|
#dim tPayload As %DynamicObject
|
|
596
596
|
#dim tProductionData As %DynamicObject
|
|
597
597
|
#dim tProduction As Ens.Config.Production
|
|
598
|
+
#dim tProductionClass As %Dictionary.ClassDefinition
|
|
598
599
|
#dim tItems,tDeclaredName
|
|
599
600
|
Try {
|
|
600
601
|
Return:(""=pProductionName) $$$ERROR($$$EnsErrGeneral,"Production name is required.")
|
|
@@ -614,6 +615,15 @@ ClassMethod CreateProductionFromJSON(
|
|
|
614
615
|
}
|
|
615
616
|
}
|
|
616
617
|
|
|
618
|
+
If ('##class(%Dictionary.ClassDefinition).%ExistsId(pProductionName)) {
|
|
619
|
+
Set tProductionClass = ##class(%Dictionary.ClassDefinition).%New()
|
|
620
|
+
Set tProductionClass.Name = pProductionName
|
|
621
|
+
Set tProductionClass.Super = "Ens.Production"
|
|
622
|
+
Set tProductionClass.ClassVersion = 25
|
|
623
|
+
Set tSC = tProductionClass.%Save()
|
|
624
|
+
Quit:$$$ISERR(tSC)
|
|
625
|
+
}
|
|
626
|
+
|
|
617
627
|
Set tProduction = ##class(Ens.Config.Production).%New()
|
|
618
628
|
Set tProduction.Name = pProductionName
|
|
619
629
|
Set tProduction.Description = ..DynamicGet(tProductionData,"Description","")
|
iop/components/settings.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from enum import Enum
|
|
2
|
-
from typing import Any
|
|
4
|
+
from typing import Any, overload
|
|
3
5
|
|
|
4
6
|
_MISSING = object()
|
|
5
7
|
|
|
@@ -45,9 +47,17 @@ class Setting:
|
|
|
45
47
|
self.control = control or ""
|
|
46
48
|
self.exclude = exclude
|
|
47
49
|
self.name = ""
|
|
50
|
+
self.owner = None
|
|
48
51
|
|
|
49
52
|
def __set_name__(self, owner, name):
|
|
50
53
|
self.name = name
|
|
54
|
+
self.owner = owner
|
|
55
|
+
|
|
56
|
+
@overload
|
|
57
|
+
def __get__(self, instance: None, owner: type | None = None) -> Setting: ...
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
def __get__(self, instance: object, owner: type | None = None) -> Any: ...
|
|
51
61
|
|
|
52
62
|
def __get__(self, instance, owner=None):
|
|
53
63
|
if instance is None:
|
iop/migration/utils.py
CHANGED
|
@@ -1036,11 +1036,21 @@ def register_production_definition(production_name: str, production: dict):
|
|
|
1036
1036
|
:param production_name: full IRIS production class name
|
|
1037
1037
|
:param production: normalized {"Production": {...}} dictionary
|
|
1038
1038
|
"""
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1039
|
+
try:
|
|
1040
|
+
raise_on_error(
|
|
1041
|
+
_iris.get_iris()
|
|
1042
|
+
.cls("IOP.Utils")
|
|
1043
|
+
.CreateProductionFromJSON(production_name, json.dumps(production))
|
|
1044
|
+
)
|
|
1045
|
+
except RuntimeError as exc:
|
|
1046
|
+
if not _is_missing_production_class_error(exc, production_name):
|
|
1047
|
+
raise
|
|
1048
|
+
register_production(production_name, dict_to_xml(production))
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
def _is_missing_production_class_error(exc: RuntimeError, production_name: str) -> bool:
|
|
1052
|
+
message = str(exc)
|
|
1053
|
+
return "CLASS DOES NOT EXIST" in message and production_name in message
|
|
1044
1054
|
|
|
1045
1055
|
|
|
1046
1056
|
def export_production(production_name):
|
iop/production/__init__.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from .component import ComponentRef as ComponentRef
|
|
4
|
+
from .declarations import ComponentItem as ComponentItem
|
|
5
|
+
from .declarations import OperationItem as OperationItem
|
|
6
|
+
from .declarations import ProcessItem as ProcessItem
|
|
7
|
+
from .declarations import Route as Route
|
|
8
|
+
from .declarations import ServiceItem as ServiceItem
|
|
4
9
|
from .model import Production as Production
|
|
5
10
|
from .runtime import resolve_target as resolve_target
|
|
6
11
|
from .types import GraphEdge as GraphEdge
|
|
@@ -19,10 +24,13 @@ from .validation import ProductionValidationWarning as ProductionValidationWarni
|
|
|
19
24
|
|
|
20
25
|
__all__ = [
|
|
21
26
|
"ComponentRef",
|
|
27
|
+
"ComponentItem",
|
|
22
28
|
"GraphEdge",
|
|
23
29
|
"GraphNode",
|
|
30
|
+
"OperationItem",
|
|
24
31
|
"PersistentMessageRegistration",
|
|
25
32
|
"Port",
|
|
33
|
+
"ProcessItem",
|
|
26
34
|
"Production",
|
|
27
35
|
"ProductionDiff",
|
|
28
36
|
"ProductionDiffEntry",
|
|
@@ -31,6 +39,8 @@ __all__ = [
|
|
|
31
39
|
"ProductionValidationIssue",
|
|
32
40
|
"ProductionValidationReport",
|
|
33
41
|
"ProductionValidationWarning",
|
|
42
|
+
"Route",
|
|
43
|
+
"ServiceItem",
|
|
34
44
|
"TargetSetting",
|
|
35
45
|
"resolve_target",
|
|
36
46
|
"target",
|
iop/production/common.py
CHANGED
|
@@ -22,6 +22,11 @@ PRODUCTION_SETTING_FIELDS_BY_IRIS: dict[str, str] = {
|
|
|
22
22
|
for field_name, (iris_name, _default) in PRODUCTION_SETTING_FIELDS.items()
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
SETTING_NAME_ALIASES = {
|
|
26
|
+
"target_config_name": "TargetConfigName",
|
|
27
|
+
"target_config_names": "TargetConfigNames",
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
|
|
26
31
|
def _bool_text(value: bool | str) -> str:
|
|
27
32
|
if isinstance(value, bool):
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable, Mapping
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, ClassVar, Protocol
|
|
6
|
+
|
|
7
|
+
from .common import SETTING_NAME_ALIASES
|
|
8
|
+
from .types import TargetSetting
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _NamedRouteTarget(Protocol):
|
|
12
|
+
@property
|
|
13
|
+
def name(self) -> str: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class Route:
|
|
18
|
+
"""Declarative route from a production item port to one or more targets."""
|
|
19
|
+
|
|
20
|
+
port: str | TargetSetting
|
|
21
|
+
targets: str | _NamedRouteTarget | Iterable[str | _NamedRouteTarget]
|
|
22
|
+
logical_name: str = ""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def port_name(self) -> str:
|
|
26
|
+
return normalize_route_port(self.port)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def port_owner(self) -> type | None:
|
|
30
|
+
if isinstance(self.port, TargetSetting):
|
|
31
|
+
return self.port.owner
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def route_logical_name(self) -> str:
|
|
36
|
+
if self.logical_name:
|
|
37
|
+
return self.logical_name
|
|
38
|
+
if isinstance(self.port, TargetSetting):
|
|
39
|
+
return self.port.logical_name
|
|
40
|
+
return ""
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def target_names(self) -> tuple[str, ...]:
|
|
44
|
+
if _is_route_target(self.targets):
|
|
45
|
+
targets = (self.targets,)
|
|
46
|
+
else:
|
|
47
|
+
try:
|
|
48
|
+
targets = tuple(self.targets)
|
|
49
|
+
except TypeError as exc:
|
|
50
|
+
raise TypeError(
|
|
51
|
+
f"Route {self.port_name!r} targets must be an item name, "
|
|
52
|
+
"a production item declaration, or an iterable of either"
|
|
53
|
+
) from exc
|
|
54
|
+
if not targets:
|
|
55
|
+
raise ValueError(f"Route {self.port_name!r} requires at least one target")
|
|
56
|
+
return tuple(_route_target_name(target, self.port_name) for target in targets)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class _ProductionItemDeclaration:
|
|
61
|
+
name: str
|
|
62
|
+
component: type | str | None = None
|
|
63
|
+
class_name: str | None = None
|
|
64
|
+
adapter_class: type | str | None = None
|
|
65
|
+
adapter_class_name: str | None = None
|
|
66
|
+
enabled: bool | str = True
|
|
67
|
+
pool_size: int | str = 1
|
|
68
|
+
category: str = ""
|
|
69
|
+
foreground: bool | str = False
|
|
70
|
+
comment: str = ""
|
|
71
|
+
log_trace_events: bool | str = False
|
|
72
|
+
schedule: str = ""
|
|
73
|
+
settings: Mapping[str, Any] | None = None
|
|
74
|
+
host_settings: Mapping[str, Any] | None = None
|
|
75
|
+
adapter_settings: Mapping[str, Any] | None = None
|
|
76
|
+
other_settings: Iterable[Mapping[str, Any]] | None = None
|
|
77
|
+
routes: Route | Iterable[Route] | None = field(default_factory=tuple)
|
|
78
|
+
|
|
79
|
+
kind: ClassVar[str] = "component"
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def host_setting_values(self) -> dict[str, Any]:
|
|
83
|
+
settings = _mapping(self.settings, "settings", self.name)
|
|
84
|
+
host_settings = _mapping(self.host_settings, "host_settings", self.name)
|
|
85
|
+
duplicates = sorted(settings.keys() & host_settings.keys())
|
|
86
|
+
if duplicates:
|
|
87
|
+
names = ", ".join(repr(name) for name in duplicates)
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"{self.kind.title()} item {self.name!r} declares duplicate "
|
|
90
|
+
f"Host setting keys: {names}"
|
|
91
|
+
)
|
|
92
|
+
merged = dict(settings)
|
|
93
|
+
merged.update(host_settings)
|
|
94
|
+
return merged
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def adapter_setting_values(self) -> dict[str, Any]:
|
|
98
|
+
return _mapping(self.adapter_settings, "adapter_settings", self.name)
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def other_setting_values(self) -> list[dict[str, Any]]:
|
|
102
|
+
return [dict(setting) for setting in self.other_settings or ()]
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def route_values(self) -> tuple[Route, ...]:
|
|
106
|
+
if self.routes is None:
|
|
107
|
+
return ()
|
|
108
|
+
if isinstance(self.routes, Route):
|
|
109
|
+
return (self.routes,)
|
|
110
|
+
routes = tuple(self.routes)
|
|
111
|
+
for route in routes:
|
|
112
|
+
if not isinstance(route, Route):
|
|
113
|
+
raise TypeError(
|
|
114
|
+
f"{self.kind.title()} item {self.name!r} routes must be Route "
|
|
115
|
+
f"instances"
|
|
116
|
+
)
|
|
117
|
+
return routes
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class ServiceItem(_ProductionItemDeclaration):
|
|
122
|
+
kind: ClassVar[str] = "service"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True)
|
|
126
|
+
class ComponentItem(_ProductionItemDeclaration):
|
|
127
|
+
kind: ClassVar[str] = "component"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass(frozen=True)
|
|
131
|
+
class ProcessItem(_ProductionItemDeclaration):
|
|
132
|
+
kind: ClassVar[str] = "process"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass(frozen=True)
|
|
136
|
+
class OperationItem(_ProductionItemDeclaration):
|
|
137
|
+
kind: ClassVar[str] = "operation"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def normalize_route_port(name: str | TargetSetting) -> str:
|
|
141
|
+
"""Normalize known Pythonic route aliases without changing other settings."""
|
|
142
|
+
|
|
143
|
+
if isinstance(name, TargetSetting):
|
|
144
|
+
if not name.name:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
"Route target setting must be declared on a component class"
|
|
147
|
+
)
|
|
148
|
+
return name.name
|
|
149
|
+
port_name = str(name)
|
|
150
|
+
return SETTING_NAME_ALIASES.get(port_name, port_name)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def normalize_route_port_for_match(name: str | TargetSetting) -> str:
|
|
154
|
+
return normalize_route_port(name)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _is_route_target(value: Any) -> bool:
|
|
158
|
+
return isinstance(value, str) or isinstance(value, _ProductionItemDeclaration)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _route_target_name(value: Any, port_name: str) -> str:
|
|
162
|
+
if isinstance(value, str):
|
|
163
|
+
if value:
|
|
164
|
+
return value
|
|
165
|
+
elif isinstance(value, _ProductionItemDeclaration):
|
|
166
|
+
if value.name:
|
|
167
|
+
return value.name
|
|
168
|
+
|
|
169
|
+
raise TypeError(
|
|
170
|
+
f"Route {port_name!r} targets must be item names or production item "
|
|
171
|
+
"declarations"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _mapping(
|
|
176
|
+
values: Mapping[str, Any] | None,
|
|
177
|
+
field_name: str,
|
|
178
|
+
item_name: str,
|
|
179
|
+
) -> dict[str, Any]:
|
|
180
|
+
try:
|
|
181
|
+
return dict(values or {})
|
|
182
|
+
except (TypeError, ValueError) as exc:
|
|
183
|
+
raise TypeError(
|
|
184
|
+
f"Production item {item_name!r} {field_name} must be a mapping"
|
|
185
|
+
) from exc
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
__all__ = [
|
|
189
|
+
"ComponentItem",
|
|
190
|
+
"OperationItem",
|
|
191
|
+
"ProcessItem",
|
|
192
|
+
"Route",
|
|
193
|
+
"ServiceItem",
|
|
194
|
+
]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from typing import Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from .component import ComponentRef
|
|
7
|
+
from .declarations import (
|
|
8
|
+
_ProductionItemDeclaration,
|
|
9
|
+
normalize_route_port_for_match,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _DeclarativeProductionMixin:
|
|
14
|
+
components: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
|
|
15
|
+
services: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
|
|
16
|
+
processes: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
|
|
17
|
+
operations: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
|
|
18
|
+
|
|
19
|
+
def _hydrate_declared_items(self) -> None:
|
|
20
|
+
declarations: list[_ProductionItemDeclaration] = []
|
|
21
|
+
for attr_name, expected_kind in (
|
|
22
|
+
("components", "component"),
|
|
23
|
+
("services", "service"),
|
|
24
|
+
("processes", "process"),
|
|
25
|
+
("operations", "operation"),
|
|
26
|
+
):
|
|
27
|
+
for declaration in self._declared_items(attr_name):
|
|
28
|
+
if declaration.kind != expected_kind:
|
|
29
|
+
raise TypeError(
|
|
30
|
+
f"{attr_name} must contain {expected_kind.title()}Item "
|
|
31
|
+
f"declarations"
|
|
32
|
+
)
|
|
33
|
+
self._raise_declared_route_conflicts(declaration)
|
|
34
|
+
self._add_declared_item(declaration)
|
|
35
|
+
declarations.append(declaration)
|
|
36
|
+
|
|
37
|
+
for declaration in declarations:
|
|
38
|
+
self._connect_declared_routes(declaration)
|
|
39
|
+
|
|
40
|
+
def _declared_items(self, attr_name: str) -> tuple[_ProductionItemDeclaration, ...]:
|
|
41
|
+
values = getattr(type(self), attr_name, ())
|
|
42
|
+
if values is None:
|
|
43
|
+
return ()
|
|
44
|
+
if isinstance(values, _ProductionItemDeclaration):
|
|
45
|
+
return (values,)
|
|
46
|
+
try:
|
|
47
|
+
declarations = tuple(values)
|
|
48
|
+
except TypeError as exc:
|
|
49
|
+
raise TypeError(f"{attr_name} must be an iterable of item declarations") from exc
|
|
50
|
+
for declaration in declarations:
|
|
51
|
+
if not isinstance(declaration, _ProductionItemDeclaration):
|
|
52
|
+
raise TypeError(f"{attr_name} must contain production item declarations")
|
|
53
|
+
return declarations
|
|
54
|
+
|
|
55
|
+
def _raise_declared_route_conflicts(
|
|
56
|
+
self,
|
|
57
|
+
declaration: _ProductionItemDeclaration,
|
|
58
|
+
) -> None:
|
|
59
|
+
host_ports = {
|
|
60
|
+
normalize_route_port_for_match(name)
|
|
61
|
+
for name in declaration.host_setting_values
|
|
62
|
+
}
|
|
63
|
+
route_ports = {route.port_name for route in declaration.route_values}
|
|
64
|
+
conflicts = sorted(host_ports & route_ports)
|
|
65
|
+
if not conflicts:
|
|
66
|
+
return
|
|
67
|
+
names = ", ".join(repr(name) for name in conflicts)
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"{declaration.kind.title()} item {declaration.name!r} declares route "
|
|
70
|
+
f"port(s) in Host settings: {names}. Declare route ports with Route only."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def _add_declared_item(self, declaration: _ProductionItemDeclaration) -> None:
|
|
74
|
+
kwargs: dict[str, Any] = {
|
|
75
|
+
"enabled": declaration.enabled,
|
|
76
|
+
"pool_size": declaration.pool_size,
|
|
77
|
+
"category": declaration.category,
|
|
78
|
+
"foreground": declaration.foreground,
|
|
79
|
+
"comment": declaration.comment,
|
|
80
|
+
"log_trace_events": declaration.log_trace_events,
|
|
81
|
+
"schedule": declaration.schedule,
|
|
82
|
+
"settings": declaration.host_setting_values,
|
|
83
|
+
"adapter_settings": declaration.adapter_setting_values,
|
|
84
|
+
}
|
|
85
|
+
if declaration.adapter_class is not None:
|
|
86
|
+
kwargs["adapter_class"] = declaration.adapter_class
|
|
87
|
+
if declaration.adapter_class_name is not None:
|
|
88
|
+
kwargs["adapter_class_name"] = declaration.adapter_class_name
|
|
89
|
+
|
|
90
|
+
method = {
|
|
91
|
+
"component": self.component,
|
|
92
|
+
"service": self.service,
|
|
93
|
+
"process": self.process,
|
|
94
|
+
"operation": self.operation,
|
|
95
|
+
}[declaration.kind]
|
|
96
|
+
|
|
97
|
+
component = declaration.component
|
|
98
|
+
if isinstance(component, type):
|
|
99
|
+
if declaration.class_name is not None:
|
|
100
|
+
kwargs["class_name"] = declaration.class_name
|
|
101
|
+
ref = method(declaration.name, component, **kwargs)
|
|
102
|
+
ref.other_settings = declaration.other_setting_values
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
class_name = declaration.class_name
|
|
106
|
+
if component is not None:
|
|
107
|
+
component_class_name = str(component)
|
|
108
|
+
if class_name is not None and class_name != component_class_name:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f"{declaration.kind.title()} item {declaration.name!r} "
|
|
111
|
+
"declares conflicting component and class_name values"
|
|
112
|
+
)
|
|
113
|
+
class_name = component_class_name
|
|
114
|
+
|
|
115
|
+
if class_name is None:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"{declaration.kind.title()} item {declaration.name!r} requires "
|
|
118
|
+
"a component class or class_name"
|
|
119
|
+
)
|
|
120
|
+
kwargs["class_name"] = class_name
|
|
121
|
+
ref = method(declaration.name, **kwargs)
|
|
122
|
+
ref.other_settings = declaration.other_setting_values
|
|
123
|
+
|
|
124
|
+
def _connect_declared_routes(self, declaration: _ProductionItemDeclaration) -> None:
|
|
125
|
+
source = self.item(declaration.name)
|
|
126
|
+
for route in declaration.route_values:
|
|
127
|
+
self._raise_if_route_port_owner_mismatch(source, route)
|
|
128
|
+
port = source.port(
|
|
129
|
+
route.port_name,
|
|
130
|
+
logical_name=route.route_logical_name,
|
|
131
|
+
)
|
|
132
|
+
targets = route.target_names
|
|
133
|
+
self.connect(port, targets[0])
|
|
134
|
+
for target in targets[1:]:
|
|
135
|
+
self.connect_add(port, target)
|
|
136
|
+
|
|
137
|
+
def _raise_if_route_port_owner_mismatch(
|
|
138
|
+
self,
|
|
139
|
+
source: ComponentRef,
|
|
140
|
+
route: Any,
|
|
141
|
+
) -> None:
|
|
142
|
+
owner = route.port_owner
|
|
143
|
+
if (
|
|
144
|
+
owner is None
|
|
145
|
+
or source.component_class is None
|
|
146
|
+
or issubclass(source.component_class, owner)
|
|
147
|
+
):
|
|
148
|
+
return
|
|
149
|
+
raise ValueError(
|
|
150
|
+
f"Route port {route.port_name!r} belongs to "
|
|
151
|
+
f"{owner.__module__}.{owner.__qualname__}, not "
|
|
152
|
+
f"{source.component_class.__module__}."
|
|
153
|
+
f"{source.component_class.__qualname__}"
|
|
154
|
+
)
|
iop/production/model.py
CHANGED
|
@@ -11,11 +11,13 @@ from .common import (
|
|
|
11
11
|
_auto_proxy_class_name,
|
|
12
12
|
)
|
|
13
13
|
from .component import ComponentRef
|
|
14
|
+
from .declarative import _DeclarativeProductionMixin
|
|
14
15
|
from .diff import _diff_productions
|
|
15
16
|
from .inspection import component_runtime_info, inspect_component
|
|
16
17
|
from .reconstruction import production_from_dict
|
|
17
18
|
from .rendering import (
|
|
18
19
|
production_graph,
|
|
20
|
+
production_to_class,
|
|
19
21
|
production_to_dict,
|
|
20
22
|
production_to_python,
|
|
21
23
|
production_to_xml,
|
|
@@ -30,8 +32,10 @@ from .types import (
|
|
|
30
32
|
_edge_identity,
|
|
31
33
|
)
|
|
32
34
|
|
|
35
|
+
_MISSING = object()
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
|
|
38
|
+
class Production(_DeclarativeProductionMixin):
|
|
35
39
|
"""Python authoring DSL for IRIS interoperability production topology.
|
|
36
40
|
|
|
37
41
|
Python Production is the source of truth for Python-authored topology.
|
|
@@ -41,42 +45,137 @@ class Production:
|
|
|
41
45
|
|
|
42
46
|
def __init__(
|
|
43
47
|
self,
|
|
44
|
-
name: str,
|
|
48
|
+
name: str | object = _MISSING,
|
|
45
49
|
*,
|
|
46
|
-
testing_enabled: bool | str =
|
|
47
|
-
log_general_trace_events: bool | str =
|
|
48
|
-
actor_pool_size: int | str =
|
|
49
|
-
description: str =
|
|
50
|
-
shutdown_timeout: int | str =
|
|
51
|
-
update_timeout: int | str =
|
|
52
|
-
alert_notification_manager: str =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
alert_notification_recipients: str = PRODUCTION_SETTING_FIELDS[
|
|
59
|
-
"alert_notification_recipients"
|
|
60
|
-
][1],
|
|
61
|
-
alert_action_window: int | str = PRODUCTION_SETTING_FIELDS[
|
|
62
|
-
"alert_action_window"
|
|
63
|
-
][1],
|
|
64
|
-
namespace: str | None = None,
|
|
65
|
-
director: _DirectorProtocol | None = None,
|
|
50
|
+
testing_enabled: bool | str | object = _MISSING,
|
|
51
|
+
log_general_trace_events: bool | str | object = _MISSING,
|
|
52
|
+
actor_pool_size: int | str | object = _MISSING,
|
|
53
|
+
description: str | object = _MISSING,
|
|
54
|
+
shutdown_timeout: int | str | object = _MISSING,
|
|
55
|
+
update_timeout: int | str | object = _MISSING,
|
|
56
|
+
alert_notification_manager: str | object = _MISSING,
|
|
57
|
+
alert_notification_operation: str | object = _MISSING,
|
|
58
|
+
alert_notification_recipients: str | object = _MISSING,
|
|
59
|
+
alert_action_window: int | str | object = _MISSING,
|
|
60
|
+
namespace: str | None | object = _MISSING,
|
|
61
|
+
director: _DirectorProtocol | None | object = _MISSING,
|
|
66
62
|
):
|
|
67
|
-
self.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
63
|
+
self._initialize(
|
|
64
|
+
name,
|
|
65
|
+
testing_enabled=testing_enabled,
|
|
66
|
+
log_general_trace_events=log_general_trace_events,
|
|
67
|
+
actor_pool_size=actor_pool_size,
|
|
68
|
+
description=description,
|
|
69
|
+
shutdown_timeout=shutdown_timeout,
|
|
70
|
+
update_timeout=update_timeout,
|
|
71
|
+
alert_notification_manager=alert_notification_manager,
|
|
72
|
+
alert_notification_operation=alert_notification_operation,
|
|
73
|
+
alert_notification_recipients=alert_notification_recipients,
|
|
74
|
+
alert_action_window=alert_action_window,
|
|
75
|
+
namespace=namespace,
|
|
76
|
+
director=director,
|
|
77
|
+
hydrate_declarations=True,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def _new_unhydrated(
|
|
82
|
+
cls,
|
|
83
|
+
name: str | object = _MISSING,
|
|
84
|
+
*,
|
|
85
|
+
testing_enabled: bool | str | object = _MISSING,
|
|
86
|
+
log_general_trace_events: bool | str | object = _MISSING,
|
|
87
|
+
actor_pool_size: int | str | object = _MISSING,
|
|
88
|
+
description: str | object = _MISSING,
|
|
89
|
+
shutdown_timeout: int | str | object = _MISSING,
|
|
90
|
+
update_timeout: int | str | object = _MISSING,
|
|
91
|
+
alert_notification_manager: str | object = _MISSING,
|
|
92
|
+
alert_notification_operation: str | object = _MISSING,
|
|
93
|
+
alert_notification_recipients: str | object = _MISSING,
|
|
94
|
+
alert_action_window: int | str | object = _MISSING,
|
|
95
|
+
namespace: str | None | object = _MISSING,
|
|
96
|
+
director: _DirectorProtocol | None | object = _MISSING,
|
|
97
|
+
) -> Production:
|
|
98
|
+
production = object.__new__(cls)
|
|
99
|
+
Production._initialize(
|
|
100
|
+
production,
|
|
101
|
+
name,
|
|
102
|
+
testing_enabled=testing_enabled,
|
|
103
|
+
log_general_trace_events=log_general_trace_events,
|
|
104
|
+
actor_pool_size=actor_pool_size,
|
|
105
|
+
description=description,
|
|
106
|
+
shutdown_timeout=shutdown_timeout,
|
|
107
|
+
update_timeout=update_timeout,
|
|
108
|
+
alert_notification_manager=alert_notification_manager,
|
|
109
|
+
alert_notification_operation=alert_notification_operation,
|
|
110
|
+
alert_notification_recipients=alert_notification_recipients,
|
|
111
|
+
alert_action_window=alert_action_window,
|
|
112
|
+
namespace=namespace,
|
|
113
|
+
director=director,
|
|
114
|
+
hydrate_declarations=False,
|
|
115
|
+
)
|
|
116
|
+
return production
|
|
117
|
+
|
|
118
|
+
def _initialize(
|
|
119
|
+
self,
|
|
120
|
+
name: str | object = _MISSING,
|
|
121
|
+
*,
|
|
122
|
+
testing_enabled: bool | str | object = _MISSING,
|
|
123
|
+
log_general_trace_events: bool | str | object = _MISSING,
|
|
124
|
+
actor_pool_size: int | str | object = _MISSING,
|
|
125
|
+
description: str | object = _MISSING,
|
|
126
|
+
shutdown_timeout: int | str | object = _MISSING,
|
|
127
|
+
update_timeout: int | str | object = _MISSING,
|
|
128
|
+
alert_notification_manager: str | object = _MISSING,
|
|
129
|
+
alert_notification_operation: str | object = _MISSING,
|
|
130
|
+
alert_notification_recipients: str | object = _MISSING,
|
|
131
|
+
alert_action_window: int | str | object = _MISSING,
|
|
132
|
+
namespace: str | None | object = _MISSING,
|
|
133
|
+
director: _DirectorProtocol | None | object = _MISSING,
|
|
134
|
+
hydrate_declarations: bool,
|
|
135
|
+
) -> None:
|
|
136
|
+
self.name = self._resolve_production_name(name)
|
|
137
|
+
self.testing_enabled = self._production_default(
|
|
138
|
+
"testing_enabled", testing_enabled, False
|
|
139
|
+
)
|
|
140
|
+
self.log_general_trace_events = self._production_default(
|
|
141
|
+
"log_general_trace_events", log_general_trace_events, False
|
|
142
|
+
)
|
|
143
|
+
self.actor_pool_size = self._production_default(
|
|
144
|
+
"actor_pool_size", actor_pool_size, 2
|
|
145
|
+
)
|
|
146
|
+
self.description = self._production_default("description", description, "")
|
|
147
|
+
self.shutdown_timeout = self._production_default(
|
|
148
|
+
"shutdown_timeout",
|
|
149
|
+
shutdown_timeout,
|
|
150
|
+
PRODUCTION_SETTING_FIELDS["shutdown_timeout"][1],
|
|
151
|
+
)
|
|
152
|
+
self.update_timeout = self._production_default(
|
|
153
|
+
"update_timeout",
|
|
154
|
+
update_timeout,
|
|
155
|
+
PRODUCTION_SETTING_FIELDS["update_timeout"][1],
|
|
156
|
+
)
|
|
157
|
+
self.alert_notification_manager = self._production_default(
|
|
158
|
+
"alert_notification_manager",
|
|
159
|
+
alert_notification_manager,
|
|
160
|
+
PRODUCTION_SETTING_FIELDS["alert_notification_manager"][1],
|
|
161
|
+
)
|
|
162
|
+
self.alert_notification_operation = self._production_default(
|
|
163
|
+
"alert_notification_operation",
|
|
164
|
+
alert_notification_operation,
|
|
165
|
+
PRODUCTION_SETTING_FIELDS["alert_notification_operation"][1],
|
|
166
|
+
)
|
|
167
|
+
self.alert_notification_recipients = self._production_default(
|
|
168
|
+
"alert_notification_recipients",
|
|
169
|
+
alert_notification_recipients,
|
|
170
|
+
PRODUCTION_SETTING_FIELDS["alert_notification_recipients"][1],
|
|
171
|
+
)
|
|
172
|
+
self.alert_action_window = self._production_default(
|
|
173
|
+
"alert_action_window",
|
|
174
|
+
alert_action_window,
|
|
175
|
+
PRODUCTION_SETTING_FIELDS["alert_action_window"][1],
|
|
176
|
+
)
|
|
177
|
+
self.namespace = self._production_default("namespace", namespace, None)
|
|
178
|
+
self._director = self._production_default("director", director, None)
|
|
80
179
|
self._items: list[ComponentRef] = []
|
|
81
180
|
self._items_by_name: dict[str, ComponentRef] = {}
|
|
82
181
|
self._connections: dict[tuple[str, str], list[str]] = {}
|
|
@@ -84,6 +183,29 @@ class Production:
|
|
|
84
183
|
self._graph_warnings: list[str] = []
|
|
85
184
|
self._queue_info: dict[str, dict[str, Any]] = {}
|
|
86
185
|
self._messages: list[PersistentMessageRegistration] = []
|
|
186
|
+
if hydrate_declarations:
|
|
187
|
+
self._hydrate_declared_items()
|
|
188
|
+
|
|
189
|
+
def _resolve_production_name(self, name: str | object) -> str:
|
|
190
|
+
if name is not _MISSING:
|
|
191
|
+
return name # type: ignore[return-value]
|
|
192
|
+
class_name = getattr(type(self), "name", _MISSING)
|
|
193
|
+
if class_name is not _MISSING:
|
|
194
|
+
return class_name
|
|
195
|
+
return f"{type(self).__module__}.{type(self).__name__}"
|
|
196
|
+
|
|
197
|
+
def _production_default(
|
|
198
|
+
self,
|
|
199
|
+
field_name: str,
|
|
200
|
+
explicit: Any,
|
|
201
|
+
default: Any,
|
|
202
|
+
) -> Any:
|
|
203
|
+
if explicit is not _MISSING:
|
|
204
|
+
return explicit
|
|
205
|
+
class_value = getattr(type(self), field_name, _MISSING)
|
|
206
|
+
if class_value is not _MISSING:
|
|
207
|
+
return class_value
|
|
208
|
+
return default
|
|
87
209
|
|
|
88
210
|
def testing(self, enabled: bool | str = True) -> Production:
|
|
89
211
|
self.testing_enabled = enabled
|
|
@@ -147,7 +269,11 @@ class Production:
|
|
|
147
269
|
namespace: str | None = None,
|
|
148
270
|
director: _DirectorProtocol | None = None,
|
|
149
271
|
) -> Production:
|
|
150
|
-
seed = cls(
|
|
272
|
+
seed = cls._new_unhydrated(
|
|
273
|
+
name,
|
|
274
|
+
namespace=namespace,
|
|
275
|
+
director=director,
|
|
276
|
+
)
|
|
151
277
|
runtime_director = _ProductionRuntime(seed).director
|
|
152
278
|
exported = runtime_director.export_production(name)
|
|
153
279
|
connections = None
|
|
@@ -553,6 +679,9 @@ class Production:
|
|
|
553
679
|
def to_python(self) -> str:
|
|
554
680
|
return production_to_python(self)
|
|
555
681
|
|
|
682
|
+
def to_class(self) -> str:
|
|
683
|
+
return production_to_class(self)
|
|
684
|
+
|
|
556
685
|
def graph(self) -> ProductionGraph:
|
|
557
686
|
return production_graph(self)
|
|
558
687
|
|
iop/production/reconstruction.py
CHANGED
|
@@ -28,7 +28,7 @@ def production_from_dict(
|
|
|
28
28
|
):
|
|
29
29
|
production_name, production_data = _production_payload(data)
|
|
30
30
|
production_settings = _split_production_settings(production_data.get("Setting"))
|
|
31
|
-
production = production_cls(
|
|
31
|
+
production = production_cls._new_unhydrated(
|
|
32
32
|
production_name,
|
|
33
33
|
testing_enabled=production_data.get("@TestingEnabled", False),
|
|
34
34
|
log_general_trace_events=production_data.get("@LogGeneralTraceEvents", False),
|
|
@@ -117,9 +117,7 @@ def _apply_runtime_item_metadata(production, connections: Any) -> None:
|
|
|
117
117
|
def _apply_runtime_connections(production, connections: Any) -> tuple[set[str], set[str]]:
|
|
118
118
|
connection_map, runtime_sources, warnings = _normalize_connections(connections)
|
|
119
119
|
production._graph_warnings.extend(warnings)
|
|
120
|
-
runtime_sources_with_targets =
|
|
121
|
-
source_item for source_item, targets in connection_map.items() if targets
|
|
122
|
-
}
|
|
120
|
+
runtime_sources_with_targets: set[str] = set()
|
|
123
121
|
|
|
124
122
|
for source_item, targets in connection_map.items():
|
|
125
123
|
if source_item not in production._items_by_name:
|
|
@@ -144,6 +142,7 @@ def _apply_runtime_connections(production, connections: Any) -> tuple[set[str],
|
|
|
144
142
|
f"Runtime connection target does not exist: "
|
|
145
143
|
f"{source_item} -> {target_name}"
|
|
146
144
|
)
|
|
145
|
+
runtime_sources_with_targets.add(source_item)
|
|
147
146
|
production._register_connection(
|
|
148
147
|
source_item,
|
|
149
148
|
source_port,
|
iop/production/rendering.py
CHANGED
|
@@ -86,8 +86,84 @@ def production_to_python(production) -> str:
|
|
|
86
86
|
return "\n".join(lines) + "\n"
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
def
|
|
90
|
-
|
|
89
|
+
def production_to_class(production) -> str:
|
|
90
|
+
item_names = {item.name for item in production.items}
|
|
91
|
+
route_targets = _valid_route_targets(
|
|
92
|
+
_route_targets(production, item_names),
|
|
93
|
+
item_names,
|
|
94
|
+
)
|
|
95
|
+
item_groups = _class_item_groups(production.items)
|
|
96
|
+
used_items = {
|
|
97
|
+
_class_item_type(kind)
|
|
98
|
+
for kind, items in item_groups.items()
|
|
99
|
+
if items
|
|
100
|
+
}
|
|
101
|
+
has_routes = bool(route_targets)
|
|
102
|
+
imports = ["Production", *sorted(used_items)]
|
|
103
|
+
if has_routes:
|
|
104
|
+
imports.append("Route")
|
|
105
|
+
|
|
106
|
+
class_name = _production_class_name(production.name)
|
|
107
|
+
lines = [
|
|
108
|
+
"# Generated from IRIS production export.",
|
|
109
|
+
"# Review before using as source of truth; some runtime/dynamic routing intent",
|
|
110
|
+
"# cannot be fully reconstructed from deployed IRIS metadata.",
|
|
111
|
+
]
|
|
112
|
+
if _has_string_python_proxy_items(production.items):
|
|
113
|
+
lines.extend(
|
|
114
|
+
[
|
|
115
|
+
"# TODO: replace Python.* string class names with imported Python",
|
|
116
|
+
"# classes before re-migration, or ensure the proxy classes already exist.",
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
lines.extend(
|
|
120
|
+
[
|
|
121
|
+
f"from iop import {', '.join(imports)}",
|
|
122
|
+
"",
|
|
123
|
+
"",
|
|
124
|
+
f"class {class_name}(Production):",
|
|
125
|
+
f" name = {_literal(production.name)}",
|
|
126
|
+
]
|
|
127
|
+
)
|
|
128
|
+
lines.extend(_class_production_setting_lines(production))
|
|
129
|
+
lines.append("")
|
|
130
|
+
|
|
131
|
+
for kind in ("component", "service", "process", "operation"):
|
|
132
|
+
items = item_groups.get(kind, [])
|
|
133
|
+
if not items:
|
|
134
|
+
continue
|
|
135
|
+
attr_name = _class_item_attr(kind)
|
|
136
|
+
lines.append(f" {attr_name} = (")
|
|
137
|
+
for item in items:
|
|
138
|
+
lines.extend(_class_item_lines(item, kind, route_targets))
|
|
139
|
+
lines.append(" )")
|
|
140
|
+
lines.append("")
|
|
141
|
+
|
|
142
|
+
unresolved = _unresolved_class_routes(production, item_names)
|
|
143
|
+
if unresolved:
|
|
144
|
+
lines.append(" # TODO: review unresolved or runtime-only routes:")
|
|
145
|
+
lines.extend(f" # - {value}" for value in unresolved)
|
|
146
|
+
lines.append("")
|
|
147
|
+
if production.graph().warnings:
|
|
148
|
+
lines.append(" # Import warnings:")
|
|
149
|
+
lines.extend(f" # - {warning}" for warning in production.graph().warnings)
|
|
150
|
+
lines.append("")
|
|
151
|
+
|
|
152
|
+
lines.append("")
|
|
153
|
+
lines.append(f"PRODUCTIONS = [{class_name}()]")
|
|
154
|
+
return "\n".join(lines) + "\n"
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _class_production_setting_lines(production) -> list[str]:
|
|
158
|
+
return [
|
|
159
|
+
f" {name} = {_literal(value)}"
|
|
160
|
+
for name, value, default in _production_setting_literals(production)
|
|
161
|
+
if value != default
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _production_setting_literals(production) -> list[tuple[str, Any, Any]]:
|
|
166
|
+
return [
|
|
91
167
|
("testing_enabled", _bool_literal(production.testing_enabled), False),
|
|
92
168
|
(
|
|
93
169
|
"log_general_trace_events",
|
|
@@ -98,26 +174,228 @@ def _production_constructor_lines(production) -> list[str]:
|
|
|
98
174
|
("description", production.description, ""),
|
|
99
175
|
("shutdown_timeout", _int_literal(production.shutdown_timeout), 120),
|
|
100
176
|
("update_timeout", _int_literal(production.update_timeout), 10),
|
|
101
|
-
(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"",
|
|
105
|
-
),
|
|
106
|
-
(
|
|
107
|
-
"alert_notification_operation",
|
|
108
|
-
production.alert_notification_operation,
|
|
109
|
-
"",
|
|
110
|
-
),
|
|
111
|
-
(
|
|
112
|
-
"alert_notification_recipients",
|
|
113
|
-
production.alert_notification_recipients,
|
|
114
|
-
"",
|
|
115
|
-
),
|
|
177
|
+
("alert_notification_manager", production.alert_notification_manager, ""),
|
|
178
|
+
("alert_notification_operation", production.alert_notification_operation, ""),
|
|
179
|
+
("alert_notification_recipients", production.alert_notification_recipients, ""),
|
|
116
180
|
("alert_action_window", _int_literal(production.alert_action_window), 60),
|
|
117
181
|
]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _class_item_groups(items) -> dict[str, list[Any]]:
|
|
185
|
+
groups: dict[str, list[Any]] = {
|
|
186
|
+
"component": [],
|
|
187
|
+
"service": [],
|
|
188
|
+
"process": [],
|
|
189
|
+
"operation": [],
|
|
190
|
+
}
|
|
191
|
+
for item in items:
|
|
192
|
+
groups[_class_item_kind(item)].append(item)
|
|
193
|
+
return groups
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _class_item_kind(item) -> str:
|
|
197
|
+
if item.kind in {"service", "process", "operation"}:
|
|
198
|
+
return item.kind
|
|
199
|
+
return "component"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _class_item_type(kind: str) -> str:
|
|
203
|
+
return {
|
|
204
|
+
"component": "ComponentItem",
|
|
205
|
+
"service": "ServiceItem",
|
|
206
|
+
"process": "ProcessItem",
|
|
207
|
+
"operation": "OperationItem",
|
|
208
|
+
}[kind]
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _class_item_attr(kind: str) -> str:
|
|
212
|
+
return {
|
|
213
|
+
"component": "components",
|
|
214
|
+
"service": "services",
|
|
215
|
+
"process": "processes",
|
|
216
|
+
"operation": "operations",
|
|
217
|
+
}[kind]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _class_item_lines(item, kind: str, route_targets) -> list[str]:
|
|
221
|
+
kwargs: list[tuple[str, Any]] = []
|
|
222
|
+
optional_fields = [
|
|
223
|
+
("category", item.category, ""),
|
|
224
|
+
("pool_size", _int_literal(item.pool_size), 1),
|
|
225
|
+
("enabled", _bool_literal(item.enabled), True),
|
|
226
|
+
("foreground", _bool_literal(item.foreground), False),
|
|
227
|
+
("comment", item.comment, ""),
|
|
228
|
+
("log_trace_events", _bool_literal(item.log_trace_events), False),
|
|
229
|
+
("schedule", item.schedule, ""),
|
|
230
|
+
]
|
|
231
|
+
kwargs.extend(
|
|
232
|
+
(name, value)
|
|
233
|
+
for name, value, default in optional_fields
|
|
234
|
+
if value != default
|
|
235
|
+
)
|
|
236
|
+
settings = {
|
|
237
|
+
name: value
|
|
238
|
+
for name, value in item.host_settings.items()
|
|
239
|
+
if (item.name, name) not in route_targets
|
|
240
|
+
}
|
|
241
|
+
if settings:
|
|
242
|
+
kwargs.append(("settings", settings))
|
|
243
|
+
if item.adapter_settings:
|
|
244
|
+
kwargs.append(("adapter_settings", dict(item.adapter_settings)))
|
|
245
|
+
if item.other_settings:
|
|
246
|
+
kwargs.append(("other_settings", [dict(value) for value in item.other_settings]))
|
|
247
|
+
|
|
248
|
+
routes = _class_item_route_values(item, route_targets)
|
|
249
|
+
if routes:
|
|
250
|
+
kwargs.append(("routes", routes))
|
|
251
|
+
|
|
252
|
+
lines = []
|
|
253
|
+
if _is_string_python_proxy_item(item):
|
|
254
|
+
lines.extend(
|
|
255
|
+
[
|
|
256
|
+
" # TODO: replace this proxy class name with the Python",
|
|
257
|
+
" # class object if this item should be auto-registered.",
|
|
258
|
+
]
|
|
259
|
+
)
|
|
260
|
+
lines.extend(
|
|
261
|
+
[
|
|
262
|
+
f" {_class_item_type(kind)}(",
|
|
263
|
+
f" {_literal(item.name)},",
|
|
264
|
+
f" {_literal(item.class_name or '')},",
|
|
265
|
+
]
|
|
266
|
+
)
|
|
267
|
+
for name, value in kwargs:
|
|
268
|
+
lines.extend(_class_keyword_lines(name, value))
|
|
269
|
+
lines.append(" ),")
|
|
270
|
+
return lines
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _class_item_route_values(item, route_targets) -> list[tuple[str, list[str]]]:
|
|
274
|
+
values: list[tuple[str, list[str]]] = []
|
|
275
|
+
for source_item, source_port in sorted(route_targets):
|
|
276
|
+
if source_item != item.name:
|
|
277
|
+
continue
|
|
278
|
+
targets = route_targets[(source_item, source_port)]
|
|
279
|
+
values.append((source_port, list(targets)))
|
|
280
|
+
return values
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _valid_route_targets(
|
|
284
|
+
route_targets: dict[tuple[str, str], list[str]],
|
|
285
|
+
item_names: set[str],
|
|
286
|
+
) -> dict[tuple[str, str], list[str]]:
|
|
287
|
+
valid_routes: dict[tuple[str, str], list[str]] = {}
|
|
288
|
+
for source, targets in route_targets.items():
|
|
289
|
+
valid_targets = [target for target in targets if target in item_names]
|
|
290
|
+
if valid_targets:
|
|
291
|
+
valid_routes[source] = valid_targets
|
|
292
|
+
return valid_routes
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _class_keyword_lines(name: str, value: Any) -> list[str]:
|
|
296
|
+
if isinstance(value, dict):
|
|
297
|
+
return _indented_dict_keyword_lines(name, value, indent=12)
|
|
298
|
+
if isinstance(value, list) and name == "routes":
|
|
299
|
+
return _class_route_keyword_lines(value)
|
|
300
|
+
if isinstance(value, list):
|
|
301
|
+
return _indented_list_keyword_lines(name, value, indent=12)
|
|
302
|
+
return [f" {name}={_literal(value)},"]
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _class_route_keyword_lines(routes: list[tuple[str, list[str]]]) -> list[str]:
|
|
306
|
+
if len(routes) == 1:
|
|
307
|
+
port, targets = routes[0]
|
|
308
|
+
target_literal = _class_route_targets_literal(targets)
|
|
309
|
+
return [f" routes=(Route({_literal(port)}, {target_literal}),),"]
|
|
310
|
+
|
|
311
|
+
lines = [" routes=("]
|
|
312
|
+
for port, targets in routes:
|
|
313
|
+
target_literal = _class_route_targets_literal(targets)
|
|
314
|
+
lines.append(f" Route({_literal(port)}, {target_literal}),")
|
|
315
|
+
lines.append(" ),")
|
|
316
|
+
return lines
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _class_route_targets_literal(targets: list[str]) -> str:
|
|
320
|
+
return _literal(targets[0]) if len(targets) == 1 else _literal(tuple(targets))
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _has_string_python_proxy_items(items) -> bool:
|
|
324
|
+
return any(_is_string_python_proxy_item(item) for item in items)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _is_string_python_proxy_item(item) -> bool:
|
|
328
|
+
return str(item.class_name or "").startswith("Python.")
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _indented_dict_keyword_lines(
|
|
332
|
+
name: str,
|
|
333
|
+
value: dict[str, Any],
|
|
334
|
+
*,
|
|
335
|
+
indent: int,
|
|
336
|
+
) -> list[str]:
|
|
337
|
+
prefix = " " * indent
|
|
338
|
+
item_prefix = " " * (indent + 4)
|
|
339
|
+
lines = [f"{prefix}{name}={{"]
|
|
340
|
+
for key, item in value.items():
|
|
341
|
+
lines.append(f"{item_prefix}{_literal(key)}: {_literal(item)},")
|
|
342
|
+
lines.append(f"{prefix}}},")
|
|
343
|
+
return lines
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _indented_list_keyword_lines(
|
|
347
|
+
name: str,
|
|
348
|
+
value: list[Any],
|
|
349
|
+
*,
|
|
350
|
+
indent: int,
|
|
351
|
+
) -> list[str]:
|
|
352
|
+
prefix = " " * indent
|
|
353
|
+
item_prefix = " " * (indent + 4)
|
|
354
|
+
lines = [f"{prefix}{name}=["]
|
|
355
|
+
for item in value:
|
|
356
|
+
lines.append(f"{item_prefix}{_literal(item)},")
|
|
357
|
+
lines.append(f"{prefix}],")
|
|
358
|
+
return lines
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _unresolved_class_routes(production, item_names: set[str]) -> list[str]:
|
|
362
|
+
unresolved: list[str] = []
|
|
363
|
+
for edge in production.edges:
|
|
364
|
+
if not edge.source_port:
|
|
365
|
+
unresolved.append(f"{edge.source_item} -> {edge.target}")
|
|
366
|
+
elif edge.target not in item_names:
|
|
367
|
+
unresolved.append(f"{edge.source_item}.{edge.source_port} -> {edge.target}")
|
|
368
|
+
return sorted(set(unresolved))
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _production_class_name(production_name: str) -> str:
|
|
372
|
+
parts = [
|
|
373
|
+
part
|
|
374
|
+
for part in re.split(r"\W+", production_name)
|
|
375
|
+
if part
|
|
376
|
+
]
|
|
377
|
+
if not parts:
|
|
378
|
+
return "GeneratedProduction"
|
|
379
|
+
candidate = parts[-1]
|
|
380
|
+
if candidate == "Production" and len(parts) > 1:
|
|
381
|
+
candidate = "".join(parts[-2:])
|
|
382
|
+
else:
|
|
383
|
+
candidate = "".join(
|
|
384
|
+
part[:1].upper() + part[1:]
|
|
385
|
+
for part in re.split(r"[_\s]+", candidate)
|
|
386
|
+
if part
|
|
387
|
+
)
|
|
388
|
+
if not candidate or not candidate[0].isalpha():
|
|
389
|
+
candidate = f"Generated{candidate}"
|
|
390
|
+
if keyword.iskeyword(candidate) or candidate == "Production":
|
|
391
|
+
candidate = f"{candidate}Definition"
|
|
392
|
+
return candidate
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _production_constructor_lines(production) -> list[str]:
|
|
118
396
|
rendered = [
|
|
119
397
|
(name, value)
|
|
120
|
-
for name, value, default in
|
|
398
|
+
for name, value, default in _production_setting_literals(production)
|
|
121
399
|
if value != default
|
|
122
400
|
]
|
|
123
401
|
if not rendered:
|
iop/production/types.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, overload
|
|
6
6
|
|
|
7
7
|
from ..components.settings import Category, Setting, controls
|
|
8
8
|
|
|
@@ -17,6 +17,15 @@ class TargetSetting(Setting):
|
|
|
17
17
|
super().__init__("", **kwargs)
|
|
18
18
|
self.logical_name = logical_name
|
|
19
19
|
|
|
20
|
+
@overload
|
|
21
|
+
def __get__(self, instance: None, owner: type | None = None) -> TargetSetting: ...
|
|
22
|
+
|
|
23
|
+
@overload
|
|
24
|
+
def __get__(self, instance: object, owner: type | None = None) -> str: ...
|
|
25
|
+
|
|
26
|
+
def __get__(self, instance, owner=None):
|
|
27
|
+
return super().__get__(instance, owner)
|
|
28
|
+
|
|
20
29
|
|
|
21
30
|
def target(logical_name: str = "", **kwargs: Any) -> TargetSetting:
|
|
22
31
|
"""Declare an outbound target port on a component class."""
|
iop/production/validation.py
CHANGED
|
@@ -4,7 +4,11 @@ import warnings
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from .common import
|
|
7
|
+
from .common import (
|
|
8
|
+
PRODUCTION_SETTING_FIELDS_BY_IRIS,
|
|
9
|
+
PRODUCTION_SETTING_NAMES,
|
|
10
|
+
SETTING_NAME_ALIASES,
|
|
11
|
+
)
|
|
8
12
|
from .import_ import _as_list, _production_payload, _split_settings
|
|
9
13
|
|
|
10
14
|
_PRODUCTION_PUBLIC_FIELDS = {
|
|
@@ -32,11 +36,6 @@ _PRODUCTION_DICT_KEYS = {
|
|
|
32
36
|
"Item",
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
_SETTING_NAME_ALIASES = {
|
|
36
|
-
"target_config_name": "TargetConfigName",
|
|
37
|
-
"target_config_names": "TargetConfigNames",
|
|
38
|
-
}
|
|
39
|
-
|
|
40
39
|
_PRODUCTION_SETTING_NAME_ALIASES = {
|
|
41
40
|
**PRODUCTION_SETTING_NAMES,
|
|
42
41
|
**{iris_name: iris_name for iris_name in PRODUCTION_SETTING_FIELDS_BY_IRIS},
|
|
@@ -350,7 +349,7 @@ def _validate_names_with_known_set(
|
|
|
350
349
|
for setting_name in sorted(settings):
|
|
351
350
|
if setting_name.startswith("%") or setting_name in known_names:
|
|
352
351
|
continue
|
|
353
|
-
alias =
|
|
352
|
+
alias = SETTING_NAME_ALIASES.get(setting_name)
|
|
354
353
|
if alias in known_names:
|
|
355
354
|
issues.append(_setting_alias_issue(path_prefix, setting_name, alias))
|
|
356
355
|
continue
|
|
@@ -372,7 +371,7 @@ def _validate_names_with_iris_dictionary(
|
|
|
372
371
|
for setting_name in sorted(settings):
|
|
373
372
|
if _iris_property_exists(iris, iris_class_name, setting_name):
|
|
374
373
|
continue
|
|
375
|
-
alias =
|
|
374
|
+
alias = SETTING_NAME_ALIASES.get(setting_name)
|
|
376
375
|
if alias and _iris_property_exists(iris, iris_class_name, alias):
|
|
377
376
|
issues.append(_setting_alias_issue(path_prefix, setting_name, alias))
|
|
378
377
|
continue
|
{iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/RECORD
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
iop/__init__.py,sha256=
|
|
1
|
+
iop/__init__.py,sha256=XgOE11EBCWbk_lBPUv7oTnrTfg2XcW_9Eygr6lMltTU,4439
|
|
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
|
|
5
|
-
iop/cli/main.py,sha256=
|
|
6
|
-
iop/cli/parser.py,sha256=
|
|
5
|
+
iop/cli/main.py,sha256=sKiZWZVqxQKdSTK7lHRMDN89A8wrTFSLgN2woXnQCHM,13302
|
|
6
|
+
iop/cli/parser.py,sha256=4Es5xJPR3HVcEgFKn6sONEJQ7bSwZ8yluV1WuofoLf8,4887
|
|
7
7
|
iop/cli/types.py,sha256=7GxUy2KHw_LNOWAQ2-EalKOikIDQFiKjMmEEmlWeOB8,1343
|
|
8
8
|
iop/cls/IOP/BusinessOperation.cls,sha256=NlvbNtm1ZFZmHaMX_9FMKoptY-hQMq5jYN1nLQwvYJw,936
|
|
9
9
|
iop/cls/IOP/BusinessProcess.cls,sha256=XJxzbiV0xokzRm-iI2Be5UIJLE3MlXr7W3WS_LkOCYs,3363
|
|
@@ -16,7 +16,7 @@ iop/cls/IOP/OutboundAdapter.cls,sha256=OQoGFHUy2qV_kcsShTlWGOngDrdH5dhwux4eopZyI
|
|
|
16
16
|
iop/cls/IOP/PickleMessage.cls,sha256=S3y7AClQ8mAILjxPuHdCjGosBZYzGbUQ5WTv4mYPNMQ,1673
|
|
17
17
|
iop/cls/IOP/Projection.cls,sha256=AZgbfpbEk02llhyIwrSw0M3QMcQNcjhjY3_vU_yx8FU,1315
|
|
18
18
|
iop/cls/IOP/Test.cls,sha256=zvlCZJfOCmSFdmi-ZQHWDGYmqZAgRspaJj1j0r_IxJQ,2017
|
|
19
|
-
iop/cls/IOP/Utils.cls,sha256=
|
|
19
|
+
iop/cls/IOP/Utils.cls,sha256=fhKWWouv4ND1-3JHojBQbOH9ufjaiS8M-4n6oM4jCxk,31496
|
|
20
20
|
iop/cls/IOP/Wrapper.cls,sha256=ydlNpqAekoH_XdGk0ZpspX1pxgv__E00Xf57F8oxsqs,1624
|
|
21
21
|
iop/cls/IOP/Duplex/Operation.cls,sha256=K_fmgeLjPZQbHgNrc0kd6DUQoW0fDn1VHQjJxHo95Zk,525
|
|
22
22
|
iop/cls/IOP/Duplex/Process.cls,sha256=xbefZ4z84a_IUhavWN6P_gZBzqkdJ5XRTXxro6iDvAg,6986
|
|
@@ -49,7 +49,7 @@ iop/components/outbound_adapter.py,sha256=Bm6KeaLR0fO3z5o9cmJxcMdIiOvdExik-Oc272
|
|
|
49
49
|
iop/components/polling_business_service.py,sha256=5rVr6DVgfK3xwEfjFHa4dsWb6wI3JXJOQQYtHLAIjSY,901
|
|
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
|
-
iop/components/settings.py,sha256=
|
|
52
|
+
iop/components/settings.py,sha256=Y-MkpGaMOS-AcvuxVp46hLosQQIn4vMhyugoSKD4ikI,5798
|
|
53
53
|
iop/messages/__init__.py,sha256=k1AKplpjdqSxM5Gl7bpWAfw93xUgigovlI4SKhwKHRI,355
|
|
54
54
|
iop/messages/base.py,sha256=2DBU8CzKLXVYUT3Vp6QeL6QHJbbfOGfoE4Kmkrq8Hpg,1183
|
|
55
55
|
iop/messages/decorators.py,sha256=jOjMllMHaDzS3Apo1h68LIn87boGfDnsQ1OhyyuReA0,2363
|
|
@@ -60,20 +60,22 @@ iop/messages/validation.py,sha256=cbq2VaaWTcqesmV-DDwReHh8peynvEMw9h0JNf4q9kQ,15
|
|
|
60
60
|
iop/migration/__init__.py,sha256=wp-RvjLKYW_Pi2AJLfiWe1bACxENzEgjXtP9UMNiSVY,36
|
|
61
61
|
iop/migration/io.py,sha256=hAehtApFZFgGzjzgwBmDMxdA1xoRhE-okOcEzvgFdQ4,1822
|
|
62
62
|
iop/migration/plans.py,sha256=oj0sNLMBtWZBCoVEoP82RagIFumfTPqmGylDUxGLJNE,6738
|
|
63
|
-
iop/migration/utils.py,sha256=
|
|
64
|
-
iop/production/__init__.py,sha256=
|
|
63
|
+
iop/migration/utils.py,sha256=tSnfQLsmPkuW4HC-fN5CYkYVjrurSAgSeGjiIQohqZQ,41229
|
|
64
|
+
iop/production/__init__.py,sha256=9-Eot8R2ioCSKbZlQDPCXJ8Xdp73KtLygGBtTljNLaM,1714
|
|
65
65
|
iop/production/actions.py,sha256=h95K9O6N8S2thV0dYEKOWxo5uPhfvgALZfB6G2jrUt8,7992
|
|
66
|
-
iop/production/common.py,sha256=
|
|
66
|
+
iop/production/common.py,sha256=IoUg9jkFzSQwDsHcXvkVAC4qxpWKs3kTfXFm_EP07EU,3091
|
|
67
67
|
iop/production/component.py,sha256=uRCQZwlPNFbAEaWgDtbDHUdf4VD8A0R1sJhLTj8kHcA,7988
|
|
68
|
+
iop/production/declarations.py,sha256=6v7015-VjU7F0KD4qhciUv616PmYfN20ZNhTuzfV7xk,5842
|
|
69
|
+
iop/production/declarative.py,sha256=rUqeQiunZFGPSe2uz-jlpNke1Cy_teyRWxkmc7I_6OU,6196
|
|
68
70
|
iop/production/diff.py,sha256=iVJeixEZzy-TaK7b7wdR5wzO5dyIGr2KPVsN0pf2dp4,9968
|
|
69
71
|
iop/production/import_.py,sha256=hK5d6CKtd-_FOmuU39wiZ2cmn-SCUoZ-r3hRz8HgwgA,9865
|
|
70
72
|
iop/production/inspection.py,sha256=LsAI4vhF27v8OMXtRAOHQn8XeaT4CIhjBu-CA-aorO0,3343
|
|
71
|
-
iop/production/model.py,sha256
|
|
72
|
-
iop/production/reconstruction.py,sha256=
|
|
73
|
-
iop/production/rendering.py,sha256=
|
|
73
|
+
iop/production/model.py,sha256=-TuT0cLNUB0EQaJEp0afWw373WLLiNe87SeQaTflL7s,36421
|
|
74
|
+
iop/production/reconstruction.py,sha256=aIQOs5W1B71OSE-38QoRxIOWo67fDDofCo0L3-HA3HA,7135
|
|
75
|
+
iop/production/rendering.py,sha256=6TY9B7Bzo8k6Xok-GbEjr8awHRch27QXEj4VTSSIVrg,19904
|
|
74
76
|
iop/production/runtime.py,sha256=JngIVVkigt1PNLUXBvQu3t8cBos-zLKlgguf0_WWh-M,2302
|
|
75
|
-
iop/production/types.py,sha256=
|
|
76
|
-
iop/production/validation.py,sha256=
|
|
77
|
+
iop/production/types.py,sha256=z2e4VG50KK8oDP3zNeT0_6FzPEhrAykjdd6uK_HvBZM,9431
|
|
78
|
+
iop/production/validation.py,sha256=14BfjrLXW1AOuP8AdBrs-CY6zynaDc8_AUD9frsYAIU,13753
|
|
77
79
|
iop/runtime/__init__.py,sha256=wcyWMRuPCYE_CLd1NmgmI1M-IbFs2Gz2yIj2-EW_2AA,70
|
|
78
80
|
iop/runtime/director.py,sha256=KW8BvkATK8XuIUPNBD9rMww9r3Zg4K3H54868eOgvl0,12867
|
|
79
81
|
iop/runtime/environment.py,sha256=0KZ4EoW6NUEbF7FW471emxQ-9FbaiX6ngUHVGihgZXU,1564
|
|
@@ -86,9 +88,9 @@ iop/runtime/remote/director.py,sha256=jNnBIocWS76vog6noJtVky5Tv7ZDnPwiWl-hCu958D
|
|
|
86
88
|
iop/runtime/remote/migration.py,sha256=Iboz-Ye7i9xif9bn433GazCF-gtD-HJW3wiUwWS5P6w,1831
|
|
87
89
|
iop/runtime/remote/settings.py,sha256=P1Hsgld5o57HpSZgPVZzobyjlUmxJj6bstiWvR0JXnc,2144
|
|
88
90
|
iop/runtime/remote/setup.py,sha256=Tr80F3JJTy7oSUso2jyx0iA_KZayd4MYt9vL0BvPpMA,2600
|
|
89
|
-
iris_pex_embedded_python-4.0.
|
|
90
|
-
iris_pex_embedded_python-4.0.
|
|
91
|
-
iris_pex_embedded_python-4.0.
|
|
92
|
-
iris_pex_embedded_python-4.0.
|
|
93
|
-
iris_pex_embedded_python-4.0.
|
|
94
|
-
iris_pex_embedded_python-4.0.
|
|
91
|
+
iris_pex_embedded_python-4.0.0b6.dist-info/licenses/LICENSE,sha256=rZSiBFId_sfbJ6RL0GjjPX-InNLkNS9ou7eQsikciI8,1089
|
|
92
|
+
iris_pex_embedded_python-4.0.0b6.dist-info/METADATA,sha256=_dCp-bIOqrvYR3wAYMbuqkrOYvvpxal9pnVehfdbH-M,4412
|
|
93
|
+
iris_pex_embedded_python-4.0.0b6.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
94
|
+
iris_pex_embedded_python-4.0.0b6.dist-info/entry_points.txt,sha256=fzS8ZFsPe8cnpEx208X60DNugCPQmuSloPeg4pyyJDY,42
|
|
95
|
+
iris_pex_embedded_python-4.0.0b6.dist-info/top_level.txt,sha256=BM54-OXQ6l2BDtmesWNXckh033s9gcjoO7bfjbwZbxU,4
|
|
96
|
+
iris_pex_embedded_python-4.0.0b6.dist-info/RECORD,,
|
{iris_pex_embedded_python-4.0.0b4.dist-info → iris_pex_embedded_python-4.0.0b6.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|