iris-pex-embedded-python 4.0.1b1__py3-none-any.whl → 4.0.1b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
iop/__init__.py CHANGED
@@ -125,8 +125,8 @@ class BusinessService(_BusinessService):
125
125
  production graph.
126
126
 
127
127
  Lifecycle:
128
- IRIS calls on_process_input(); the default implementation delegates to
129
- on_process_input(request).
128
+ IRIS calls on_process_input(); the default implementation delegates the
129
+ incoming request to on_message(request).
130
130
 
131
131
  Best practices:
132
132
  Declare outbound routes with target() and wire them in a Production
@@ -20,16 +20,13 @@ def _call_process_input(method, request):
20
20
 
21
21
 
22
22
  class _BusinessService(_BusinessHost):
23
- """This class is responsible for receiving the data from external system and sending it to business processes or business operations in the production.
24
- The business service can use an adapter to access the external system, which is specified in the InboundAdapter property.
25
- There are three ways of implementing a business service:
26
- 1) Polling business service with an adapter - The production framework at regular intervals calls the adapter's task method,
27
- which sends incoming data to the business service process input method.
28
- 2) Polling business service that uses the default adapter - In this case, the framework calls the default adapter's OnTask method with no data.
29
- The on_process_input() method then performs the role of the adapter and is responsible for accessing the external system and receiving the data.
30
- 3) Nonpolling business service - The production framework does not initiate the business service. Instead custom code in either a long-running process
31
- or one that is started at regular intervals initiates the business service through the Director API.
32
- """
23
+ """Runtime base for inbound production entry points.
24
+
25
+ IRIS invokes on_process_input(...). By default, that hook delegates to
26
+ on_message(...). Application code should normally subclass iop.BusinessService
27
+ or iop.PollingBusinessService and wire outbound routes with target() in a
28
+ Production graph.
29
+ """
33
30
 
34
31
  Adapter = adapter = None
35
32
  _wait_for_next_call_interval = False
@@ -79,13 +76,12 @@ class _BusinessService(_BusinessHost):
79
76
  return None
80
77
 
81
78
  def on_process_input(self, message_input=None):
82
- """Receives the message from the inbond adapter via the PRocessInput method and is responsible for forwarding it to target business processes or operations.
83
- If the business service does not specify an adapter, then the default adapter calls this method with no message
84
- and the business service is responsible for receiving the data from the external system and validating it.
79
+ """Handle IRIS ProcessInput and delegate to on_message(message_input).
85
80
 
86
- Parameters:
87
- message_input: an instance of IRISObject or subclass of Message containing the data that the inbound adapter passes in.
88
- The message can have any structure agreed upon by the inbound adapter and the business service.
81
+ Override this low-level hook only when an inbound adapter or Director
82
+ call needs custom ProcessInput handling. For simple services, override
83
+ on_message(...); for scheduled polling, use PollingBusinessService and
84
+ override on_poll().
89
85
  """
90
86
  return self.on_message(message_input)
91
87
 
iop/messages/dispatch.py CHANGED
@@ -306,8 +306,9 @@ def get_handler_info(host: Any, method_name: str) -> tuple[str, str] | None:
306
306
  if len(params) != 1:
307
307
  return None
308
308
 
309
+ method = getattr(host, method_name)
309
310
  param: Parameter = next(iter(params.values()))
310
- annotation = param.annotation
311
+ annotation = _resolve_annotation(host, method, param.annotation)
311
312
 
312
313
  if annotation == Parameter.empty:
313
314
  return None
@@ -322,6 +323,42 @@ def get_handler_info(host: Any, method_name: str) -> tuple[str, str] | None:
322
323
  return None
323
324
 
324
325
 
326
+ def _resolve_annotation(host: Any, method: Callable, annotation: Any) -> Any:
327
+ if not isinstance(annotation, str):
328
+ return annotation
329
+
330
+ globalns = _annotation_globalns(method)
331
+ localns = _annotation_localns(host)
332
+
333
+ try:
334
+ resolved = eval(annotation, globalns, localns) # noqa: B307
335
+ except Exception:
336
+ resolved = annotation
337
+
338
+ if isinstance(resolved, str):
339
+ # Quoted postponed annotations evaluate to a string first.
340
+ try:
341
+ resolved = eval(resolved, globalns, localns) # noqa: B307
342
+ except Exception:
343
+ if "." in resolved:
344
+ return resolved
345
+ return Parameter.empty
346
+
347
+ return resolved
348
+
349
+
350
+ def _annotation_globalns(method: Callable) -> dict[str, Any]:
351
+ function = getattr(method, "__func__", method)
352
+ return getattr(function, "__globals__", {})
353
+
354
+
355
+ def _annotation_localns(host: Any) -> dict[str, Any]:
356
+ namespace: dict[str, Any] = {}
357
+ for klass in reversed(type(host).__mro__):
358
+ namespace.update(vars(klass))
359
+ return namespace
360
+
361
+
325
362
  def _message_class_name(message_type: Any) -> str | None:
326
363
  if isinstance(message_type, str):
327
364
  return message_type
iop/migration/utils.py CHANGED
@@ -193,8 +193,11 @@ def register_component(
193
193
  overwrite: int = 1,
194
194
  iris_classname: str = "Python",
195
195
  ):
196
- """
197
- It registers a component in the Iris database.
196
+ """Register a Python component as an IRIS proxy class.
197
+
198
+ Prefer a Python Production graph in PRODUCTIONS for new application
199
+ components. Use register_component() directly for standalone bindings,
200
+ legacy migration helpers, or manual proxy registration only.
198
201
 
199
202
  :param module: The name of the module that contains the class
200
203
  :type module: str
@@ -239,8 +242,11 @@ def bind_component(
239
242
  overwrite: int = 1,
240
243
  iris_classname: str = "Python",
241
244
  ):
242
- """
243
- Public alias for registering a Python component as an IRIS proxy class.
245
+ """Public alias for register_component().
246
+
247
+ Prefer a Python Production graph in PRODUCTIONS for new application
248
+ components. Use bind_component() directly only for standalone bindings,
249
+ legacy migration helpers, or manual proxy registration.
244
250
  """
245
251
  return register_component(module, classname, path, overwrite, iris_classname)
246
252
 
@@ -436,18 +442,12 @@ def migrate(
436
442
  namespace: str | None = None,
437
443
  strict_production_validation: bool = False,
438
444
  ):
439
- """
440
- Read the settings.py file and register all the components
441
- settings.py file has two dictionaries:
442
- * CLASSES
443
- * key: the name of the class
444
- * value: an instance of the class
445
- * PRODUCTIONS
446
- list of dictionaries:
447
- * key: the name of the production
448
- * value: a dictionary containing the settings for the production
449
- * SCHEMAS
450
- List of classes
445
+ """Read a migration file and apply its registrations to IRIS.
446
+
447
+ New Python-authored applications should normally export Production objects
448
+ through PRODUCTIONS. CLASSES remains available for standalone bindings,
449
+ native PersistentMessage classes, and legacy migration files. SCHEMAS
450
+ registers Message or PydanticMessage schemas for DTL support.
451
451
  """
452
452
  settings, path = _load_settings(filename)
453
453
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris_pex_embedded_python
3
- Version: 4.0.1b1
3
+ Version: 4.0.1b3
4
4
  Summary: Iris Interoperability based on Embedded Python
5
5
  Author-email: grongier <guillaume.rongier@intersystems.com>
6
6
  License: MIT License
@@ -69,14 +69,38 @@ For application repositories, start from the [reusable AGENTS.md template](https
69
69
 
70
70
  ## Example
71
71
 
72
- Here's a simple example of how a Business Operation can be implemented in Python:
72
+ Here's a tiny Python-authored production:
73
73
 
74
74
  ```python
75
- from iop import BusinessOperation
75
+ from dataclasses import dataclass
76
76
 
77
- class MyBo(BusinessOperation):
78
- def on_message(self, request):
79
- self.log_info("Hello World")
77
+ from iop import BusinessOperation, Message, PollingBusinessService, Production, target
78
+
79
+
80
+ @dataclass
81
+ class HelloRequest(Message):
82
+ text: str = "Hello World"
83
+
84
+
85
+ class HelloService(PollingBusinessService):
86
+ Output = target()
87
+
88
+ def on_poll(self):
89
+ self.send_request_async(self.Output, HelloRequest())
90
+
91
+
92
+ class HelloOperation(BusinessOperation):
93
+ def on_message(self, request: HelloRequest):
94
+ self.log_info(request.text)
95
+ return request
96
+
97
+
98
+ prod = Production("HelloWorld.Production", testing_enabled=True)
99
+ service = prod.service("HelloService", HelloService)
100
+ operation = prod.operation("HelloOperation", HelloOperation)
101
+ prod.connect(service.Output, operation)
102
+
103
+ PRODUCTIONS = [prod]
80
104
  ```
81
105
 
82
106
  ## Installation
@@ -1,4 +1,4 @@
1
- iop/__init__.py,sha256=4QGYNWs4t-Zt4-WfjHXmKRosWS5jZ5A27vl2hiObCVc,10621
1
+ iop/__init__.py,sha256=bnhSiC7XcOIiS7e51uY8fr9Li7RYjhCnlCyVDzZI6-I,10636
2
2
  iop/__main__.py,sha256=2rUNM7vaYK0W57Jj72rm7GORyIFLMXKwPq3QNLwYBmY,100
3
3
  iop/cli/__init__.py,sha256=Y78pBdA-ULzq1c_9xQZsFBlzm3x5xgxSzkvrH5DoM7I,31
4
4
  iop/cli/formatting.py,sha256=OLgHA1d8_paAuINQyKkYgihWMLoBhPn7kSXW5whRlIs,1606
@@ -39,7 +39,7 @@ iop/components/async_request.py,sha256=AU36pf6NIQMM9ZYR5h6LeQry0EfXXqDaKAjsP16_h
39
39
  iop/components/business_host.py,sha256=5EXwzZzfEVYLFlzi519GXKclTz2m5FMcqtl10lLzUyw,12089
40
40
  iop/components/business_operation.py,sha256=KycRNz-zuyJs2VneTBVcppyd3rKLekrISjbubCbzHxY,2905
41
41
  iop/components/business_process.py,sha256=1xTYwqhvJaXC-BvQ5WoGOud5QwxT9EPpvDPDX4zThXg,9839
42
- iop/components/business_service.py,sha256=fwSdHGFuC55_qSjFBpW-cLZmrK3Cm8Yxgk1KvxuQv2Y,4718
42
+ iop/components/business_service.py,sha256=Bp47bJzTWHp66kp0Zo_D-blOKHzj4i6kXu-lCRMMzfg,3597
43
43
  iop/components/common.py,sha256=tsP2APRBnks1hVazS9dX0Q8KCoANbUsnaFTlzqOIkVY,19063
44
44
  iop/components/debugpy.py,sha256=sNFKaF9MtWBNAqxH4ubSIVqjsIpsHGjRK2jzBU-O59Y,6011
45
45
  iop/components/generator_request.py,sha256=NOllwJnY06kk_eC7cQXDQ8NBFKfQakyz3A_AgYJ5f5Y,1351
@@ -53,7 +53,7 @@ iop/components/settings.py,sha256=USAKAisdersKlAEIk8HDu_TuYtkO-Sc3an3PXjPcDRA,62
53
53
  iop/messages/__init__.py,sha256=k1AKplpjdqSxM5Gl7bpWAfw93xUgigovlI4SKhwKHRI,355
54
54
  iop/messages/base.py,sha256=ZrVXEz7JrYHnt5b3M7avxcJCB0_OZxm1uWm9gQ4_eAw,1906
55
55
  iop/messages/decorators.py,sha256=CWk08k8U2aTJv7Dx1n2jp8sjZZSCD8NJPoifKLB3W2g,2488
56
- iop/messages/dispatch.py,sha256=Mgz0OHTQDU02Z8qPxl_lks-nlAvW6ADcJkTS4faYE-Q,10459
56
+ iop/messages/dispatch.py,sha256=wfoKyaMOcJTzMEAE5hEHYEt3aiRsaUR_TYmROT2fDrg,11592
57
57
  iop/messages/persistent.py,sha256=ibRhRTqzwGcAqem744KZzHiouUlc8-J3ftB4zyzQYx4,16906
58
58
  iop/messages/serialization.py,sha256=BW0o-7GaceMW7RMOzOJ8g9rKEv28NYnYFwtTLmAf7Yg,9255
59
59
  iop/messages/validation.py,sha256=X6MSWsjLZDuyX1Tfx0TP3T6OI4nj1HZccwR2Z9PSubY,1606
@@ -61,7 +61,7 @@ iop/migration/__init__.py,sha256=wp-RvjLKYW_Pi2AJLfiWe1bACxENzEgjXtP9UMNiSVY,36
61
61
  iop/migration/io.py,sha256=hAehtApFZFgGzjzgwBmDMxdA1xoRhE-okOcEzvgFdQ4,1822
62
62
  iop/migration/manifest.py,sha256=Bs4eC1_i-ZnovlgSW9FKu5WBwy_6Zwy5ZAcsi5A1szU,19351
63
63
  iop/migration/plans.py,sha256=5kx3aqQiPDJ3F8hU9pp9_mOO1_N6GA0Ps7--G1Iv6z8,2549
64
- iop/migration/utils.py,sha256=qJrp-Q3WjYxtzwTu7TqQppjUvQSYCUN-RJ5KPAu4M9M,37880
64
+ iop/migration/utils.py,sha256=pOojLPMZes05KXrGN2ap45ffppP5Z4tS4bjJ0gSD5Oc,38183
65
65
  iop/production/__init__.py,sha256=95VmJgC3lWtHpUJijLqyKKxFZa65OcRni8803kb4aHE,2148
66
66
  iop/production/actions.py,sha256=TLVAjPTPq21oLj9c-IG10qMNJ2hvPzueviMy_c1a0ZU,17199
67
67
  iop/production/common.py,sha256=IoUg9jkFzSQwDsHcXvkVAC4qxpWKs3kTfXFm_EP07EU,3091
@@ -91,9 +91,9 @@ iop/runtime/remote/director.py,sha256=bkie751GrYLgGs7ApRCowHpSnJA8KupxCOrFDQmjyO
91
91
  iop/runtime/remote/migration.py,sha256=Iboz-Ye7i9xif9bn433GazCF-gtD-HJW3wiUwWS5P6w,1831
92
92
  iop/runtime/remote/settings.py,sha256=P1Hsgld5o57HpSZgPVZzobyjlUmxJj6bstiWvR0JXnc,2144
93
93
  iop/runtime/remote/setup.py,sha256=Tr80F3JJTy7oSUso2jyx0iA_KZayd4MYt9vL0BvPpMA,2600
94
- iris_pex_embedded_python-4.0.1b1.dist-info/licenses/LICENSE,sha256=rZSiBFId_sfbJ6RL0GjjPX-InNLkNS9ou7eQsikciI8,1089
95
- iris_pex_embedded_python-4.0.1b1.dist-info/METADATA,sha256=wiQ5-SOMCjBKmcfgxkbkpCVsMM4bW9SgdRCaN--_PGM,5283
96
- iris_pex_embedded_python-4.0.1b1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
97
- iris_pex_embedded_python-4.0.1b1.dist-info/entry_points.txt,sha256=fzS8ZFsPe8cnpEx208X60DNugCPQmuSloPeg4pyyJDY,42
98
- iris_pex_embedded_python-4.0.1b1.dist-info/top_level.txt,sha256=BM54-OXQ6l2BDtmesWNXckh033s9gcjoO7bfjbwZbxU,4
99
- iris_pex_embedded_python-4.0.1b1.dist-info/RECORD,,
94
+ iris_pex_embedded_python-4.0.1b3.dist-info/licenses/LICENSE,sha256=rZSiBFId_sfbJ6RL0GjjPX-InNLkNS9ou7eQsikciI8,1089
95
+ iris_pex_embedded_python-4.0.1b3.dist-info/METADATA,sha256=UFR3F4n2-7iQJSkQ0WLBQYtmNaBwO08z7gZLl-q-_Hg,5846
96
+ iris_pex_embedded_python-4.0.1b3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
97
+ iris_pex_embedded_python-4.0.1b3.dist-info/entry_points.txt,sha256=fzS8ZFsPe8cnpEx208X60DNugCPQmuSloPeg4pyyJDY,42
98
+ iris_pex_embedded_python-4.0.1b3.dist-info/top_level.txt,sha256=BM54-OXQ6l2BDtmesWNXckh033s9gcjoO7bfjbwZbxU,4
99
+ iris_pex_embedded_python-4.0.1b3.dist-info/RECORD,,