iris-pex-embedded-python 3.7.2b1__tar.gz → 3.7.2b2__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 (102) hide show
  1. {iris_pex_embedded_python-3.7.2b1/src/iris_pex_embedded_python.egg-info → iris_pex_embedded_python-3.7.2b2}/PKG-INFO +1 -1
  2. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/pyproject.toml +1 -1
  3. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/__init__.py +9 -0
  4. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/__init__.py +9 -0
  5. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_cli.py +29 -0
  6. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_common.py +240 -78
  7. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_local.py +1 -1
  8. iris_pex_embedded_python-3.7.2b2/src/iop/_polling_business_service.py +6 -0
  9. iris_pex_embedded_python-3.7.2b2/src/iop/_settings.py +188 -0
  10. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_utils.py +219 -17
  11. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Utils.cls +21 -1
  12. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2/src/iris_pex_embedded_python.egg-info}/PKG-INFO +1 -1
  13. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iris_pex_embedded_python.egg-info/SOURCES.txt +2 -0
  14. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/LICENSE +0 -0
  15. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/README.md +0 -0
  16. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/setup.cfg +0 -0
  17. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/setup.py +0 -0
  18. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/__init__.py +0 -0
  19. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -0
  20. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -0
  21. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/BusinessService.cls +0 -0
  22. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Common.cls +0 -0
  23. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Director.cls +0 -0
  24. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -0
  25. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -0
  26. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -0
  27. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -0
  28. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Message.cls +0 -0
  29. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -0
  30. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/PickleMessage.cls +0 -0
  31. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -0
  32. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -0
  33. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -0
  34. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -0
  35. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -0
  36. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Test.cls +0 -0
  37. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/PEX/Utils.cls +0 -0
  38. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/cls/Grongier/Service/WSGI.cls +0 -0
  39. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/__main__.py +0 -0
  40. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/_business_host.py +0 -0
  41. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/_cli.py +0 -0
  42. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/_common.py +0 -0
  43. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/_director.py +0 -0
  44. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/_utils.py +0 -0
  45. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/grongier/pex/wsgi/handlers.py +0 -0
  46. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/__main__.py +0 -0
  47. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_async_request.py +0 -0
  48. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_business_host.py +0 -0
  49. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_business_operation.py +0 -0
  50. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_business_process.py +0 -0
  51. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_business_service.py +0 -0
  52. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_debugpy.py +0 -0
  53. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_decorators.py +0 -0
  54. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_director.py +0 -0
  55. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_director_protocol.py +0 -0
  56. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_dispatch.py +0 -0
  57. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_generator_request.py +0 -0
  58. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_inbound_adapter.py +0 -0
  59. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_iris.py +0 -0
  60. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_log_manager.py +0 -0
  61. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_message.py +0 -0
  62. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_message_validator.py +0 -0
  63. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_outbound_adapter.py +0 -0
  64. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_persistent_message.py +0 -0
  65. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_private_session_duplex.py +0 -0
  66. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_private_session_process.py +0 -0
  67. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_remote.py +0 -0
  68. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/_serialization.py +0 -0
  69. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/BusinessOperation.cls +0 -0
  70. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/BusinessProcess.cls +0 -0
  71. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/BusinessService.cls +0 -0
  72. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Common.cls +0 -0
  73. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Director.cls +0 -0
  74. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Duplex/Operation.cls +0 -0
  75. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Duplex/Process.cls +0 -0
  76. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Duplex/Service.cls +0 -0
  77. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Generator/Message/Ack.cls +0 -0
  78. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Generator/Message/Poll.cls +0 -0
  79. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Generator/Message/Start.cls +0 -0
  80. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Generator/Message/StartPickle.cls +0 -0
  81. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Generator/Message/Stop.cls +0 -0
  82. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/InboundAdapter.cls +0 -0
  83. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Message/JSONSchema.cls +0 -0
  84. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Message.cls +0 -0
  85. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/OutboundAdapter.cls +0 -0
  86. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/PickleMessage.cls +0 -0
  87. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/PrivateSession/Duplex.cls +0 -0
  88. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/PrivateSession/Message/Ack.cls +0 -0
  89. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/PrivateSession/Message/Poll.cls +0 -0
  90. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/PrivateSession/Message/Start.cls +0 -0
  91. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/PrivateSession/Message/Stop.cls +0 -0
  92. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Projection.cls +0 -0
  93. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Service/Remote/Handler.cls +0 -0
  94. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Service/Remote/Rest/v1.cls +0 -0
  95. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Service/WSGI.cls +0 -0
  96. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Test.cls +0 -0
  97. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/cls/IOP/Wrapper.cls +0 -0
  98. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iop/wsgi/handlers.py +0 -0
  99. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iris_pex_embedded_python.egg-info/dependency_links.txt +0 -0
  100. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iris_pex_embedded_python.egg-info/entry_points.txt +0 -0
  101. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/src/iris_pex_embedded_python.egg-info/requires.txt +0 -0
  102. {iris_pex_embedded_python-3.7.2b1 → iris_pex_embedded_python-3.7.2b2}/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: 3.7.2b1
3
+ Version: 3.7.2b2
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 = "3.7.2b1"
6
+ version = "3.7.2b2"
7
7
  description = "Iris Interoperability based on Embedded Python"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10"
@@ -6,11 +6,16 @@ from iop._inbound_adapter import _InboundAdapter
6
6
  from iop._message import _Message
7
7
  from iop._message import _PickleMessage
8
8
  from iop._outbound_adapter import _OutboundAdapter
9
+ from iop._polling_business_service import _PollingBusinessServiceMixin
9
10
  from iop._persistent_message import Field as Field
10
11
  from iop._persistent_message import Model as Model
11
12
  from iop._persistent_message import _PersistentMessage
12
13
  from iop._private_session_duplex import _PrivateSessionDuplex
13
14
  from iop._private_session_process import _PrivateSessionProcess
15
+ from iop._settings import Category as Category
16
+ from iop._settings import Setting as Setting
17
+ from iop._settings import controls as controls
18
+ from iop._settings import setting as setting
14
19
  from iop._utils import _Utils
15
20
 
16
21
 
@@ -30,6 +35,10 @@ class BusinessService(_BusinessService):
30
35
  pass
31
36
 
32
37
 
38
+ class PollingBusinessService(_PollingBusinessServiceMixin, BusinessService):
39
+ pass
40
+
41
+
33
42
  class BusinessOperation(_BusinessOperation):
34
43
  pass
35
44
 
@@ -10,11 +10,16 @@ from iop._message import (
10
10
  _PydanticPickleMessage,
11
11
  )
12
12
  from iop._outbound_adapter import _OutboundAdapter
13
+ from iop._polling_business_service import _PollingBusinessServiceMixin
13
14
  from iop._persistent_message import Field as Field
14
15
  from iop._persistent_message import Model as Model
15
16
  from iop._persistent_message import _PersistentMessage
16
17
  from iop._private_session_duplex import _PrivateSessionDuplex
17
18
  from iop._private_session_process import _PrivateSessionProcess
19
+ from iop._settings import Category as Category
20
+ from iop._settings import Setting as Setting
21
+ from iop._settings import controls as controls
22
+ from iop._settings import setting as setting
18
23
  from iop._utils import _Utils
19
24
 
20
25
 
@@ -34,6 +39,10 @@ class BusinessService(_BusinessService):
34
39
  pass
35
40
 
36
41
 
42
+ class PollingBusinessService(_PollingBusinessServiceMixin, BusinessService):
43
+ pass
44
+
45
+
37
46
  class BusinessOperation(_BusinessOperation):
38
47
  pass
39
48
 
@@ -13,6 +13,7 @@ from importlib.metadata import version
13
13
  from ._local import _LocalDirector
14
14
  from ._remote import _RemoteDirector, get_remote_settings
15
15
  from ._director_protocol import DirectorProtocol
16
+ from ._utils import _Utils
16
17
 
17
18
 
18
19
  class CommandType(Enum):
@@ -57,6 +58,7 @@ class CommandArgs:
57
58
  force_local: bool = False
58
59
  remote_settings: Optional[str] = None
59
60
  update: bool = False
61
+ migration_plan: bool = False
60
62
 
61
63
 
62
64
  class Command:
@@ -252,7 +254,27 @@ class Command:
252
254
  if migrate_path is not None:
253
255
  if not os.path.isabs(migrate_path):
254
256
  migrate_path = os.path.join(os.getcwd(), migrate_path)
257
+ mode = "REMOTE" if self._is_remote else "LOCAL"
258
+ if self.args.migration_plan:
259
+ print(
260
+ _Utils.explain_migration(
261
+ migrate_path, mode=mode, namespace=self.director.namespace
262
+ )
263
+ )
264
+ return
265
+ if self._is_remote:
266
+ print(
267
+ _Utils.explain_migration(
268
+ migrate_path, mode=mode, namespace=self.director.namespace
269
+ )
270
+ )
255
271
  self.director.migrate(migrate_path)
272
+ if self._is_remote:
273
+ print(
274
+ _Utils.format_migration_success(
275
+ migrate_path, namespace=self.director.namespace
276
+ )
277
+ )
256
278
 
257
279
  def _handle_log(self) -> None:
258
280
  if self.args.log == "not_set":
@@ -406,6 +428,13 @@ def create_parser() -> argparse.ArgumentParser:
406
428
  help="force local mode, skip remote even if REMOTE_SETTINGS or IOP_URL is present",
407
429
  action="store_true",
408
430
  )
431
+ migrate.add_argument(
432
+ "--dry-run",
433
+ "--explain",
434
+ dest="migration_plan",
435
+ help="show the migration plan and validation messages without writing to IRIS",
436
+ action="store_true",
437
+ )
409
438
 
410
439
  remote = main_parser.add_argument_group("remote arguments")
411
440
  remote.add_argument(
@@ -1,11 +1,143 @@
1
1
  import abc
2
2
  import inspect
3
3
  import traceback
4
- from typing import Any, ClassVar, List, Optional, Tuple
4
+ from enum import Enum
5
+ from types import UnionType
6
+ from typing import (
7
+ Annotated,
8
+ Any,
9
+ ClassVar,
10
+ List,
11
+ Optional,
12
+ Tuple,
13
+ Union,
14
+ get_args,
15
+ get_origin,
16
+ get_type_hints,
17
+ )
5
18
 
6
19
  from . import _iris
7
- from ._log_manager import LogManager, logging
8
20
  from ._debugpy import debugpython
21
+ from ._log_manager import LogManager, logging
22
+ from ._settings import Setting
23
+
24
+
25
+ _NO_VALUE = object()
26
+
27
+ _EXCLUDED_SETTING_NAMES = {
28
+ "INFO_URL",
29
+ "ICON_URL",
30
+ "PERSISTENT_PROPERTY_LIST",
31
+ "log_to_console",
32
+ "logger",
33
+ "iris_handle",
34
+ "DISPATCH",
35
+ "adapter",
36
+ "Adapter",
37
+ "buffer",
38
+ "BusinessHost",
39
+ "business_host",
40
+ "business_host_python",
41
+ }
42
+
43
+ _PYTHON_TYPE_TO_IRIS = {
44
+ int: "Integer",
45
+ float: "Numeric",
46
+ complex: "Numeric",
47
+ bool: "Boolean",
48
+ str: "String",
49
+ }
50
+
51
+ _SIMPLE_TYPE_NAMES = {
52
+ "int": "Integer",
53
+ "integer": "Integer",
54
+ "float": "Numeric",
55
+ "complex": "Numeric",
56
+ "number": "Numeric",
57
+ "numeric": "Numeric",
58
+ "bool": "Boolean",
59
+ "boolean": "Boolean",
60
+ "str": "String",
61
+ "string": "String",
62
+ }
63
+
64
+
65
+ def _string_metadata(value: Any) -> str:
66
+ if value is None:
67
+ return ""
68
+ if isinstance(value, Enum):
69
+ return str(value.value)
70
+ return str(value)
71
+
72
+
73
+ def _type_hints_with_extras(cls) -> dict[str, Any]:
74
+ try:
75
+ return get_type_hints(cls, include_extras=True)
76
+ except Exception:
77
+ hints: dict[str, Any] = {}
78
+ for base in reversed(inspect.getmro(cls)):
79
+ hints.update(getattr(base, "__annotations__", {}))
80
+ return hints
81
+
82
+
83
+ def _unwrap_annotated(data_type: Any) -> tuple[Any, tuple[Any, ...]]:
84
+ if get_origin(data_type) is Annotated:
85
+ args = get_args(data_type)
86
+ if args:
87
+ return args[0], args[1:]
88
+ return data_type, ()
89
+
90
+
91
+ def _setting_from_annotation(data_type: Any) -> tuple[Any, Optional[Setting]]:
92
+ data_type, metadata = _unwrap_annotated(data_type)
93
+ setting = None
94
+ for item in metadata:
95
+ if isinstance(item, Setting):
96
+ setting = item
97
+ return data_type, setting
98
+
99
+
100
+ def _unwrap_optional(data_type: Any) -> Any:
101
+ origin = get_origin(data_type)
102
+ if origin in (Union, UnionType):
103
+ args = [arg for arg in get_args(data_type) if arg is not type(None)]
104
+ if len(args) == 1:
105
+ return args[0]
106
+ return data_type
107
+
108
+
109
+ def _iris_data_type(data_type: Any) -> Optional[str]:
110
+ if data_type is None or data_type == "":
111
+ return None
112
+
113
+ data_type, _ = _unwrap_annotated(data_type)
114
+ data_type = _unwrap_optional(data_type)
115
+
116
+ if data_type is Any or data_type is object:
117
+ return "String"
118
+
119
+ if isinstance(data_type, str):
120
+ data_type = data_type.strip()
121
+ return _SIMPLE_TYPE_NAMES.get(data_type.lower(), data_type)
122
+
123
+ if data_type in _PYTHON_TYPE_TO_IRIS:
124
+ return _PYTHON_TYPE_TO_IRIS[data_type]
125
+
126
+ origin = get_origin(data_type)
127
+ if origin in _PYTHON_TYPE_TO_IRIS:
128
+ return _PYTHON_TYPE_TO_IRIS[origin]
129
+ if origin is not None:
130
+ return "String"
131
+
132
+ return None
133
+
134
+
135
+ def _is_setting_member(name: str, value: Any) -> bool:
136
+ if name.startswith("_") or name in _EXCLUDED_SETTING_NAMES:
137
+ return False
138
+ return not (
139
+ inspect.ismethod(value) or inspect.isfunction(value) or inspect.isclass(value)
140
+ )
9
141
 
10
142
 
11
143
  class _Common(metaclass=abc.ABCMeta):
@@ -168,87 +300,117 @@ class _Common(metaclass=abc.ABCMeta):
168
300
  - Required flag
169
301
  - Category
170
302
  - Description
303
+ - Control/editor context
171
304
 
172
305
  Only includes non-private class attributes and properties.
173
306
  """
174
307
  ret = []
175
308
  try:
176
- # getmembers() returns all the members of an obj
177
- for member in inspect.getmembers(cls):
178
- # remove private and protected functions
179
- if not member[0].startswith("_"):
180
- # remove other methods and functions
181
- if (
182
- not inspect.ismethod(member[1])
183
- and not inspect.isfunction(member[1])
184
- and not inspect.isclass(member[1])
185
- ):
186
- if member[0] not in (
187
- "INFO_URL",
188
- "ICON_URL",
189
- "PERSISTENT_PROPERTY_LIST",
190
- "log_to_console",
191
- "logger",
192
- "iris_handle",
193
- "DISPATCH",
194
- "adapter",
195
- "Adapter",
196
- "buffer",
197
- "BusinessHost",
198
- "business_host",
199
- "business_host_python",
200
- ):
201
- name = member[0]
202
- req = 0
203
- cat = "Additional"
204
- desc = ""
205
- # get value, set to "" if None or a @property
206
- val = member[1]
207
- if isinstance(val, property) or (val is None):
208
- val = ""
209
- dt = str(type(val))[8:-2]
210
- # get datatype from attribute definition, default to String
211
- data_type = {
212
- "int": "Integer",
213
- "float": "Numeric",
214
- "complex": "Numeric",
215
- "bool": "Boolean",
216
- }.get(dt, "String")
217
- # if the user has created a attr_info function, then check the annotation on the return from that for more information about this attribute
218
- if hasattr(cls, name + "_info"):
219
- func = getattr(cls, name + "_info")
220
- if callable(func):
221
- annotations = func.__annotations__["return"]
222
- if annotations is not None:
223
- if bool(annotations.get("ExcludeFromSettings")):
224
- # don't add this attribute to the settings list
225
- continue
226
- req = bool(annotations.get("IsRequired"))
227
- cat = annotations.get("Category", "Additional")
228
- desc = annotations.get("Description")
229
- dt = annotations.get("DataType")
230
- # only override DataType found
231
- if (dt is not None) and ("" != dt):
232
- data_type = {
233
- int: "Integer",
234
- float: "Number",
235
- complex: "Number",
236
- bool: "Boolean",
237
- str: "String",
238
- }.get(dt, str(dt))
239
- default = func()
240
- if default is not None:
241
- val = default
242
- # create list of information for this specific property
243
- info = []
244
- info.append(name) # Name
245
- info.append(data_type) # DataType
246
- info.append(val) # Default Value
247
- info.append(req) # Required
248
- info.append(cat) # Category
249
- info.append(desc) # Description
250
- # add this property to the list
251
- ret.append(info)
309
+ annotations = _type_hints_with_extras(cls)
310
+ members = dict(inspect.getmembers(cls))
311
+
312
+ names = [
313
+ name
314
+ for name, value in members.items()
315
+ if _is_setting_member(name, value)
316
+ ]
317
+ for name in annotations:
318
+ if (
319
+ name in names
320
+ or name.startswith("_")
321
+ or name in _EXCLUDED_SETTING_NAMES
322
+ ):
323
+ continue
324
+ value = members.get(name, _NO_VALUE)
325
+ if value is not _NO_VALUE and not _is_setting_member(name, value):
326
+ continue
327
+ names.append(name)
328
+
329
+ for name in names:
330
+ member_exists = name in members
331
+ val = members.get(name, "")
332
+ annotated_type, annotated_setting = _setting_from_annotation(
333
+ annotations.get(name)
334
+ )
335
+ value_setting = val if isinstance(val, Setting) else None
336
+ setting_info = value_setting or annotated_setting
337
+
338
+ if setting_info is not None and setting_info.exclude:
339
+ continue
340
+
341
+ req = bool(setting_info.required) if setting_info is not None else False
342
+ cat = setting_info.category if setting_info is not None else ""
343
+ desc = setting_info.description if setting_info is not None else ""
344
+ control = setting_info.control if setting_info is not None else ""
345
+
346
+ if value_setting is not None:
347
+ val = value_setting.default if value_setting.has_default else ""
348
+ elif (
349
+ not member_exists
350
+ and annotated_setting is not None
351
+ and annotated_setting.has_default
352
+ ):
353
+ val = annotated_setting.default
354
+ elif not member_exists:
355
+ val = ""
356
+
357
+ if isinstance(val, property) or (val is None):
358
+ val = ""
359
+
360
+ if setting_info is not None and setting_info.iris_type:
361
+ data_type = setting_info.iris_type
362
+ else:
363
+ data_type_source = (
364
+ setting_info.data_type
365
+ if setting_info is not None and setting_info.data_type
366
+ else annotated_type
367
+ )
368
+ data_type = _iris_data_type(data_type_source)
369
+ if data_type is None:
370
+ data_type = _iris_data_type(type(val)) or "String"
371
+
372
+ # Legacy attr_info() support. Values supplied here keep working
373
+ # and can also provide the new control/editor context field.
374
+ if hasattr(cls, name + "_info"):
375
+ func = getattr(cls, name + "_info")
376
+ if callable(func):
377
+ info_annotations = getattr(func, "__annotations__", {}).get(
378
+ "return"
379
+ )
380
+ if info_annotations is not None:
381
+ if bool(info_annotations.get("ExcludeFromSettings")):
382
+ continue
383
+ req = bool(info_annotations.get("IsRequired", req))
384
+ cat = _string_metadata(
385
+ info_annotations.get("Category", cat)
386
+ )
387
+ desc = _string_metadata(
388
+ info_annotations.get("Description", desc)
389
+ )
390
+ control = _string_metadata(
391
+ info_annotations.get(
392
+ "Control",
393
+ info_annotations.get("EditorContext", control),
394
+ )
395
+ )
396
+ dt = info_annotations.get("DataType")
397
+ if (dt is not None) and ("" != dt):
398
+ data_type = _iris_data_type(dt) or str(dt)
399
+ default = func()
400
+ if default is not None:
401
+ val = default
402
+
403
+ ret.append(
404
+ [
405
+ name, # Name
406
+ data_type, # DataType
407
+ val, # Default Value
408
+ req, # Required
409
+ _string_metadata(cat), # Category
410
+ _string_metadata(desc), # Description
411
+ _string_metadata(control), # Control/editor context
412
+ ]
413
+ )
252
414
  except Exception:
253
415
  pass
254
416
  return ret
@@ -95,7 +95,7 @@ class _LocalDirector(_DirectorProtocol):
95
95
  # ------------------------------------------------------------------
96
96
 
97
97
  def migrate(self, path: str) -> None:
98
- _Utils.migrate(path)
98
+ _Utils.migrate(path, mode="LOCAL", namespace=self.namespace)
99
99
 
100
100
  # ------------------------------------------------------------------
101
101
  # Metadata
@@ -0,0 +1,6 @@
1
+ class _PollingBusinessServiceMixin:
2
+ """Mixin for services polled by the default IRIS inbound adapter."""
3
+
4
+ @staticmethod
5
+ def get_adapter_type() -> str:
6
+ return "Ens.InboundAdapter"
@@ -0,0 +1,188 @@
1
+ from enum import Enum
2
+ from typing import Any
3
+
4
+
5
+ _MISSING = object()
6
+
7
+
8
+ class Category(str, Enum):
9
+ """Common IRIS production setting categories for Management Portal grouping."""
10
+
11
+ INFO = "Info"
12
+ BASIC = "Basic"
13
+ CONNECTION = "Connection"
14
+ ADDITIONAL = "Additional"
15
+ ALERTING = "Alerting"
16
+ DEV = "Dev"
17
+
18
+
19
+ def _string_value(value: Any) -> str:
20
+ if isinstance(value, Enum):
21
+ return str(value.value)
22
+ return str(value)
23
+
24
+
25
+ class Setting:
26
+ """Metadata for an IRIS production setting."""
27
+
28
+ def __init__(
29
+ self,
30
+ default: Any = _MISSING,
31
+ *,
32
+ data_type: Any = None,
33
+ iris_type: str | None = None,
34
+ category: str | Category | None = None,
35
+ required: bool = False,
36
+ description: str = "",
37
+ control: str | None = None,
38
+ exclude: bool = False,
39
+ ):
40
+ self.default = default
41
+ self.data_type = data_type
42
+ self.iris_type = iris_type
43
+ self.category = _string_value(category) if category is not None else None
44
+ self.required = required
45
+ self.description = description or ""
46
+ self.control = control or ""
47
+ self.exclude = exclude
48
+ self.name = ""
49
+
50
+ def __set_name__(self, owner, name):
51
+ self.name = name
52
+
53
+ def __get__(self, instance, owner=None):
54
+ if instance is None:
55
+ return self
56
+ if self.name in instance.__dict__:
57
+ return instance.__dict__[self.name]
58
+ if self.default is _MISSING:
59
+ return None
60
+ return self.default
61
+
62
+ def __set__(self, instance, value):
63
+ instance.__dict__[self.name] = value
64
+
65
+ @property
66
+ def has_default(self) -> bool:
67
+ return self.default is not _MISSING
68
+
69
+
70
+ def setting(default: Any = _MISSING, **kwargs) -> Setting:
71
+ return Setting(default, **kwargs)
72
+
73
+
74
+ class _Controls:
75
+ """Helpers for IRIS production setting editor controls."""
76
+
77
+ @staticmethod
78
+ def raw(value: str) -> str:
79
+ """Return an advanced IRIS editor context string unchanged."""
80
+ return value
81
+
82
+ @staticmethod
83
+ def selector(
84
+ context: str | None = None, *, multi_select: bool | None = None, **params
85
+ ) -> str:
86
+ """Build an IRIS selector editor context from a context search string."""
87
+ query = []
88
+ if multi_select is not None:
89
+ query.append(f"multiSelect={1 if multi_select else 0}")
90
+ if context:
91
+ if not (context.startswith("{") and context.endswith("}")):
92
+ context = "{" + context + "}"
93
+ query.append(f"context={context}")
94
+ for key, value in params.items():
95
+ if value is not None:
96
+ query.append(f"{key}={value}")
97
+ return "selector" + (f"?{'&'.join(query)}" if query else "")
98
+
99
+ @staticmethod
100
+ def production_item(
101
+ *,
102
+ targets: bool = True,
103
+ production_name: str = "@productionId",
104
+ multi_select: bool = False,
105
+ ) -> str:
106
+ """Select production items, normally target components, from a production."""
107
+ context = (
108
+ "Ens.ContextSearch/ProductionItems?"
109
+ f"targets={1 if targets else 0}&productionName={production_name}"
110
+ )
111
+ return _Controls.selector(context, multi_select=multi_select or None)
112
+
113
+ @staticmethod
114
+ def partner() -> str:
115
+ """Select a partner setting value."""
116
+ return "partnerSelector"
117
+
118
+ @staticmethod
119
+ def rule() -> str:
120
+ """Select an IRIS rule definition."""
121
+ return "ruleSelector"
122
+
123
+ @staticmethod
124
+ def credentials() -> str:
125
+ """Select an IRIS credentials entry."""
126
+ return "credentialsSelector"
127
+
128
+ @staticmethod
129
+ def directory() -> str:
130
+ """Select a directory path."""
131
+ return "directorySelector"
132
+
133
+ @staticmethod
134
+ def file() -> str:
135
+ """Select a file path."""
136
+ return "fileSelector"
137
+
138
+ @staticmethod
139
+ def dtl() -> str:
140
+ """Select an IRIS DTL data transformation."""
141
+ return "dtlSelector"
142
+
143
+ @staticmethod
144
+ def schedule() -> str:
145
+ """Select an IRIS schedule."""
146
+ return "scheduleSelector"
147
+
148
+ @staticmethod
149
+ def ssl_config() -> str:
150
+ """Select an IRIS SSL/TLS configuration."""
151
+ return "sslConfigSelector"
152
+
153
+ @staticmethod
154
+ def bpl() -> str:
155
+ """Select an IRIS BPL business process."""
156
+ return "bplSelector"
157
+
158
+ @staticmethod
159
+ def character_set() -> str:
160
+ """Select an IRIS character set."""
161
+ return _Controls.selector("Ens.ContextSearch/CharacterSets")
162
+
163
+ charset = character_set
164
+
165
+ @staticmethod
166
+ def framing(*, host: str = "@currHostId", prop: str = "Framing") -> str:
167
+ """Select a display-list value such as the current host framing option."""
168
+ return _Controls.selector(
169
+ f"Ens.ContextSearch/getDisplayList?host={host}&prop={prop}"
170
+ )
171
+
172
+ @staticmethod
173
+ def local_interface() -> str:
174
+ """Select a configured TCP local interface."""
175
+ return _Controls.selector("Ens.ContextSearch/TCPLocalInterfaces")
176
+
177
+ @staticmethod
178
+ def schema_category(host: str) -> str:
179
+ """Select a schema category for the specified host expression."""
180
+ return _Controls.selector(f"Ens.ContextSearch/SchemaCategories?host={host}")
181
+
182
+ @staticmethod
183
+ def search_table(host: str) -> str:
184
+ """Select a search table class for the specified host expression."""
185
+ return _Controls.selector(f"Ens.ContextSearch/SearchTableClasses?host={host}")
186
+
187
+
188
+ controls = _Controls()