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
@@ -1,27 +1,23 @@
1
- import abc
2
- import inspect
3
- import traceback
1
+ import inspect
2
+ import traceback
3
+ import warnings
4
4
  from enum import Enum
5
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
- )
18
-
19
- from . import _iris
20
- from ._debugpy import debugpython
21
- from ._log_manager import LogManager, logging
22
- from ._settings import Setting
23
-
24
-
6
+ from typing import (
7
+ Annotated,
8
+ Any,
9
+ ClassVar,
10
+ Union,
11
+ get_args,
12
+ get_origin,
13
+ get_type_hints,
14
+ )
15
+
16
+ from ..runtime import iris as _iris
17
+ from .debugpy import debugpython
18
+ from .log_manager import LogManager, logging
19
+ from .settings import Setting
20
+
25
21
  _NO_VALUE = object()
26
22
 
27
23
  _EXCLUDED_SETTING_NAMES = {
@@ -88,7 +84,7 @@ def _unwrap_annotated(data_type: Any) -> tuple[Any, tuple[Any, ...]]:
88
84
  return data_type, ()
89
85
 
90
86
 
91
- def _setting_from_annotation(data_type: Any) -> tuple[Any, Optional[Setting]]:
87
+ def _setting_from_annotation(data_type: Any) -> tuple[Any, Setting | None]:
92
88
  data_type, metadata = _unwrap_annotated(data_type)
93
89
  setting = None
94
90
  for item in metadata:
@@ -106,7 +102,7 @@ def _unwrap_optional(data_type: Any) -> Any:
106
102
  return data_type
107
103
 
108
104
 
109
- def _iris_data_type(data_type: Any) -> Optional[str]:
105
+ def _iris_data_type(data_type: Any) -> str | None:
110
106
  if data_type is None or data_type == "":
111
107
  return None
112
108
 
@@ -138,9 +134,18 @@ def _is_setting_member(name: str, value: Any) -> bool:
138
134
  return not (
139
135
  inspect.ismethod(value) or inspect.isfunction(value) or inspect.isclass(value)
140
136
  )
141
-
142
-
143
- class _Common(metaclass=abc.ABCMeta):
137
+
138
+
139
+ def _custom_init_owner(cls: type) -> type | None:
140
+ for base in inspect.getmro(cls):
141
+ init = base.__dict__.get("__init__", _NO_VALUE)
142
+ if init is _NO_VALUE or init is object.__init__:
143
+ continue
144
+ return base
145
+ return None
146
+
147
+
148
+ class _Common:
144
149
  """Base class that defines common methods for all component types.
145
150
 
146
151
  Provides core functionality like initialization, teardown, connection handling
@@ -149,13 +154,52 @@ class _Common(metaclass=abc.ABCMeta):
149
154
 
150
155
  INFO_URL: ClassVar[str]
151
156
  ICON_URL: ClassVar[str]
152
- iris_handle: Any = None
153
- _log_to_console: bool = False
154
- _logger: Optional[logging.Logger] = None
155
-
156
- @staticmethod
157
- def get_adapter_type() -> Optional[str]:
158
- """Get the adapter type for this component. Override in subclasses."""
157
+ iris_handle: Any = None
158
+ _log_to_console: bool = False
159
+ _logger: logging.Logger | None = None
160
+
161
+ def __init_subclass__(cls, **kwargs: Any) -> None:
162
+ super().__init_subclass__(**kwargs)
163
+ cls._warn_if_custom_init_defined(stacklevel=2)
164
+
165
+ @classmethod
166
+ def _custom_init_warning_message(cls) -> str | None:
167
+ if _custom_init_owner(cls) is None:
168
+ return None
169
+ classname = f"{cls.__module__}.{cls.__qualname__}"
170
+ return (
171
+ f"{classname} defines or inherits __init__(), but IoP/IRIS "
172
+ "instantiates components with __new__() and does not call "
173
+ "__init__(). Move startup logic to on_init(); use class attributes "
174
+ "or iop.Setting for configurable defaults."
175
+ )
176
+
177
+ @classmethod
178
+ def _warn_if_custom_init_defined(cls, stacklevel: int = 2) -> None:
179
+ message = cls._custom_init_warning_message()
180
+ if message is None:
181
+ return
182
+ warnings.warn(message, RuntimeWarning, stacklevel=stacklevel)
183
+
184
+ def _log_custom_init_warning(self) -> None:
185
+ message = self.__class__._custom_init_warning_message()
186
+ if message is None:
187
+ return
188
+ try:
189
+ self.log_warning(message)
190
+ except Exception:
191
+ try:
192
+ warnings.warn(message, RuntimeWarning, stacklevel=2)
193
+ except Exception:
194
+ pass
195
+
196
+ def _warn_if_custom_init(self) -> None:
197
+ """Metadata-safe warning hook for ObjectScript __new__ allocations."""
198
+ self._log_custom_init_warning()
199
+
200
+ @staticmethod
201
+ def get_adapter_type() -> str | None:
202
+ """Get the adapter type for this component. Override in subclasses."""
159
203
  return None
160
204
 
161
205
  @property
@@ -179,10 +223,36 @@ class _Common(metaclass=abc.ABCMeta):
179
223
  self._log_to_console = value
180
224
  self.logger = LogManager.get_logger(self.__class__.__name__, value)
181
225
 
182
- # Lifecycle methods
183
- def on_init(self) -> None:
184
- """Initialize component when started."""
185
- pass
226
+ # Lifecycle methods
227
+ def on_init(self) -> None:
228
+ """Purpose:
229
+ Component startup hook.
230
+
231
+ Use when:
232
+ A component needs to initialize clients, caches, credentials,
233
+ counters, or other runtime state after IRIS creates the host.
234
+
235
+ Lifecycle:
236
+ IoP calls on_init() from the internal dispatch startup path. IRIS
237
+ may allocate the Python object without calling __init__().
238
+
239
+ Best practices:
240
+ Keep initialization idempotent and quick. Read production settings
241
+ from self inside this method.
242
+
243
+ Common mistakes:
244
+ Do not put component startup work in __init__(); it may not run in
245
+ the IRIS component lifecycle.
246
+
247
+ Minimal example:
248
+ def on_init(self):
249
+ self.client = make_client(self.Endpoint)
250
+
251
+ Related:
252
+ docs/ai-coding.md,
253
+ docs/cookbooks/index.md
254
+ """
255
+ pass
186
256
 
187
257
  def on_tear_down(self) -> None:
188
258
  """Clean up component before termination."""
@@ -196,9 +266,10 @@ class _Common(metaclass=abc.ABCMeta):
196
266
  def _dispatch_on_connected(self, host_object: Any) -> None:
197
267
  self.on_connected()
198
268
 
199
- def _dispatch_on_init(self, host_object: Any) -> None:
200
- """Initialize component when started."""
201
- self.on_init()
269
+ def _dispatch_on_init(self, host_object: Any) -> None:
270
+ """Initialize component when started."""
271
+ self._log_custom_init_warning()
272
+ self.on_init()
202
273
 
203
274
  def _dispatch_on_tear_down(self, host_object: Any) -> None:
204
275
  self.on_tear_down()
@@ -214,7 +285,7 @@ class _Common(metaclass=abc.ABCMeta):
214
285
 
215
286
  # Component information methods
216
287
  @classmethod
217
- def _get_info(cls) -> List[str]:
288
+ def _get_info(cls) -> list[str]:
218
289
  """Get component configuration information.
219
290
 
220
291
  Returns information used to display in Production config UI including:
@@ -231,37 +302,26 @@ class _Common(metaclass=abc.ABCMeta):
231
302
  super_class = ""
232
303
  adapter = ""
233
304
  try:
234
- # Get tuple of the class's base classes and loop through them until we find one of the PEX component classes
235
- classes = inspect.getmro(cls)
236
- for cl in classes:
237
- classname = str(cl)[7:-1]
305
+ # Get tuple of the class's base classes and loop through them until we find one of the public component classes.
306
+ classes = inspect.getmro(cls)
307
+ for cl in classes:
308
+ classname = str(cl)[7:-1]
238
309
  if classname in [
239
310
  "'iop.BusinessService'",
240
- "'iop.BusinessOperation'",
241
- "'iop.DuplexOperation'",
242
- "'iop.DuplexService'",
243
- "'grongier.pex.BusinessService'",
244
- "'grongier.pex.BusinessOperation'",
245
- "'grongier.pex.DuplexOperation'",
246
- "'grongier.pex.DuplexService'",
247
- ]:
248
- # Remove the apostrophes and set as super_class, then find if it uses an adapter
249
- super_class = classname[1:-1]
250
- adapter = cls.get_adapter_type()
251
- if adapter is None:
252
- # for retro-compatibility
253
- adapter = cls.getAdapterType() # type: ignore
254
- break
255
- elif classname in [
256
- "'iop.BusinessProcess'",
257
- "'iop.DuplexProcess'",
258
- "'iop.InboundAdapter'",
259
- "'iop.OutboundAdapter'",
260
- "'grongier.pex.BusinessProcess'",
261
- "'grongier.pex.DuplexProcess'",
262
- "'grongier.pex.InboundAdapter'",
263
- "'grongier.pex.OutboundAdapter'",
264
- ]:
311
+ "'iop.BusinessOperation'",
312
+ "'iop.DuplexOperation'",
313
+ "'iop.DuplexService'",
314
+ ]:
315
+ # Remove the apostrophes and set as super_class, then find if it uses an adapter
316
+ super_class = classname[1:-1]
317
+ adapter = cls.get_adapter_type() or ""
318
+ break
319
+ elif classname in [
320
+ "'iop.BusinessProcess'",
321
+ "'iop.DuplexProcess'",
322
+ "'iop.InboundAdapter'",
323
+ "'iop.OutboundAdapter'",
324
+ ]:
265
325
  # Remove the apostrophes and set as super_class
266
326
  super_class = classname[1:-1]
267
327
  break
@@ -290,7 +350,7 @@ class _Common(metaclass=abc.ABCMeta):
290
350
  return ret
291
351
 
292
352
  @classmethod
293
- def _get_properties(cls) -> List[List[Any]]:
353
+ def _get_properties(cls) -> list[list[Any]]:
294
354
  """Get component properties for Production configuration.
295
355
 
296
356
  Returns list of property definitions containing:
@@ -304,8 +364,8 @@ class _Common(metaclass=abc.ABCMeta):
304
364
 
305
365
  Only includes non-private class attributes and properties.
306
366
  """
307
- ret = []
308
- try:
367
+ ret = []
368
+ try:
309
369
  annotations = _type_hints_with_extras(cls)
310
370
  members = dict(inspect.getmembers(cls))
311
371
 
@@ -411,12 +471,15 @@ class _Common(metaclass=abc.ABCMeta):
411
471
  _string_metadata(control), # Control/editor context
412
472
  ]
413
473
  )
414
- except Exception:
415
- pass
416
- return ret
474
+ except Exception as exc:
475
+ raise RuntimeError(
476
+ f"Failed to build settings metadata for "
477
+ f"{cls.__module__}.{cls.__qualname__}"
478
+ ) from exc
479
+ return ret
417
480
 
418
481
  # Logging methods
419
- def _log(self) -> Tuple[str, Optional[str]]:
482
+ def _log(self) -> tuple[str, str | None]:
420
483
  """Get class and method name for logging.
421
484
 
422
485
  Returns:
@@ -432,7 +495,7 @@ class _Common(metaclass=abc.ABCMeta):
432
495
  return current_class, current_method
433
496
 
434
497
  def _logging(
435
- self, message: str, level: int, to_console: Optional[bool] = None
498
+ self, message: str, level: int, to_console: bool | None = None
436
499
  ) -> None:
437
500
  """Write log entry.
438
501
 
@@ -441,56 +504,20 @@ class _Common(metaclass=abc.ABCMeta):
441
504
  level: Log level
442
505
  to_console: If True, log to console instead of IRIS
443
506
  """
444
- current_class, current_method = self._log()
445
- if to_console is None:
446
- to_console = self.log_to_console
447
- if level == logging.DEBUG:
448
- self.logger.debug(
449
- message,
450
- extra={
451
- "to_console": to_console,
452
- "class_name": current_class,
453
- "method_name": current_method,
454
- },
455
- )
456
- elif level == logging.INFO:
457
- self.logger.info(
458
- message,
459
- extra={
460
- "to_console": to_console,
461
- "class_name": current_class,
462
- "method_name": current_method,
463
- },
464
- )
465
- elif level == logging.CRITICAL:
466
- self.logger.critical(
467
- message,
468
- extra={
469
- "to_console": to_console,
470
- "class_name": current_class,
471
- "method_name": current_method,
472
- },
473
- )
474
- elif level == logging.WARNING:
475
- self.logger.warning(
476
- message,
477
- extra={
478
- "to_console": to_console,
479
- "class_name": current_class,
480
- "method_name": current_method,
481
- },
482
- )
483
- elif level == logging.ERROR:
484
- self.logger.error(
485
- message,
486
- extra={
487
- "to_console": to_console,
488
- "class_name": current_class,
489
- "method_name": current_method,
490
- },
491
- )
492
-
493
- def trace(self, message: str, to_console: Optional[bool] = None) -> None:
507
+ current_class, current_method = self._log()
508
+ if to_console is None:
509
+ to_console = self.log_to_console
510
+ self.logger.log(
511
+ level,
512
+ message,
513
+ extra={
514
+ "to_console": to_console,
515
+ "class_name": current_class,
516
+ "method_name": current_method,
517
+ },
518
+ )
519
+
520
+ def trace(self, message: str, to_console: bool | None = None) -> None:
494
521
  """Write trace log entry.
495
522
 
496
523
  Args:
@@ -499,7 +526,7 @@ class _Common(metaclass=abc.ABCMeta):
499
526
  """
500
527
  self._logging(message, logging.DEBUG, to_console)
501
528
 
502
- def log_info(self, message: str, to_console: Optional[bool] = None) -> None:
529
+ def log_info(self, message: str, to_console: bool | None = None) -> None:
503
530
  """Write info log entry.
504
531
 
505
532
  Args:
@@ -508,7 +535,7 @@ class _Common(metaclass=abc.ABCMeta):
508
535
  """
509
536
  self._logging(message, logging.INFO, to_console)
510
537
 
511
- def log_alert(self, message: str, to_console: Optional[bool] = None) -> None:
538
+ def log_alert(self, message: str, to_console: bool | None = None) -> None:
512
539
  """Write alert log entry.
513
540
 
514
541
  Args:
@@ -517,7 +544,7 @@ class _Common(metaclass=abc.ABCMeta):
517
544
  """
518
545
  self._logging(message, logging.CRITICAL, to_console)
519
546
 
520
- def log_warning(self, message: str, to_console: Optional[bool] = None) -> None:
547
+ def log_warning(self, message: str, to_console: bool | None = None) -> None:
521
548
  """Write warning log entry.
522
549
 
523
550
  Args:
@@ -526,7 +553,7 @@ class _Common(metaclass=abc.ABCMeta):
526
553
  """
527
554
  self._logging(message, logging.WARNING, to_console)
528
555
 
529
- def log_error(self, message: str, to_console: Optional[bool] = None) -> None:
556
+ def log_error(self, message: str, to_console: bool | None = None) -> None:
530
557
  """Write error log entry.
531
558
 
532
559
  Args:
@@ -545,34 +572,3 @@ class _Common(metaclass=abc.ABCMeta):
545
572
  current_class, current_method = self._log()
546
573
  iris.cls("Ens.Util.Log").LogAssert(current_class, current_method, message)
547
574
 
548
- def LOGINFO(self, message: str) -> None:
549
- """DEPRECATED: Use log_info."""
550
- return self.log_info(message=message)
551
-
552
- def LOGALERT(self, message: str) -> None:
553
- """DEPRECATED: Use log_alert."""
554
- return self.log_alert(message)
555
-
556
- def LOGWARNING(self, message: str) -> None:
557
- """DEPRECATED: Use log_warning."""
558
- return self.log_warning(message)
559
-
560
- def LOGERROR(self, message: str) -> None:
561
- """DEPRECATED: Use log_error."""
562
- return self.log_error(message)
563
-
564
- def LOGASSERT(self, message: str) -> None:
565
- """DEPRECATED: Use log_assert."""
566
- return self.log_assert(message)
567
-
568
- def OnInit(self) -> None:
569
- """DEPRECATED: Use on_init."""
570
- return
571
-
572
- def OnTearDown(self) -> None:
573
- """DEPRECATED: Use on_tear_down."""
574
- return
575
-
576
- def OnConnected(self) -> None:
577
- """DEPRECATED: Use on_connected."""
578
- return
@@ -1,14 +1,14 @@
1
- import threading
2
- import time
3
1
  import contextlib
4
- import socket
5
2
  import os
3
+ import socket
6
4
  import sys
5
+ import threading
6
+ import time
7
7
  from contextlib import closing
8
- from typing import Optional, cast, Any, Dict
8
+ from typing import Any, cast
9
9
 
10
10
 
11
- def find_free_port(start: Optional[int] = None, end: Optional[int] = None) -> int:
11
+ def find_free_port(start: int | None = None, end: int | None = None) -> int:
12
12
  port = start
13
13
  if port is None:
14
14
  port = 0
@@ -42,7 +42,7 @@ def is_debugpy_installed() -> bool:
42
42
  return False
43
43
 
44
44
 
45
- def _get_python_interpreter_path(install_dir: Optional[str]) -> Optional[str]:
45
+ def _get_python_interpreter_path(install_dir: str | None) -> str | None:
46
46
  """Get the path to the Python interpreter."""
47
47
  if not install_dir:
48
48
  return None
@@ -53,12 +53,12 @@ def _get_python_interpreter_path(install_dir: Optional[str]) -> Optional[str]:
53
53
  return python_path if os.path.exists(python_path) else None
54
54
 
55
55
 
56
- def _get_debugpy_config(python_path: str) -> Dict[str, str]:
56
+ def _get_debugpy_config(python_path: str) -> dict[str, str]:
57
57
  """Get the debugpy configuration."""
58
58
  return {"python": python_path}
59
59
 
60
60
 
61
- def configure_debugpy(self, python_path: Optional[str] = None) -> bool:
61
+ def configure_debugpy(self, python_path: str | None = None) -> bool:
62
62
  """Configure debugpy with the appropriate Python interpreter."""
63
63
  import debugpy
64
64
 
@@ -116,7 +116,7 @@ def debugpython(self, host_object: Any) -> None:
116
116
  # hack to set __file__ for os module in debugpy
117
117
  # This is a workaround for the issue where debugpy cannot find the __file__ attribute of the os module.
118
118
  if not hasattr(os, "__file__"):
119
- setattr(os, "__file__", __file__)
119
+ os.__file__ = __file__
120
120
 
121
121
  if host_object is None:
122
122
  self.log_alert("No host object found, cannot enable debugpy.")
@@ -164,7 +164,7 @@ def debugpython(self, host_object: Any) -> None:
164
164
  def debugpy_in_iris(iris_dir, port) -> bool:
165
165
 
166
166
  if not hasattr(os, "__file__"):
167
- setattr(os, "__file__", __file__)
167
+ os.__file__ = __file__
168
168
 
169
169
  if not is_debugpy_installed():
170
170
  print("debugpy is not installed.")
@@ -195,7 +195,7 @@ def debugpy_in_iris(iris_dir, port) -> bool:
195
195
  return False
196
196
 
197
197
 
198
- def mgr_dir_to_install_dir(mgr_dir: str) -> Optional[str]:
198
+ def mgr_dir_to_install_dir(mgr_dir: str) -> str | None:
199
199
  """Convert manager directory to install directory."""
200
200
  import os
201
201
 
@@ -1,7 +1,7 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
- from . import _iris
4
- from ._dispatch import dispatch_serializer
3
+ from ..messages.dispatch import dispatch_serializer
4
+ from ..runtime import iris as _iris
5
5
 
6
6
 
7
7
  class _GeneratorRequest:
@@ -14,7 +14,7 @@ class _GeneratorRequest:
14
14
  target: str,
15
15
  request: Any,
16
16
  timeout: int = -1,
17
- description: Optional[str] = None,
17
+ description: str | None = None,
18
18
  ) -> None:
19
19
  self.host = host
20
20
  self.target = target
@@ -1,4 +1,4 @@
1
- from ._common import _Common
1
+ from .common import _Common
2
2
 
3
3
 
4
4
  class _InboundAdapter(_Common):
@@ -8,12 +8,12 @@ class _InboundAdapter(_Common):
8
8
 
9
9
  BusinessHost = business_host = business_host_python = None
10
10
 
11
- def on_task(self):
12
- """Called by the production framework at intervals determined by the business service CallInterval property.
13
- It is responsible for receiving the data from the external system, validating the data, and sending it in a message to the business service OnProcessInput method.
14
- The message can have any structure agreed upon by the inbound adapter and the business service.
15
- """
16
- return self.OnTask()
11
+ def on_task(self):
12
+ """Called by the production framework at intervals determined by the business service CallInterval property.
13
+ It is responsible for receiving the data from the external system, validating the data, and sending it in a message to the business service.
14
+ The message can have any structure agreed upon by the inbound adapter and the business service.
15
+ """
16
+ return None
17
17
 
18
18
  def _set_iris_handles(self, handle_current, handle_partner):
19
19
  """For internal use only."""
@@ -22,14 +22,6 @@ class _InboundAdapter(_Common):
22
22
  self.business_host = handle_partner
23
23
  try:
24
24
  self.business_host_python = handle_partner.GetClass()
25
- except Exception:
26
- pass
27
- return
28
-
29
- def OnTask(self):
30
- """DEPRECATED : use on_task
31
- Called by the production framework at intervals determined by the business service CallInterval property.
32
- It is responsible for receiving the data from the external system, validating the data, and sending it in a message to the business service OnProcessInput method.
33
- The message can have any structure agreed upon by the inbound adapter and the business service.
34
- """
35
- return
25
+ except Exception:
26
+ pass
27
+ return
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from . import _iris
3
+ from ..runtime import iris as _iris
4
4
 
5
5
 
6
6
  class LogManager:
@@ -1,4 +1,4 @@
1
- from ._common import _Common
1
+ from .common import _Common
2
2
 
3
3
 
4
4
  class _OutboundAdapter(_Common):
@@ -0,0 +1,48 @@
1
+ import warnings
2
+
3
+
4
+ class _PollingBusinessServiceMixin:
5
+ """Mixin for services polled by the default IRIS inbound adapter."""
6
+
7
+ @staticmethod
8
+ def get_adapter_type() -> str:
9
+ return "Ens.InboundAdapter"
10
+
11
+ def on_poll(self):
12
+ """Purpose:
13
+ Run one scheduled polling cycle.
14
+
15
+ Use when:
16
+ A PollingBusinessService fetches or discovers new work from Python.
17
+
18
+ Lifecycle:
19
+ The default IRIS inbound adapter calls on_process_input(), and this
20
+ mixin delegates that call to on_poll().
21
+
22
+ Best practices:
23
+ Do a bounded unit of work, then return. Send produced messages with
24
+ send_request_async(self.Output, message).
25
+
26
+ Common mistakes:
27
+ Do not create an infinite loop inside on_poll(); use the production
28
+ schedule or call interval for repeated execution.
29
+
30
+ Minimal example:
31
+ def on_poll(self):
32
+ self.send_request_async(self.Output, PollRequest())
33
+
34
+ Related:
35
+ docs/cookbooks/add-polling-service.md
36
+ """
37
+ warnings.warn(
38
+ f"{self.__class__.__name__} did not override on_poll() or "
39
+ "on_process_input(); the scheduled poll was ignored. "
40
+ "This default no-op handler will raise NotImplementedError in v5.0.",
41
+ RuntimeWarning,
42
+ stacklevel=2,
43
+ )
44
+ return None
45
+
46
+ def on_process_input(self, message_input=None):
47
+ """Compatibility hook called by IRIS ProcessInput."""
48
+ return self.on_poll()