iris-pex-embedded-python 3.7.2b2__py3-none-any.whl → 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. iop/__init__.py +276 -18
  2. iop/__main__.py +3 -3
  3. iop/cli/__init__.py +1 -0
  4. iop/cli/formatting.py +45 -0
  5. iop/{_cli.py → cli/main.py} +238 -216
  6. iop/cli/parser.py +165 -0
  7. iop/cli/types.py +73 -0
  8. iop/cls/IOP/Common.cls +11 -2
  9. iop/cls/IOP/PrivateSession/Duplex.cls +1 -1
  10. iop/cls/IOP/Service/Remote/Rest/v1.cls +152 -5
  11. iop/cls/IOP/Utils.cls +814 -7
  12. iop/cls/IOP/Wrapper.cls +1 -1
  13. iop/components/__init__.py +16 -0
  14. iop/{_async_request.py → components/async_request.py} +7 -7
  15. iop/components/business_host.py +363 -0
  16. iop/components/business_operation.py +84 -0
  17. iop/{_business_process.py → components/business_process.py} +145 -125
  18. iop/components/business_service.py +106 -0
  19. iop/{_common.py → components/common.py} +160 -164
  20. iop/{_debugpy.py → components/debugpy.py} +11 -11
  21. iop/{_generator_request.py → components/generator_request.py} +4 -4
  22. iop/{_inbound_adapter.py → components/inbound_adapter.py} +10 -18
  23. iop/{_log_manager.py → components/log_manager.py} +1 -1
  24. iop/{_outbound_adapter.py → components/outbound_adapter.py} +1 -1
  25. iop/components/polling_business_service.py +48 -0
  26. iop/{_private_session_duplex.py → components/private_session_duplex.py} +7 -9
  27. iop/{_private_session_process.py → components/private_session_process.py} +2 -2
  28. iop/{_settings.py → components/settings.py} +23 -3
  29. iop/messages/__init__.py +7 -0
  30. iop/messages/base.py +61 -0
  31. iop/{_decorators.py → messages/decorators.py} +8 -2
  32. iop/messages/dispatch.py +351 -0
  33. iop/{_persistent_message.py → messages/persistent.py} +24 -19
  34. iop/{_serialization.py → messages/serialization.py} +34 -21
  35. iop/{_message_validator.py → messages/validation.py} +15 -13
  36. iop/migration/__init__.py +1 -0
  37. iop/migration/io.py +61 -0
  38. iop/migration/manifest.py +534 -0
  39. iop/migration/plans.py +80 -0
  40. iop/migration/utils.py +1126 -0
  41. iop/production/__init__.py +55 -0
  42. iop/production/actions.py +525 -0
  43. iop/production/common.py +103 -0
  44. iop/production/component.py +234 -0
  45. iop/production/declarations.py +190 -0
  46. iop/production/declarative.py +214 -0
  47. iop/production/diff.py +327 -0
  48. iop/production/import_.py +296 -0
  49. iop/production/inspection.py +92 -0
  50. iop/production/model.py +1143 -0
  51. iop/production/planning.py +678 -0
  52. iop/production/reconstruction.py +341 -0
  53. iop/production/rendering.py +610 -0
  54. iop/production/runtime.py +91 -0
  55. iop/production/source_inference.py +729 -0
  56. iop/production/types.py +546 -0
  57. iop/production/validation.py +452 -0
  58. iop/runtime/__init__.py +1 -0
  59. iop/runtime/director.py +415 -0
  60. iop/runtime/environment.py +59 -0
  61. iop/{_iris.py → runtime/iris.py} +2 -2
  62. iop/runtime/local.py +145 -0
  63. iop/{_director_protocol.py → runtime/protocol.py} +25 -8
  64. iop/runtime/remote/__init__.py +12 -0
  65. iop/runtime/remote/client.py +90 -0
  66. iop/runtime/remote/director.py +283 -0
  67. iop/runtime/remote/migration.py +57 -0
  68. iop/runtime/remote/settings.py +69 -0
  69. iop/runtime/remote/setup.py +72 -0
  70. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/METADATA +11 -2
  71. iris_pex_embedded_python-4.0.0.dist-info/RECORD +99 -0
  72. iris_pex_embedded_python-4.0.0.dist-info/entry_points.txt +2 -0
  73. iris_pex_embedded_python-4.0.0.dist-info/top_level.txt +1 -0
  74. grongier/__init__.py +0 -0
  75. grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -8
  76. grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -13
  77. grongier/cls/Grongier/PEX/BusinessService.cls +0 -8
  78. grongier/cls/Grongier/PEX/Common.cls +0 -10
  79. grongier/cls/Grongier/PEX/Director.cls +0 -10
  80. grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -4
  81. grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -13
  82. grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -4
  83. grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -8
  84. grongier/cls/Grongier/PEX/Message.cls +0 -13
  85. grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -8
  86. grongier/cls/Grongier/PEX/PickleMessage.cls +0 -13
  87. grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -8
  88. grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -14
  89. grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -14
  90. grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -14
  91. grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -14
  92. grongier/cls/Grongier/PEX/Test.cls +0 -10
  93. grongier/cls/Grongier/PEX/Utils.cls +0 -10
  94. grongier/cls/Grongier/Service/WSGI.cls +0 -4
  95. grongier/pex/__init__.py +0 -75
  96. grongier/pex/__main__.py +0 -4
  97. grongier/pex/_business_host.py +0 -1
  98. grongier/pex/_cli.py +0 -4
  99. grongier/pex/_common.py +0 -1
  100. grongier/pex/_director.py +0 -1
  101. grongier/pex/_utils.py +0 -1
  102. grongier/pex/wsgi/handlers.py +0 -104
  103. iop/_business_host.py +0 -322
  104. iop/_business_operation.py +0 -77
  105. iop/_business_service.py +0 -67
  106. iop/_director.py +0 -333
  107. iop/_dispatch.py +0 -194
  108. iop/_local.py +0 -106
  109. iop/_message.py +0 -45
  110. iop/_polling_business_service.py +0 -6
  111. iop/_remote.py +0 -473
  112. iop/_utils.py +0 -912
  113. iop/cls/IOP/Service/WSGI.cls +0 -310
  114. iop/wsgi/handlers.py +0 -112
  115. iris_pex_embedded_python-3.7.2b2.dist-info/RECORD +0 -96
  116. iris_pex_embedded_python-3.7.2b2.dist-info/entry_points.txt +0 -2
  117. iris_pex_embedded_python-3.7.2b2.dist-info/top_level.txt +0 -2
  118. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/WHEEL +0 -0
  119. {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,234 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+ from .common import (
7
+ _adapter_type_from_class_name,
8
+ _adapter_type_from_component_class,
9
+ _apply_settings_update,
10
+ _auto_proxy_class_name,
11
+ _bool_text,
12
+ _settings_to_iris,
13
+ _text_value,
14
+ )
15
+ from .types import TargetSetting, TargetSettingRef
16
+
17
+
18
+ @dataclass
19
+ class ComponentRef:
20
+ """Reference to a component item declared inside a Production."""
21
+
22
+ production: Any
23
+ name: str
24
+ component_class: type | None = None
25
+ class_name: str | None = None
26
+ adapter_class: type | None = None
27
+ adapter_class_name: str = ""
28
+ kind: str = "component"
29
+ category: str = ""
30
+ pool_size: int | str = 1
31
+ enabled: bool | str = True
32
+ foreground: bool | str = False
33
+ comment: str = ""
34
+ log_trace_events: bool | str = False
35
+ schedule: str = ""
36
+ host_settings: dict[str, Any] = field(default_factory=dict)
37
+ adapter_settings: dict[str, Any] = field(default_factory=dict)
38
+ other_settings: list[dict[str, Any]] = field(default_factory=list)
39
+ target_setting_names: set[str] = field(default_factory=set)
40
+ runtime_metadata: dict[str, Any] = field(default_factory=dict)
41
+
42
+ def __post_init__(self) -> None:
43
+ if self.class_name is None:
44
+ if self.component_class is None:
45
+ raise ValueError("component_class or class_name is required")
46
+ self.class_name = _auto_proxy_class_name(self.component_class)
47
+ if self.adapter_class is not None and not self.adapter_class_name:
48
+ self.adapter_class_name = _auto_proxy_class_name(self.adapter_class)
49
+ if not self.adapter_class_name:
50
+ self.adapter_class_name = _adapter_type_from_component_class(
51
+ self.component_class
52
+ )
53
+ if not self.adapter_class_name:
54
+ self.adapter_class_name = _adapter_type_from_class_name(self.class_name)
55
+
56
+ def __getattr__(self, name: str) -> TargetSettingRef:
57
+ if self.component_class is not None:
58
+ descriptor = getattr(self.component_class, name, None)
59
+ if isinstance(descriptor, TargetSetting):
60
+ return TargetSettingRef(
61
+ production=self.production,
62
+ component=self,
63
+ name=name,
64
+ )
65
+ if name in self.target_setting_names:
66
+ return self.target_setting(name)
67
+ raise AttributeError(name)
68
+
69
+ def target_setting(self, name: str) -> TargetSettingRef:
70
+ self.target_setting_names.add(name)
71
+ return TargetSettingRef(
72
+ production=self.production,
73
+ component=self,
74
+ name=name,
75
+ )
76
+
77
+ def set_host_setting(self, name: str, value: Any) -> None:
78
+ self.host_settings[name] = value
79
+
80
+ def pool(self, size: int | str) -> ComponentRef:
81
+ self.pool_size = size
82
+ return self
83
+
84
+ def enable(self, enabled: bool | str = True) -> ComponentRef:
85
+ self.enabled = enabled
86
+ return self
87
+
88
+ def disable(self) -> ComponentRef:
89
+ return self.enable(False)
90
+
91
+ def run_foreground(self, enabled: bool | str = True) -> ComponentRef:
92
+ self.foreground = enabled
93
+ return self
94
+
95
+ def trace(self, enabled: bool | str = True) -> ComponentRef:
96
+ self.log_trace_events = enabled
97
+ return self
98
+
99
+ def schedule_on(self, schedule: str) -> ComponentRef:
100
+ self.schedule = schedule
101
+ return self
102
+
103
+ def comment_as(self, text: str) -> ComponentRef:
104
+ self.comment = text
105
+ return self
106
+
107
+ def category_as(self, category: str) -> ComponentRef:
108
+ self.category = category
109
+ return self
110
+
111
+ def host_setting(self, name: str, value: Any) -> ComponentRef:
112
+ _apply_settings_update(self.host_settings, {name: value})
113
+ return self
114
+
115
+ def host_settings_update(self, values: dict[str, Any]) -> ComponentRef:
116
+ _apply_settings_update(self.host_settings, values)
117
+ return self
118
+
119
+ def setting(self, name: str, value: Any) -> ComponentRef:
120
+ return self.host_setting(name, value)
121
+
122
+ def settings_update(self, values: dict[str, Any]) -> ComponentRef:
123
+ return self.host_settings_update(values)
124
+
125
+ def adapter_setting(self, name: str, value: Any) -> ComponentRef:
126
+ _apply_settings_update(self.adapter_settings, {name: value})
127
+ return self
128
+
129
+ def adapter_settings_update(self, values: dict[str, Any]) -> ComponentRef:
130
+ _apply_settings_update(self.adapter_settings, values)
131
+ return self
132
+
133
+ def other_setting(
134
+ self,
135
+ target: str,
136
+ name: str,
137
+ value: Any,
138
+ ) -> ComponentRef:
139
+ if target == "Host":
140
+ return self.host_setting(name, value)
141
+ if target == "Adapter":
142
+ return self.adapter_setting(name, value)
143
+
144
+ self.other_settings = [
145
+ setting
146
+ for setting in self.other_settings
147
+ if not (
148
+ setting.get("@Target") == target
149
+ and setting.get("@Name") == name
150
+ )
151
+ ]
152
+ if value is not None:
153
+ self.other_settings.append(
154
+ {
155
+ "@Target": target,
156
+ "@Name": name,
157
+ "#text": _text_value(value),
158
+ }
159
+ )
160
+ return self
161
+
162
+ def connect(
163
+ self,
164
+ target_setting: str | TargetSettingRef,
165
+ target_component: ComponentRef | str | None = None,
166
+ **kwargs: Any,
167
+ ) -> ComponentRef:
168
+ target_setting_ref = self._coerce_target_setting_ref(target_setting)
169
+ self.production.connect(target_setting_ref, target_component, **kwargs)
170
+ return self
171
+
172
+ def _coerce_target_setting_ref(
173
+ self,
174
+ target_setting: str | TargetSettingRef,
175
+ ) -> TargetSettingRef:
176
+ if isinstance(target_setting, TargetSettingRef):
177
+ if target_setting.production is not self.production:
178
+ raise ValueError(
179
+ "source target setting belongs to a different Production"
180
+ )
181
+ if target_setting.component is not self:
182
+ raise ValueError(
183
+ "source target setting belongs to a different component"
184
+ )
185
+ return target_setting
186
+ return self.target_setting(str(target_setting))
187
+
188
+ def inspect(self, *, refresh: bool = True) -> dict[str, Any]:
189
+ return self.production.inspect_component(self, refresh=refresh)
190
+
191
+ def start(self) -> None:
192
+ self.production.start_component(self)
193
+
194
+ def stop(self) -> None:
195
+ self.production.stop_component(self)
196
+
197
+ def restart(self) -> None:
198
+ self.production.restart_component(self)
199
+
200
+ def test(
201
+ self,
202
+ message: Any = None,
203
+ *,
204
+ classname: str | None = None,
205
+ body: str | dict | None = None,
206
+ ) -> Any:
207
+ return self.production.test_component(
208
+ self,
209
+ message=message,
210
+ classname=classname,
211
+ body=body,
212
+ )
213
+
214
+ def to_dict(self) -> dict[str, Any]:
215
+ item: dict[str, Any] = {
216
+ "@Name": self.name,
217
+ "@Category": self.category,
218
+ "@ClassName": self.class_name,
219
+ "@PoolSize": _text_value(self.pool_size),
220
+ "@Enabled": _bool_text(self.enabled),
221
+ "@Foreground": _bool_text(self.foreground),
222
+ "@Comment": self.comment,
223
+ "@LogTraceEvents": _bool_text(self.log_trace_events),
224
+ "@Schedule": self.schedule,
225
+ }
226
+
227
+ settings = []
228
+ settings.extend(_settings_to_iris("Host", self.host_settings))
229
+ settings.extend(_settings_to_iris("Adapter", self.adapter_settings))
230
+ settings.extend(dict(setting) for setting in self.other_settings)
231
+ if settings:
232
+ item["Setting"] = settings
233
+
234
+ return item
@@ -0,0 +1,190 @@
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 target setting to one or more targets."""
19
+
20
+ target_setting: str | TargetSetting
21
+ targets: str | _NamedRouteTarget | Iterable[str | _NamedRouteTarget]
22
+
23
+ @property
24
+ def target_setting_name(self) -> str:
25
+ return normalize_route_target_setting(self.target_setting)
26
+
27
+ @property
28
+ def target_setting_owner(self) -> type | None:
29
+ if isinstance(self.target_setting, TargetSetting):
30
+ return self.target_setting.owner
31
+ return None
32
+
33
+ @property
34
+ def target_names(self) -> tuple[str, ...]:
35
+ if _is_route_target(self.targets):
36
+ targets = (self.targets,)
37
+ else:
38
+ try:
39
+ targets = tuple(self.targets)
40
+ except TypeError as exc:
41
+ raise TypeError(
42
+ f"Route {self.target_setting_name!r} targets must be an item name, "
43
+ "a production item declaration, or an iterable of either"
44
+ ) from exc
45
+ if not targets:
46
+ raise ValueError(
47
+ f"Route {self.target_setting_name!r} requires at least one target"
48
+ )
49
+ return tuple(
50
+ _route_target_name(target, self.target_setting_name)
51
+ for target in targets
52
+ )
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class _ProductionItemDeclaration:
57
+ name: str
58
+ component: type | str | None = None
59
+ class_name: str | None = None
60
+ adapter_class: type | str | None = None
61
+ adapter_class_name: str | None = None
62
+ enabled: bool | str = True
63
+ pool_size: int | str = 1
64
+ category: str = ""
65
+ foreground: bool | str = False
66
+ comment: str = ""
67
+ log_trace_events: bool | str = False
68
+ schedule: str = ""
69
+ settings: Mapping[str, Any] | None = None
70
+ host_settings: Mapping[str, Any] | None = None
71
+ adapter_settings: Mapping[str, Any] | None = None
72
+ other_settings: Iterable[Mapping[str, Any]] | None = None
73
+ routes: Route | Iterable[Route] | None = field(default_factory=tuple)
74
+
75
+ kind: ClassVar[str] = "component"
76
+
77
+ @property
78
+ def host_setting_values(self) -> dict[str, Any]:
79
+ settings = _mapping(self.settings, "settings", self.name)
80
+ host_settings = _mapping(self.host_settings, "host_settings", self.name)
81
+ duplicates = sorted(settings.keys() & host_settings.keys())
82
+ if duplicates:
83
+ names = ", ".join(repr(name) for name in duplicates)
84
+ raise ValueError(
85
+ f"{self.kind.title()} item {self.name!r} declares duplicate "
86
+ f"Host setting keys: {names}"
87
+ )
88
+ merged = dict(settings)
89
+ merged.update(host_settings)
90
+ return merged
91
+
92
+ @property
93
+ def adapter_setting_values(self) -> dict[str, Any]:
94
+ return _mapping(self.adapter_settings, "adapter_settings", self.name)
95
+
96
+ @property
97
+ def other_setting_values(self) -> list[dict[str, Any]]:
98
+ return [dict(setting) for setting in self.other_settings or ()]
99
+
100
+ @property
101
+ def route_values(self) -> tuple[Route, ...]:
102
+ if self.routes is None:
103
+ return ()
104
+ if isinstance(self.routes, Route):
105
+ return (self.routes,)
106
+ routes = tuple(self.routes)
107
+ for route in routes:
108
+ if not isinstance(route, Route):
109
+ raise TypeError(
110
+ f"{self.kind.title()} item {self.name!r} routes must be Route "
111
+ f"instances"
112
+ )
113
+ return routes
114
+
115
+
116
+ @dataclass(frozen=True)
117
+ class ServiceItem(_ProductionItemDeclaration):
118
+ kind: ClassVar[str] = "service"
119
+
120
+
121
+ @dataclass(frozen=True)
122
+ class ComponentItem(_ProductionItemDeclaration):
123
+ kind: ClassVar[str] = "component"
124
+
125
+
126
+ @dataclass(frozen=True)
127
+ class ProcessItem(_ProductionItemDeclaration):
128
+ kind: ClassVar[str] = "process"
129
+
130
+
131
+ @dataclass(frozen=True)
132
+ class OperationItem(_ProductionItemDeclaration):
133
+ kind: ClassVar[str] = "operation"
134
+
135
+
136
+ def normalize_route_target_setting(name: str | TargetSetting) -> str:
137
+ """Normalize known Pythonic route aliases without changing other settings."""
138
+
139
+ if isinstance(name, TargetSetting):
140
+ if not name.name:
141
+ raise ValueError(
142
+ "Route target setting must be declared on a component class"
143
+ )
144
+ return name.name
145
+ target_setting_name = str(name)
146
+ return SETTING_NAME_ALIASES.get(target_setting_name, target_setting_name)
147
+
148
+
149
+ def normalize_route_target_setting_for_match(name: str | TargetSetting) -> str:
150
+ return normalize_route_target_setting(name)
151
+
152
+
153
+ def _is_route_target(value: Any) -> bool:
154
+ return isinstance(value, str) or isinstance(value, _ProductionItemDeclaration)
155
+
156
+
157
+ def _route_target_name(value: Any, target_setting_name: str) -> str:
158
+ if isinstance(value, str):
159
+ if value:
160
+ return value
161
+ elif isinstance(value, _ProductionItemDeclaration):
162
+ if value.name:
163
+ return value.name
164
+
165
+ raise TypeError(
166
+ f"Route {target_setting_name!r} targets must be item names or production item "
167
+ "declarations"
168
+ )
169
+
170
+
171
+ def _mapping(
172
+ values: Mapping[str, Any] | None,
173
+ field_name: str,
174
+ item_name: str,
175
+ ) -> dict[str, Any]:
176
+ try:
177
+ return dict(values or {})
178
+ except (TypeError, ValueError) as exc:
179
+ raise TypeError(
180
+ f"Production item {item_name!r} {field_name} must be a mapping"
181
+ ) from exc
182
+
183
+
184
+ __all__ = [
185
+ "ComponentItem",
186
+ "OperationItem",
187
+ "ProcessItem",
188
+ "Route",
189
+ "ServiceItem",
190
+ ]
@@ -0,0 +1,214 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import Any, ClassVar, Protocol, cast
5
+
6
+ from .component import ComponentRef
7
+ from .declarations import (
8
+ Route,
9
+ _ProductionItemDeclaration,
10
+ normalize_route_target_setting_for_match,
11
+ )
12
+ from .types import TargetSettingRef
13
+
14
+
15
+ class _ComponentFactory(Protocol):
16
+ def __call__(
17
+ self,
18
+ name_or_cls: str | type,
19
+ cls: type | None = None,
20
+ **kwargs: Any,
21
+ ) -> ComponentRef: ...
22
+
23
+
24
+ class _DeclarativeProductionHost(Protocol):
25
+ def component(
26
+ self,
27
+ name_or_cls: str | type,
28
+ cls: type | None = None,
29
+ **kwargs: Any,
30
+ ) -> ComponentRef: ...
31
+
32
+ def service(
33
+ self,
34
+ name_or_cls: str | type,
35
+ cls: type | None = None,
36
+ **kwargs: Any,
37
+ ) -> ComponentRef: ...
38
+
39
+ def process(
40
+ self,
41
+ name_or_cls: str | type,
42
+ cls: type | None = None,
43
+ **kwargs: Any,
44
+ ) -> ComponentRef: ...
45
+
46
+ def operation(
47
+ self,
48
+ name_or_cls: str | type,
49
+ cls: type | None = None,
50
+ **kwargs: Any,
51
+ ) -> ComponentRef: ...
52
+
53
+ def item(self, name: str) -> ComponentRef: ...
54
+
55
+ def connect(
56
+ self,
57
+ source: TargetSettingRef,
58
+ target_component: ComponentRef | str | None = None,
59
+ *,
60
+ mode: str = "replace",
61
+ ) -> None: ...
62
+
63
+
64
+ class _DeclarativeProductionMixin:
65
+ components: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
66
+ services: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
67
+ processes: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
68
+ operations: ClassVar[Iterable[_ProductionItemDeclaration] | None] = ()
69
+
70
+ def _hydrate_declared_items(self) -> None:
71
+ declarations: list[_ProductionItemDeclaration] = []
72
+ for attr_name, expected_kind in (
73
+ ("components", "component"),
74
+ ("services", "service"),
75
+ ("processes", "process"),
76
+ ("operations", "operation"),
77
+ ):
78
+ for declaration in self._declared_items(attr_name):
79
+ if declaration.kind != expected_kind:
80
+ raise TypeError(
81
+ f"{attr_name} must contain {expected_kind.title()}Item "
82
+ f"declarations"
83
+ )
84
+ self._raise_declared_route_conflicts(declaration)
85
+ self._add_declared_item(declaration)
86
+ declarations.append(declaration)
87
+
88
+ for declaration in declarations:
89
+ self._connect_declared_routes(declaration)
90
+
91
+ def _declared_items(self, attr_name: str) -> tuple[_ProductionItemDeclaration, ...]:
92
+ values: object = getattr(type(self), attr_name, ())
93
+ if values is None:
94
+ return ()
95
+ if isinstance(values, _ProductionItemDeclaration):
96
+ return (values,)
97
+ try:
98
+ declarations = tuple(cast(Iterable[object], values))
99
+ except TypeError as exc:
100
+ raise TypeError(f"{attr_name} must be an iterable of item declarations") from exc
101
+ items: list[_ProductionItemDeclaration] = []
102
+ for declaration in declarations:
103
+ if not isinstance(declaration, _ProductionItemDeclaration):
104
+ raise TypeError(f"{attr_name} must contain production item declarations")
105
+ items.append(declaration)
106
+ return tuple(items)
107
+
108
+ def _raise_declared_route_conflicts(
109
+ self,
110
+ declaration: _ProductionItemDeclaration,
111
+ ) -> None:
112
+ host_target_settings = {
113
+ normalize_route_target_setting_for_match(name)
114
+ for name in declaration.host_setting_values
115
+ }
116
+ route_target_settings = {
117
+ route.target_setting_name for route in declaration.route_values
118
+ }
119
+ conflicts = sorted(host_target_settings & route_target_settings)
120
+ if not conflicts:
121
+ return
122
+ names = ", ".join(repr(name) for name in conflicts)
123
+ raise ValueError(
124
+ f"{declaration.kind.title()} item {declaration.name!r} declares route "
125
+ "target setting(s) in Host settings: "
126
+ f"{names}. Declare route target settings with Route only."
127
+ )
128
+
129
+ def _add_declared_item(self, declaration: _ProductionItemDeclaration) -> None:
130
+ kwargs: dict[str, Any] = {
131
+ "enabled": declaration.enabled,
132
+ "pool_size": declaration.pool_size,
133
+ "category": declaration.category,
134
+ "foreground": declaration.foreground,
135
+ "comment": declaration.comment,
136
+ "log_trace_events": declaration.log_trace_events,
137
+ "schedule": declaration.schedule,
138
+ "settings": declaration.host_setting_values,
139
+ "adapter_settings": declaration.adapter_setting_values,
140
+ }
141
+ if declaration.adapter_class is not None:
142
+ kwargs["adapter_class"] = declaration.adapter_class
143
+ if declaration.adapter_class_name is not None:
144
+ kwargs["adapter_class_name"] = declaration.adapter_class_name
145
+
146
+ host = cast(_DeclarativeProductionHost, self)
147
+ methods: dict[str, _ComponentFactory] = {
148
+ "component": host.component,
149
+ "service": host.service,
150
+ "process": host.process,
151
+ "operation": host.operation,
152
+ }
153
+ method = methods[declaration.kind]
154
+
155
+ component = declaration.component
156
+ if isinstance(component, type):
157
+ if declaration.class_name is not None:
158
+ kwargs["class_name"] = declaration.class_name
159
+ ref = method(declaration.name, component, **kwargs)
160
+ ref.other_settings = declaration.other_setting_values
161
+ return
162
+
163
+ class_name = declaration.class_name
164
+ if component is not None:
165
+ component_class_name = str(component)
166
+ if class_name is not None and class_name != component_class_name:
167
+ raise ValueError(
168
+ f"{declaration.kind.title()} item {declaration.name!r} "
169
+ "declares conflicting component and class_name values"
170
+ )
171
+ class_name = component_class_name
172
+
173
+ if class_name is None:
174
+ raise ValueError(
175
+ f"{declaration.kind.title()} item {declaration.name!r} requires "
176
+ "a component class or class_name"
177
+ )
178
+ kwargs["class_name"] = class_name
179
+ ref = method(declaration.name, **kwargs)
180
+ ref.other_settings = declaration.other_setting_values
181
+
182
+ def _connect_declared_routes(self, declaration: _ProductionItemDeclaration) -> None:
183
+ host = cast(_DeclarativeProductionHost, self)
184
+ source = host.item(declaration.name)
185
+ for route in declaration.route_values:
186
+ self._raise_if_route_target_setting_owner_mismatch(source, route)
187
+ target_setting_ref = source.target_setting(route.target_setting_name)
188
+ targets = route.target_names
189
+ host.connect(target_setting_ref, targets[0])
190
+ for target in targets[1:]:
191
+ host.connect(
192
+ target_setting_ref,
193
+ target,
194
+ mode="add",
195
+ )
196
+
197
+ def _raise_if_route_target_setting_owner_mismatch(
198
+ self,
199
+ source: ComponentRef,
200
+ route: Route,
201
+ ) -> None:
202
+ owner = route.target_setting_owner
203
+ if (
204
+ owner is None
205
+ or source.component_class is None
206
+ or issubclass(source.component_class, owner)
207
+ ):
208
+ return
209
+ raise ValueError(
210
+ f"Route target setting {route.target_setting_name!r} belongs to "
211
+ f"{owner.__module__}.{owner.__qualname__}, not "
212
+ f"{source.component_class.__module__}."
213
+ f"{source.component_class.__qualname__}"
214
+ )