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 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","")
@@ -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
- raise_on_error(
1040
- _iris.get_iris()
1041
- .cls("IOP.Utils")
1042
- .CreateProductionFromJSON(production_name, json.dumps(production))
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):
@@ -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
- class Production:
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 = False,
47
- log_general_trace_events: bool | str = False,
48
- actor_pool_size: int | str = 2,
49
- description: str = "",
50
- shutdown_timeout: int | str = PRODUCTION_SETTING_FIELDS["shutdown_timeout"][1],
51
- update_timeout: int | str = PRODUCTION_SETTING_FIELDS["update_timeout"][1],
52
- alert_notification_manager: str = PRODUCTION_SETTING_FIELDS[
53
- "alert_notification_manager"
54
- ][1],
55
- alert_notification_operation: str = PRODUCTION_SETTING_FIELDS[
56
- "alert_notification_operation"
57
- ][1],
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.name = name
68
- self.testing_enabled = testing_enabled
69
- self.log_general_trace_events = log_general_trace_events
70
- self.actor_pool_size = actor_pool_size
71
- self.description = description
72
- self.shutdown_timeout = shutdown_timeout
73
- self.update_timeout = update_timeout
74
- self.alert_notification_manager = alert_notification_manager
75
- self.alert_notification_operation = alert_notification_operation
76
- self.alert_notification_recipients = alert_notification_recipients
77
- self.alert_action_window = alert_action_window
78
- self.namespace = namespace
79
- self._director = director
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(name, namespace=namespace, director=director)
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
 
@@ -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,
@@ -86,8 +86,84 @@ def production_to_python(production) -> str:
86
86
  return "\n".join(lines) + "\n"
87
87
 
88
88
 
89
- def _production_constructor_lines(production) -> list[str]:
90
- kwargs = [
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
- "alert_notification_manager",
103
- production.alert_notification_manager,
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 kwargs
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."""
@@ -4,7 +4,11 @@ import warnings
4
4
  from dataclasses import dataclass
5
5
  from typing import Any
6
6
 
7
- from .common import PRODUCTION_SETTING_FIELDS_BY_IRIS, PRODUCTION_SETTING_NAMES
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 = _SETTING_NAME_ALIASES.get(setting_name)
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 = _SETTING_NAME_ALIASES.get(setting_name)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris_pex_embedded_python
3
- Version: 4.0.0b4
3
+ Version: 4.0.0b6
4
4
  Summary: Iris Interoperability based on Embedded Python
5
5
  Author-email: grongier <guillaume.rongier@intersystems.com>
6
6
  License: MIT License
@@ -1,9 +1,9 @@
1
- iop/__init__.py,sha256=JCkmGv50W1i4oi7CKrvLXyQ88-lv-2y-8SJ_05BOsPA,4001
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=FzE86PbdIrJ8zVIIcsTRD7-v5_zdFUisnQ5Rs5hbQH8,13197
6
- iop/cli/parser.py,sha256=HWn3mxndR8Fw9HrnjtSP-ZMTps9X1cbQWCcye7NPIug,4878
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=SZOKKVaJEUp3Y4xkt1OIWw2ExsjNZoWCBsySP7CjqoY,31095
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=VfUQ4H0GrJ7N-IG-KYUxtTQbRJmsTI5crU7V90mPczY,5509
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=s2CoqWj4HeW8KrKTaQZGBKH4ZMku3Zg8eolNZvGKMq0,40817
64
- iop/production/__init__.py,sha256=bVFLRI4lvhbUlKApRvvSqM6pVkOsBuofRrzuEIBT6nY,1360
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=RopwhFJT7jP15a4iBvMWipwYwIAV6A0d0UsoxWLo-is,2969
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=7YDAgQ-fo87VnE5A-CxmNtNsRHMVFB2NKkpBykH-ohQ,31106
72
- iop/production/reconstruction.py,sha256=YJMOBtpTzT-IxgQ4lx1kivA8zbWz3UZfwt-5q7d_kMk,7135
73
- iop/production/rendering.py,sha256=d9Et3hE8aPTCfnk0RKc6vu5HYOrI-JXFCfU6vZLKsKg,10649
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=SsTYPspc8VGrSH8hy49tDBVLWu55D5yODNT7lfFbjmc,9131
76
- iop/production/validation.py,sha256=yeWEQmWANQ8md2H8QN4DhHyR5nIDLMJWZN7e0d2y2Rk,13839
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.0b4.dist-info/licenses/LICENSE,sha256=rZSiBFId_sfbJ6RL0GjjPX-InNLkNS9ou7eQsikciI8,1089
90
- iris_pex_embedded_python-4.0.0b4.dist-info/METADATA,sha256=wksvI6gY4uyZ7OftWfoAvCB4AspPjHl5xNqMsiA0sIY,4412
91
- iris_pex_embedded_python-4.0.0b4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
92
- iris_pex_embedded_python-4.0.0b4.dist-info/entry_points.txt,sha256=fzS8ZFsPe8cnpEx208X60DNugCPQmuSloPeg4pyyJDY,42
93
- iris_pex_embedded_python-4.0.0b4.dist-info/top_level.txt,sha256=BM54-OXQ6l2BDtmesWNXckh033s9gcjoO7bfjbwZbxU,4
94
- iris_pex_embedded_python-4.0.0b4.dist-info/RECORD,,
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,,