iris-pex-embedded-python 4.0.0b4__tar.gz → 4.0.0b5__tar.gz

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 (100) hide show
  1. {iris_pex_embedded_python-4.0.0b4/src/iris_pex_embedded_python.egg-info → iris_pex_embedded_python-4.0.0b5}/PKG-INFO +1 -1
  2. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/pyproject.toml +1 -1
  3. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/__init__.py +12 -0
  4. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cli/main.py +3 -0
  5. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cli/parser.py +1 -1
  6. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Utils.cls +10 -0
  7. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/settings.py +11 -1
  8. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/migration/utils.py +15 -5
  9. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/__init__.py +10 -0
  10. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/common.py +5 -0
  11. iris_pex_embedded_python-4.0.0b5/src/iop/production/declarations.py +165 -0
  12. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/model.py +233 -35
  13. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/reconstruction.py +3 -3
  14. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/rendering.py +274 -0
  15. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/types.py +10 -1
  16. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/validation.py +7 -8
  17. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5/src/iris_pex_embedded_python.egg-info}/PKG-INFO +1 -1
  18. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iris_pex_embedded_python.egg-info/SOURCES.txt +1 -0
  19. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/LICENSE +0 -0
  20. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/README.md +0 -0
  21. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/setup.cfg +0 -0
  22. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/__main__.py +0 -0
  23. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cli/__init__.py +0 -0
  24. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cli/formatting.py +0 -0
  25. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cli/types.py +0 -0
  26. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/BusinessOperation.cls +0 -0
  27. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/BusinessProcess.cls +0 -0
  28. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/BusinessService.cls +0 -0
  29. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Common.cls +0 -0
  30. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Director.cls +0 -0
  31. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Duplex/Operation.cls +0 -0
  32. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Duplex/Process.cls +0 -0
  33. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Duplex/Service.cls +0 -0
  34. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Generator/Message/Ack.cls +0 -0
  35. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Generator/Message/Poll.cls +0 -0
  36. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Generator/Message/Start.cls +0 -0
  37. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Generator/Message/StartPickle.cls +0 -0
  38. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Generator/Message/Stop.cls +0 -0
  39. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/InboundAdapter.cls +0 -0
  40. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Message/JSONSchema.cls +0 -0
  41. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Message.cls +0 -0
  42. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/OutboundAdapter.cls +0 -0
  43. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/PickleMessage.cls +0 -0
  44. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/PrivateSession/Duplex.cls +0 -0
  45. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/PrivateSession/Message/Ack.cls +0 -0
  46. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/PrivateSession/Message/Poll.cls +0 -0
  47. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/PrivateSession/Message/Start.cls +0 -0
  48. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/PrivateSession/Message/Stop.cls +0 -0
  49. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Projection.cls +0 -0
  50. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Service/Remote/Handler.cls +0 -0
  51. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Service/Remote/Rest/v1.cls +0 -0
  52. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Test.cls +0 -0
  53. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/cls/IOP/Wrapper.cls +0 -0
  54. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/__init__.py +0 -0
  55. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/async_request.py +0 -0
  56. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/business_host.py +0 -0
  57. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/business_operation.py +0 -0
  58. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/business_process.py +0 -0
  59. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/business_service.py +0 -0
  60. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/common.py +0 -0
  61. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/debugpy.py +0 -0
  62. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/generator_request.py +0 -0
  63. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/inbound_adapter.py +0 -0
  64. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/log_manager.py +0 -0
  65. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/outbound_adapter.py +0 -0
  66. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/polling_business_service.py +0 -0
  67. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/private_session_duplex.py +0 -0
  68. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/components/private_session_process.py +0 -0
  69. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/__init__.py +0 -0
  70. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/base.py +0 -0
  71. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/decorators.py +0 -0
  72. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/dispatch.py +0 -0
  73. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/persistent.py +0 -0
  74. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/serialization.py +0 -0
  75. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/messages/validation.py +0 -0
  76. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/migration/__init__.py +0 -0
  77. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/migration/io.py +0 -0
  78. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/migration/plans.py +0 -0
  79. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/actions.py +0 -0
  80. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/component.py +0 -0
  81. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/diff.py +0 -0
  82. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/import_.py +0 -0
  83. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/inspection.py +0 -0
  84. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/production/runtime.py +0 -0
  85. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/__init__.py +0 -0
  86. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/director.py +0 -0
  87. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/environment.py +0 -0
  88. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/iris.py +0 -0
  89. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/local.py +0 -0
  90. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/protocol.py +0 -0
  91. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/remote/__init__.py +0 -0
  92. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/remote/client.py +0 -0
  93. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/remote/director.py +0 -0
  94. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/remote/migration.py +0 -0
  95. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/remote/settings.py +0 -0
  96. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iop/runtime/remote/setup.py +0 -0
  97. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iris_pex_embedded_python.egg-info/dependency_links.txt +0 -0
  98. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iris_pex_embedded_python.egg-info/entry_points.txt +0 -0
  99. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iris_pex_embedded_python.egg-info/requires.txt +0 -0
  100. {iris_pex_embedded_python-4.0.0b4 → iris_pex_embedded_python-4.0.0b5}/src/iris_pex_embedded_python.egg-info/top_level.txt +0 -0
@@ -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.0b5
4
4
  Summary: Iris Interoperability based on Embedded Python
5
5
  Author-email: grongier <guillaume.rongier@intersystems.com>
6
6
  License: MIT License
@@ -3,7 +3,7 @@ requires = ["setuptools", "wheel"]
3
3
 
4
4
  [project]
5
5
  name = "iris_pex_embedded_python"
6
- version = "4.0.0b4"
6
+ version = "4.0.0b5"
7
7
  description = "Iris Interoperability based on Embedded Python"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10"
@@ -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",
@@ -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
@@ -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
  )
@@ -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:
@@ -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",
@@ -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,165 @@
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
6
+
7
+ from .common import SETTING_NAME_ALIASES
8
+ from .types import TargetSetting
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class Route:
13
+ """Declarative route from a production item port to one or more targets."""
14
+
15
+ port: str | TargetSetting
16
+ targets: str | Iterable[str]
17
+ logical_name: str = ""
18
+
19
+ @property
20
+ def port_name(self) -> str:
21
+ return normalize_route_port(self.port)
22
+
23
+ @property
24
+ def port_owner(self) -> type | None:
25
+ if isinstance(self.port, TargetSetting):
26
+ return self.port.owner
27
+ return None
28
+
29
+ @property
30
+ def route_logical_name(self) -> str:
31
+ if self.logical_name:
32
+ return self.logical_name
33
+ if isinstance(self.port, TargetSetting):
34
+ return self.port.logical_name
35
+ return ""
36
+
37
+ @property
38
+ def target_names(self) -> tuple[str, ...]:
39
+ if isinstance(self.targets, str):
40
+ targets = (self.targets,)
41
+ else:
42
+ targets = tuple(str(target) for target in self.targets)
43
+ if not targets:
44
+ raise ValueError(f"Route {self.port_name!r} requires at least one target")
45
+ return targets
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class _ProductionItemDeclaration:
50
+ name: str
51
+ component: type | str | None = None
52
+ class_name: str | None = None
53
+ adapter_class: type | str | None = None
54
+ adapter_class_name: str | None = None
55
+ enabled: bool | str = True
56
+ pool_size: int | str = 1
57
+ category: str = ""
58
+ foreground: bool | str = False
59
+ comment: str = ""
60
+ log_trace_events: bool | str = False
61
+ schedule: str = ""
62
+ settings: Mapping[str, Any] | None = None
63
+ host_settings: Mapping[str, Any] | None = None
64
+ adapter_settings: Mapping[str, Any] | None = None
65
+ other_settings: Iterable[Mapping[str, Any]] | None = None
66
+ routes: Route | Iterable[Route] | None = field(default_factory=tuple)
67
+
68
+ kind: ClassVar[str] = "component"
69
+
70
+ @property
71
+ def host_setting_values(self) -> dict[str, Any]:
72
+ settings = _mapping(self.settings, "settings", self.name)
73
+ host_settings = _mapping(self.host_settings, "host_settings", self.name)
74
+ duplicates = sorted(settings.keys() & host_settings.keys())
75
+ if duplicates:
76
+ names = ", ".join(repr(name) for name in duplicates)
77
+ raise ValueError(
78
+ f"{self.kind.title()} item {self.name!r} declares duplicate "
79
+ f"Host setting keys: {names}"
80
+ )
81
+ merged = dict(settings)
82
+ merged.update(host_settings)
83
+ return merged
84
+
85
+ @property
86
+ def adapter_setting_values(self) -> dict[str, Any]:
87
+ return _mapping(self.adapter_settings, "adapter_settings", self.name)
88
+
89
+ @property
90
+ def other_setting_values(self) -> list[dict[str, Any]]:
91
+ return [dict(setting) for setting in self.other_settings or ()]
92
+
93
+ @property
94
+ def route_values(self) -> tuple[Route, ...]:
95
+ if self.routes is None:
96
+ return ()
97
+ if isinstance(self.routes, Route):
98
+ return (self.routes,)
99
+ routes = tuple(self.routes)
100
+ for route in routes:
101
+ if not isinstance(route, Route):
102
+ raise TypeError(
103
+ f"{self.kind.title()} item {self.name!r} routes must be Route "
104
+ f"instances"
105
+ )
106
+ return routes
107
+
108
+
109
+ @dataclass(frozen=True)
110
+ class ServiceItem(_ProductionItemDeclaration):
111
+ kind: ClassVar[str] = "service"
112
+
113
+
114
+ @dataclass(frozen=True)
115
+ class ComponentItem(_ProductionItemDeclaration):
116
+ kind: ClassVar[str] = "component"
117
+
118
+
119
+ @dataclass(frozen=True)
120
+ class ProcessItem(_ProductionItemDeclaration):
121
+ kind: ClassVar[str] = "process"
122
+
123
+
124
+ @dataclass(frozen=True)
125
+ class OperationItem(_ProductionItemDeclaration):
126
+ kind: ClassVar[str] = "operation"
127
+
128
+
129
+ def normalize_route_port(name: str | TargetSetting) -> str:
130
+ """Normalize known Pythonic route aliases without changing other settings."""
131
+
132
+ if isinstance(name, TargetSetting):
133
+ if not name.name:
134
+ raise ValueError(
135
+ "Route target setting must be declared on a component class"
136
+ )
137
+ return name.name
138
+ port_name = str(name)
139
+ return SETTING_NAME_ALIASES.get(port_name, port_name)
140
+
141
+
142
+ def normalize_route_port_for_match(name: str | TargetSetting) -> str:
143
+ return normalize_route_port(name)
144
+
145
+
146
+ def _mapping(
147
+ values: Mapping[str, Any] | None,
148
+ field_name: str,
149
+ item_name: str,
150
+ ) -> dict[str, Any]:
151
+ try:
152
+ return dict(values or {})
153
+ except (TypeError, ValueError) as exc:
154
+ raise TypeError(
155
+ f"Production item {item_name!r} {field_name} must be a mapping"
156
+ ) from exc
157
+
158
+
159
+ __all__ = [
160
+ "ComponentItem",
161
+ "OperationItem",
162
+ "ProcessItem",
163
+ "Route",
164
+ "ServiceItem",
165
+ ]