iris-pex-embedded-python 3.4.0b14__py3-none-any.whl → 3.4.1__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.

Potentially problematic release.


This version of iris-pex-embedded-python might be problematic. Click here for more details.

Files changed (86) hide show
  1. iop/_async_request.py +3 -1
  2. iop/_business_host.py +2 -1
  3. iop/_business_service.py +6 -0
  4. iop/_cli.py +9 -1
  5. iop/_common.py +11 -1
  6. iop/_debugpy.py +145 -0
  7. iop/_director.py +40 -40
  8. iop/_iris.py +7 -0
  9. iop/_log_manager.py +9 -9
  10. iop/_serialization.py +27 -19
  11. iop/_utils.py +10 -10
  12. iop/cls/IOP/Common.cls +3 -1
  13. iop/cls/IOP/Utils.cls +59 -9
  14. {iris_pex_embedded_python-3.4.0b14.dist-info → iris_pex_embedded_python-3.4.1.dist-info}/METADATA +2 -1
  15. iris_pex_embedded_python-3.4.1.dist-info/RECORD +80 -0
  16. {iris_pex_embedded_python-3.4.0b14.dist-info → iris_pex_embedded_python-3.4.1.dist-info}/WHEEL +1 -1
  17. iris_pex_embedded_python-3.4.1.dist-info/top_level.txt +2 -0
  18. intersystems_iris/_BufferReader.py +0 -10
  19. intersystems_iris/_BufferWriter.py +0 -32
  20. intersystems_iris/_ConnectionInformation.py +0 -56
  21. intersystems_iris/_ConnectionParameters.py +0 -18
  22. intersystems_iris/_Constant.py +0 -38
  23. intersystems_iris/_DBList.py +0 -506
  24. intersystems_iris/_Device.py +0 -69
  25. intersystems_iris/_GatewayContext.py +0 -25
  26. intersystems_iris/_GatewayException.py +0 -4
  27. intersystems_iris/_GatewayUtility.py +0 -74
  28. intersystems_iris/_IRIS.py +0 -1294
  29. intersystems_iris/_IRISConnection.py +0 -516
  30. intersystems_iris/_IRISEmbedded.py +0 -85
  31. intersystems_iris/_IRISGlobalNode.py +0 -273
  32. intersystems_iris/_IRISGlobalNodeView.py +0 -25
  33. intersystems_iris/_IRISIterator.py +0 -143
  34. intersystems_iris/_IRISList.py +0 -360
  35. intersystems_iris/_IRISNative.py +0 -208
  36. intersystems_iris/_IRISOREF.py +0 -9
  37. intersystems_iris/_IRISObject.py +0 -424
  38. intersystems_iris/_IRISReference.py +0 -133
  39. intersystems_iris/_InStream.py +0 -149
  40. intersystems_iris/_LegacyIterator.py +0 -135
  41. intersystems_iris/_ListItem.py +0 -15
  42. intersystems_iris/_ListReader.py +0 -84
  43. intersystems_iris/_ListWriter.py +0 -161
  44. intersystems_iris/_LogFileStream.py +0 -115
  45. intersystems_iris/_MessageHeader.py +0 -51
  46. intersystems_iris/_OutStream.py +0 -25
  47. intersystems_iris/_PrintStream.py +0 -65
  48. intersystems_iris/_PythonGateway.py +0 -850
  49. intersystems_iris/_SharedMemorySocket.py +0 -87
  50. intersystems_iris/__init__.py +0 -79
  51. intersystems_iris/__main__.py +0 -7
  52. intersystems_iris/dbapi/_Column.py +0 -56
  53. intersystems_iris/dbapi/_DBAPI.py +0 -2631
  54. intersystems_iris/dbapi/_Descriptor.py +0 -46
  55. intersystems_iris/dbapi/_IRISStream.py +0 -65
  56. intersystems_iris/dbapi/_Message.py +0 -158
  57. intersystems_iris/dbapi/_Parameter.py +0 -171
  58. intersystems_iris/dbapi/_ParameterCollection.py +0 -141
  59. intersystems_iris/dbapi/_ResultSetRow.py +0 -361
  60. intersystems_iris/dbapi/_SQLType.py +0 -32
  61. intersystems_iris/dbapi/__init__.py +0 -0
  62. intersystems_iris/dbapi/preparser/_PreParser.py +0 -1674
  63. intersystems_iris/dbapi/preparser/_Scanner.py +0 -391
  64. intersystems_iris/dbapi/preparser/_Token.py +0 -81
  65. intersystems_iris/dbapi/preparser/_TokenList.py +0 -251
  66. intersystems_iris/dbapi/preparser/__init__.py +0 -0
  67. intersystems_iris/pex/_BusinessHost.py +0 -101
  68. intersystems_iris/pex/_BusinessOperation.py +0 -105
  69. intersystems_iris/pex/_BusinessProcess.py +0 -214
  70. intersystems_iris/pex/_BusinessService.py +0 -95
  71. intersystems_iris/pex/_Common.py +0 -228
  72. intersystems_iris/pex/_Director.py +0 -24
  73. intersystems_iris/pex/_IRISBusinessOperation.py +0 -5
  74. intersystems_iris/pex/_IRISBusinessService.py +0 -18
  75. intersystems_iris/pex/_IRISInboundAdapter.py +0 -5
  76. intersystems_iris/pex/_IRISOutboundAdapter.py +0 -17
  77. intersystems_iris/pex/_InboundAdapter.py +0 -57
  78. intersystems_iris/pex/_Message.py +0 -6
  79. intersystems_iris/pex/_OutboundAdapter.py +0 -46
  80. intersystems_iris/pex/__init__.py +0 -25
  81. iris_pex_embedded_python-3.4.0b14.dist-info/RECORD +0 -143
  82. iris_pex_embedded_python-3.4.0b14.dist-info/top_level.txt +0 -4
  83. irisnative/_IRISNative.py +0 -9
  84. irisnative/__init__.py +0 -10
  85. {iris_pex_embedded_python-3.4.0b14.dist-info → iris_pex_embedded_python-3.4.1.dist-info}/entry_points.txt +0 -0
  86. {iris_pex_embedded_python-3.4.0b14.dist-info → iris_pex_embedded_python-3.4.1.dist-info}/licenses/LICENSE +0 -0
iop/_async_request.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- import iris
2
+ from . import _iris
3
3
  from typing import Any, Optional, Union
4
4
  from iop._dispatch import dispatch_deserializer, dispatch_serializer
5
5
  from iop._message import _Message as Message
@@ -24,6 +24,7 @@ class AsyncRequest(asyncio.Future):
24
24
 
25
25
  async def send(self) -> None:
26
26
  # init parameters
27
+ iris = _iris.get_iris()
27
28
  message_header_id = iris.ref()
28
29
  queue_name = iris.ref()
29
30
  end_time = iris.ref()
@@ -46,6 +47,7 @@ class AsyncRequest(asyncio.Future):
46
47
  self.set_result(self._response)
47
48
 
48
49
  def is_done(self) -> None:
50
+ iris = _iris.get_iris()
49
51
  response = iris.ref()
50
52
  status = self._iris_handle.dispatchIsRequestDone(self.timeout, self._end_time,
51
53
  self._queue_name, self._message_header_id,
iop/_business_host.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from inspect import getsource
2
2
  from typing import Any,List, Optional, Tuple, Union
3
3
 
4
- import iris
4
+ from . import _iris
5
5
 
6
6
  from iop._common import _Common
7
7
  from iop._message import _Message as Message
@@ -109,6 +109,7 @@ class _BusinessHost(_Common):
109
109
 
110
110
  def _create_call_structure(self, target: str, request: Union[Message, Any]) -> Any:
111
111
  """Create an Ens.CallStructure object for the request."""
112
+ iris = _iris.get_iris()
112
113
  call = iris.cls("Ens.CallStructure")._New()
113
114
  call.TargetDispatchName = target
114
115
  call.Request = dispatch_serializer(request)
iop/_business_service.py CHANGED
@@ -16,6 +16,12 @@ class _BusinessService(_BusinessHost):
16
16
  Adapter = adapter = None
17
17
  _wait_for_next_call_interval = False
18
18
 
19
+ def _dispatch_on_init(self, host_object) -> None:
20
+ """For internal use only."""
21
+ self.on_init()
22
+
23
+ return
24
+
19
25
  def on_process_input(self, message_input):
20
26
  """ Receives the message from the inbond adapter via the PRocessInput method and is responsible for forwarding it to target business processes or operations.
21
27
  If the business service does not specify an adapter, then the default adapter calls this method with no message
iop/_cli.py CHANGED
@@ -7,10 +7,10 @@ from enum import Enum, auto
7
7
  import sys
8
8
  from typing import Optional, Callable
9
9
  from importlib.metadata import version
10
-
11
10
  from iop._director import _Director
12
11
  from iop._utils import _Utils
13
12
 
13
+
14
14
  class CommandType(Enum):
15
15
  DEFAULT = auto()
16
16
  LIST = auto()
@@ -46,11 +46,16 @@ class CommandArgs:
46
46
  test: Optional[str] = None
47
47
  classname: Optional[str] = None
48
48
  body: Optional[str] = None
49
+ namespace: Optional[str] = None
49
50
 
50
51
  class Command:
51
52
  def __init__(self, args: CommandArgs):
52
53
  self.args = args
53
54
 
55
+ if self.args.namespace and self.args.namespace != 'not_set':
56
+ # set environment variable IRISNAMESPACE
57
+ os.environ['IRISNAMESPACE'] = self.args.namespace
58
+
54
59
  def execute(self) -> None:
55
60
  command_type = self._determine_command_type()
56
61
  command_handlers = {
@@ -182,6 +187,9 @@ def create_parser() -> argparse.ArgumentParser:
182
187
  test = main_parser.add_argument_group('test arguments')
183
188
  test.add_argument('-C', '--classname', help='test classname', nargs='?', const='not_set')
184
189
  test.add_argument('-B', '--body', help='test body', nargs='?', const='not_set')
190
+
191
+ namespace = main_parser.add_argument_group('namespace arguments')
192
+ namespace.add_argument('-n', '--namespace', help='set namespace', nargs='?', const='not_set')
185
193
 
186
194
  return main_parser
187
195
 
iop/_common.py CHANGED
@@ -1,12 +1,15 @@
1
1
  import abc
2
2
  import inspect
3
3
  import traceback
4
+
4
5
  from typing import Any, ClassVar, List, Optional, Tuple
5
6
 
6
- import iris
7
+ from . import _iris
7
8
 
8
9
  from iop._log_manager import LogManager, logging
9
10
 
11
+ from iop._debugpy import debugpython
12
+
10
13
  class _Common(metaclass=abc.ABCMeta):
11
14
  """Base class that defines common methods for all component types.
12
15
 
@@ -57,6 +60,7 @@ class _Common(metaclass=abc.ABCMeta):
57
60
  self.on_connected()
58
61
 
59
62
  def _dispatch_on_init(self, host_object: Any) -> None:
63
+ """Initialize component when started."""
60
64
  self.on_init()
61
65
 
62
66
  def _dispatch_on_tear_down(self, host_object: Any) -> None:
@@ -66,6 +70,11 @@ class _Common(metaclass=abc.ABCMeta):
66
70
  """Internal method to set IRIS handles."""
67
71
  pass
68
72
 
73
+ def _debugpy(self, host) -> None:
74
+ """Set up debugpy for debugging."""
75
+ if debugpython is not None:
76
+ debugpython(self=self, host_object=host)
77
+
69
78
  # Component information methods
70
79
  @classmethod
71
80
  def _get_info(cls) -> List[str]:
@@ -285,6 +294,7 @@ class _Common(metaclass=abc.ABCMeta):
285
294
  Parameters:
286
295
  message: a string that is written to the log.
287
296
  """
297
+ iris = _iris.get_iris()
288
298
  current_class, current_method = self._log()
289
299
  iris.cls("Ens.Util.Log").LogAssert(current_class, current_method, message)
290
300
 
iop/_debugpy.py ADDED
@@ -0,0 +1,145 @@
1
+
2
+ import threading
3
+ import time
4
+ import contextlib
5
+ import socket
6
+ import os
7
+ import sys
8
+ from contextlib import closing
9
+ from typing import Optional, cast, Any, Dict
10
+
11
+ def find_free_port(start: Optional[int] = None, end: Optional[int] = None) -> int:
12
+ port = start
13
+ if port is None:
14
+ port = 0
15
+ if end is None:
16
+ end = port
17
+
18
+ try:
19
+ with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
20
+ with contextlib.suppress(Exception):
21
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
22
+
23
+ s.bind(("0.0.0.0", port))
24
+
25
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
26
+ return cast(int, s.getsockname()[1])
27
+ except (SystemExit, KeyboardInterrupt):
28
+ raise
29
+ except BaseException:
30
+ if port and end > port:
31
+ return find_free_port(port + 1, end)
32
+ if start and start > 0:
33
+ return find_free_port(None)
34
+ raise
35
+
36
+ def is_debugpy_installed() -> bool:
37
+ try:
38
+ __import__("debugpy")
39
+ return True
40
+ except ImportError:
41
+ return False
42
+
43
+ def _get_python_interpreter_path(install_dir: Optional[str]) -> Optional[str]:
44
+ """Get the path to the Python interpreter."""
45
+ if not install_dir:
46
+ return None
47
+
48
+ python_exe = 'irispython.exe' if sys.platform == 'win32' else 'irispython'
49
+ python_path = os.path.join(install_dir, 'bin', python_exe)
50
+
51
+ return python_path if os.path.exists(python_path) else None
52
+
53
+ def _get_debugpy_config(python_path: str) -> Dict[str, str]:
54
+ """Get the debugpy configuration."""
55
+ return {"python": python_path}
56
+
57
+ def configure_debugpy(self, python_path: Optional[str] = None) -> bool:
58
+ """Configure debugpy with the appropriate Python interpreter."""
59
+ import debugpy
60
+
61
+ if not python_path:
62
+ install_dir = os.environ.get('IRISINSTALLDIR') or os.environ.get('ISC_PACKAGE_INSTALLDIR')
63
+ python_path = _get_python_interpreter_path(install_dir)
64
+
65
+ if not python_path:
66
+ self.log_alert("Could not determine Python interpreter path")
67
+ return False
68
+
69
+ try:
70
+ debugpy.configure(_get_debugpy_config(python_path))
71
+ self.log_info(f"Debugpy configured with Python interpreter: {python_path}")
72
+ return True
73
+ except Exception as e:
74
+ self.log_alert(f"Failed to configure debugpy: {e}")
75
+ return False
76
+
77
+ def wait_for_debugpy_connected(timeout: float = 30, port: int = 0) -> bool:
78
+ """Wait for debugpy client to connect."""
79
+ import debugpy
80
+
81
+ if not is_debugpy_installed():
82
+ return False
83
+
84
+ def timeout_handler():
85
+ time.sleep(timeout)
86
+ debugpy.wait_for_client.cancel()
87
+
88
+ threading.Thread(target=timeout_handler, daemon=True).start()
89
+
90
+ try:
91
+ debugpy.wait_for_client()
92
+ return debugpy.is_client_connected()
93
+ except Exception:
94
+ import pydevd # type: ignore
95
+ pydevd.stoptrace()
96
+ return False
97
+
98
+ def enable_debugpy(port: int, address: str = "0.0.0.0") -> None:
99
+ """Enable debugpy server on specified port and address."""
100
+ import debugpy
101
+ debugpy.listen((address, port))
102
+
103
+ def debugpython(self, host_object: Any) -> None:
104
+ """Enable and configure debugpy for debugging purposes."""
105
+ # hack to set __file__ for os module in debugpy
106
+ # This is a workaround for the issue where debugpy cannot find the __file__ attribute of the os module.
107
+ if not hasattr(os, '__file__'):
108
+ setattr(os, '__file__', __file__)
109
+
110
+
111
+ if host_object is None:
112
+ self.log_alert("No host object found, cannot enable debugpy.")
113
+ return
114
+
115
+ if host_object.enable != 1:
116
+ self.log_info("Debugpy is not enabled.")
117
+ return
118
+
119
+ if not is_debugpy_installed():
120
+ self.log_alert("Debugpy is not installed.")
121
+ return
122
+
123
+ # Configure Python interpreter
124
+ if host_object.PythonInterpreterPath != '':
125
+ success = configure_debugpy(self, host_object.PythonInterpreterPath)
126
+ else:
127
+ success = configure_debugpy(self)
128
+
129
+ if not success:
130
+ return
131
+
132
+ # Setup debugging server
133
+ port = host_object.port if host_object.port and host_object.port > 0 else find_free_port()
134
+
135
+ try:
136
+ enable_debugpy(port=port)
137
+ self.log_info(f"Debugpy enabled on port {port}")
138
+
139
+ self.trace(f"Waiting {host_object.timeout} seconds for debugpy connection...")
140
+ if wait_for_debugpy_connected(timeout=host_object.timeout, port=port):
141
+ self.log_info("Debugpy connected successfully")
142
+ else:
143
+ self.log_alert(f"Debugpy connection timed out after {host_object.timeout} seconds")
144
+ except Exception as e:
145
+ self.log_alert(f"Error enabling debugpy: {e}")
iop/_director.py CHANGED
@@ -1,8 +1,7 @@
1
1
  import asyncio
2
2
  import datetime
3
3
  import functools
4
- import iris
5
- import intersystems_iris.dbapi._DBAPI as irisdbapi
4
+ from . import _iris
6
5
  import signal
7
6
  from dataclasses import dataclass
8
7
 
@@ -55,7 +54,7 @@ class _Director():
55
54
  Returns:
56
55
  an object that contains an instance of IRISBusinessService
57
56
  """
58
- iris_object = iris.cls("IOP.Director").dispatchCreateBusinessService(target)
57
+ iris_object = _iris.get_iris().cls("IOP.Director").dispatchCreateBusinessService(target)
59
58
  return iris_object
60
59
 
61
60
  @staticmethod
@@ -69,7 +68,7 @@ class _Director():
69
68
  Returns:
70
69
  an object that contains an instance of IRISBusinessService
71
70
  """
72
- iris_object = iris.cls("IOP.Director").dispatchCreateBusinessService(target)
71
+ iris_object = _iris.get_iris().cls("IOP.Director").dispatchCreateBusinessService(target)
73
72
  return iris_object.GetClass()
74
73
 
75
74
  ### List of function to manage the production
@@ -102,37 +101,37 @@ class _Director():
102
101
  def start_production(production_name=None):
103
102
  if production_name is None or production_name == '':
104
103
  production_name = _Director.get_default_production()
105
- iris.cls('Ens.Director').StartProduction(production_name)
104
+ _iris.get_iris().cls('Ens.Director').StartProduction(production_name)
106
105
 
107
106
  ### stop production
108
107
  @staticmethod
109
108
  def stop_production():
110
- iris.cls('Ens.Director').StopProduction()
109
+ _iris.get_iris().cls('Ens.Director').StopProduction()
111
110
 
112
111
  ### restart production
113
112
  @staticmethod
114
113
  def restart_production():
115
- iris.cls('Ens.Director').RestartProduction()
114
+ _iris.get_iris().cls('Ens.Director').RestartProduction()
116
115
 
117
116
  ### shutdown production
118
117
  @staticmethod
119
118
  def shutdown_production():
120
- iris.cls('Ens.Director').StopProduction(10,1)
119
+ _iris.get_iris().cls('Ens.Director').StopProduction(10,1)
121
120
 
122
121
  ### update production
123
122
  @staticmethod
124
123
  def update_production():
125
- iris.cls('Ens.Director').UpdateProduction()
124
+ _iris.get_iris().cls('Ens.Director').UpdateProduction()
126
125
 
127
126
  ### list production
128
127
  @staticmethod
129
128
  def list_productions():
130
- return iris.cls('IOP.Director').dispatchListProductions()
129
+ return _iris.get_iris().cls('IOP.Director').dispatchListProductions()
131
130
 
132
131
  ### status production
133
132
  @staticmethod
134
133
  def status_production():
135
- dikt = iris.cls('IOP.Director').StatusProduction()
134
+ dikt = _iris.get_iris().cls('IOP.Director').StatusProduction()
136
135
  if dikt['Production'] is None or dikt['Production'] == '':
137
136
  dikt['Production'] = _Director.get_default_production()
138
137
  return dikt
@@ -141,13 +140,13 @@ class _Director():
141
140
  @staticmethod
142
141
  def set_default_production(production_name=''):
143
142
  #set ^Ens.Configuration("SuperUser","LastProduction")
144
- glb = iris.gref("^Ens.Configuration")
143
+ glb = _iris.get_iris().gref("^Ens.Configuration")
145
144
  glb['csp', "LastProduction"] = production_name
146
145
 
147
146
  ### get default production
148
147
  @staticmethod
149
148
  def get_default_production():
150
- glb = iris.gref("^Ens.Configuration")
149
+ glb = _iris.get_iris().gref("^Ens.Configuration")
151
150
  default_production_name = glb['csp', "LastProduction"]
152
151
  if default_production_name is None or default_production_name == '':
153
152
  default_production_name = 'Not defined'
@@ -178,7 +177,7 @@ class _Director():
178
177
  return str(row[9]) + ' ' + typ + ' ' + str(row[1]) + ' ' + str(row[2]) + ' ' + str(row[3]) + ' ' + str(row[4]) + ' ' + str(row[5]) + ' ' + str(row[6]) + ' ' + str(row[8])
179
178
 
180
179
  @staticmethod
181
- def read_top_log(cursor,top) -> list:
180
+ def read_top_log(top) -> list:
182
181
  sql = """
183
182
  SELECT top ?
184
183
  ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
@@ -186,13 +185,14 @@ class _Director():
186
185
  order by id desc
187
186
  """
188
187
  result = []
189
- cursor.execute(sql, top)
190
- for row in cursor:
188
+ stmt = _iris.get_iris().sql.prepare(sql)
189
+ rs = stmt.execute(top)
190
+ for row in rs:
191
191
  result.append(_Director.format_log(row))
192
192
  return result
193
193
 
194
194
  @staticmethod
195
- def read_log(cursor) -> list:
195
+ def read_log() -> list:
196
196
  sql = """
197
197
  SELECT
198
198
  ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
@@ -201,8 +201,12 @@ class _Director():
201
201
  order by id desc
202
202
  """
203
203
  result = []
204
- cursor.execute(sql, (datetime.datetime.now() - datetime.timedelta(seconds=1),))
205
- for row in cursor:
204
+ stmt = _iris.get_iris().sql.prepare(sql)
205
+ time = datetime.datetime.now() - datetime.timedelta(seconds=1)
206
+ # convert to utc time
207
+ time = time.astimezone(datetime.timezone.utc)
208
+ rs = stmt.execute(time.isoformat(sep=' '))
209
+ for row in rs:
206
210
  result.append(_Director.format_log(row))
207
211
  return result
208
212
 
@@ -211,14 +215,12 @@ class _Director():
211
215
  """ Log production
212
216
  if ctrl+c is pressed, the log is stopped
213
217
  """
214
- with irisdbapi.connect(embedded=True) as conn:
215
- with conn.cursor() as cursor:
216
- while True:
217
- for row in reversed(_Director.read_log(cursor)):
218
- print(row)
219
- if handler.sigint_log:
220
- break
221
- await asyncio.sleep(1)
218
+ while True:
219
+ for row in reversed(_Director.read_log()):
220
+ print(row)
221
+ if handler.sigint_log:
222
+ break
223
+ await asyncio.sleep(1)
222
224
 
223
225
  @staticmethod
224
226
  def log_production_top(top=10):
@@ -227,10 +229,8 @@ class _Director():
227
229
  Parameters:
228
230
  top: the number of log to display
229
231
  """
230
- with irisdbapi.connect(embedded=True) as conn:
231
- with conn.cursor() as cursor:
232
- for row in reversed(_Director.read_top_log(cursor, top)):
233
- print(row)
232
+ for row in reversed(_Director.read_top_log(top)):
233
+ print(row)
234
234
 
235
235
  @staticmethod
236
236
  def log_production():
@@ -240,10 +240,10 @@ class _Director():
240
240
  loop = asyncio.get_event_loop()
241
241
  handler = SigintHandler(log_only=True)
242
242
  loop.add_signal_handler(signal.SIGINT, functools.partial(handler.signal_handler, signal.SIGINT, loop))
243
- with irisdbapi.connect(embedded=True) as conn:
244
- with conn.cursor() as cursor:
245
- for row in reversed(_Director.read_top_log(cursor, 10)):
246
- print(row)
243
+
244
+ for row in reversed(_Director.read_top_log( 10)):
245
+ print(row)
246
+
247
247
  loop.run_until_complete(_Director._log_production_async(handler))
248
248
  loop.close()
249
249
 
@@ -257,20 +257,20 @@ class _Director():
257
257
  body: the body of the message
258
258
  """
259
259
  if not message:
260
- message = iris.cls('Ens.Request')._New()
260
+ message = _iris.get_iris().cls('Ens.Request')._New()
261
261
  if classname:
262
262
  # if classname start with 'iris.' then create an iris object
263
263
  if classname.startswith('iris.'):
264
264
  # strip the iris. prefix
265
265
  classname = classname[5:]
266
266
  if body:
267
- message = iris.cls(classname)._New(body)
267
+ message = _iris.get_iris().cls(classname)._New(body)
268
268
  else:
269
- message = iris.cls(classname)._New()
269
+ message = _iris.get_iris().cls(classname)._New()
270
270
  # else create a python object
271
271
  else:
272
272
  # python message are casted to Grongier.PEX.Message
273
- message = iris.cls("IOP.Message")._New()
273
+ message = _iris.get_iris().cls("IOP.Message")._New()
274
274
  message.classname = classname
275
275
  if body:
276
276
  message.json = body
@@ -278,7 +278,7 @@ class _Director():
278
278
  message.json = _Utils.string_to_stream("{}")
279
279
  # serialize the message
280
280
  serial_message = dispatch_serializer(message)
281
- response = iris.cls('IOP.Utils').dispatchTestComponent(target,serial_message)
281
+ response = _iris.get_iris().cls('IOP.Utils').dispatchTestComponent(target,serial_message)
282
282
  try:
283
283
  deserialized_response = dispatch_deserializer(response)
284
284
  except ImportError as e:
iop/_iris.py ADDED
@@ -0,0 +1,7 @@
1
+ import os
2
+
3
+ def get_iris(namespace:str=None)->'iris':
4
+ if namespace:
5
+ os.environ['IRISNAMESPACE'] = namespace
6
+ import iris
7
+ return iris
iop/_log_manager.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import traceback
2
- import iris
2
+ from . import _iris
3
3
  import logging
4
4
  from typing import Optional, Tuple
5
5
 
@@ -41,11 +41,11 @@ class IRISLogHandler(logging.Handler):
41
41
 
42
42
  # Map Python logging levels to IRIS logging methods
43
43
  self.level_map = {
44
- logging.DEBUG: iris.cls("Ens.Util.Log").LogTrace,
45
- logging.INFO: iris.cls("Ens.Util.Log").LogInfo,
46
- logging.WARNING: iris.cls("Ens.Util.Log").LogWarning,
47
- logging.ERROR: iris.cls("Ens.Util.Log").LogError,
48
- logging.CRITICAL: iris.cls("Ens.Util.Log").LogAlert,
44
+ logging.DEBUG: 5,
45
+ logging.INFO: 4,
46
+ logging.WARNING: 3,
47
+ logging.ERROR: 2,
48
+ logging.CRITICAL: 6,
49
49
  }
50
50
  # Map Python logging levels to IRIS logging Console level
51
51
  self.level_map_console = {
@@ -76,8 +76,8 @@ class IRISLogHandler(logging.Handler):
76
76
  class_name = record.class_name if hasattr(record, "class_name") else record.name
77
77
  method_name = record.method_name if hasattr(record, "method_name") else record.funcName
78
78
  if self.to_console or (hasattr(record, "to_console") and record.to_console):
79
- iris.cls("%SYS.System").WriteToConsoleLog(self.format(record),
79
+ _iris.get_iris().cls("%SYS.System").WriteToConsoleLog(self.format(record),
80
80
  0,self.level_map_console.get(record.levelno, 0),class_name+"."+method_name)
81
81
  else:
82
- log_func = self.level_map.get(record.levelno, iris.cls("Ens.Util.Log").LogInfo)
83
- log_func(class_name, method_name, self.format(record))
82
+ log_lvl = self.level_map.get(record.levelno, 4)
83
+ _iris.get_iris().cls("Ens.Util.Log").Log(log_lvl,class_name, method_name, self.format(record))
iop/_serialization.py CHANGED
@@ -4,13 +4,13 @@ import importlib
4
4
  import inspect
5
5
  import pickle
6
6
  import json
7
- from dataclasses import asdict, is_dataclass
7
+ from dataclasses import is_dataclass
8
8
  from typing import Any, Dict, Type
9
9
 
10
- import iris
10
+ from . import _iris
11
11
  from pydantic import BaseModel, TypeAdapter, ValidationError
12
12
 
13
- from iop._message import _PydanticPickleMessage
13
+ from iop._message import _PydanticPickleMessage, _Message
14
14
  from iop._utils import _Utils
15
15
 
16
16
  class SerializationError(Exception):
@@ -31,23 +31,23 @@ class MessageSerializer:
31
31
  """Convert objects to JSON-safe format."""
32
32
  if isinstance(obj, BaseModel):
33
33
  return obj.model_dump_json()
34
- elif is_dataclass(obj):
34
+ elif is_dataclass(obj) and isinstance(obj, _Message):
35
35
  return TempPydanticModel.model_validate(dataclass_to_dict(obj)).model_dump_json()
36
36
  else:
37
- raise SerializationError(f"Object {obj} must be a Pydantic model or dataclass")
37
+ raise SerializationError(f"Object {obj} must be a Pydantic model or dataclass Message")
38
38
 
39
39
  @staticmethod
40
- def serialize(message: Any, use_pickle: bool = False) -> iris.cls:
40
+ def serialize(message: Any, use_pickle: bool = False) -> Any:
41
41
  """Serializes a message to IRIS format."""
42
42
  if isinstance(message, _PydanticPickleMessage) or use_pickle:
43
43
  return MessageSerializer._serialize_pickle(message)
44
44
  return MessageSerializer._serialize_json(message)
45
45
 
46
46
  @staticmethod
47
- def _serialize_json(message: Any) -> iris.cls:
47
+ def _serialize_json(message: Any) -> Any:
48
48
  json_string = MessageSerializer._convert_to_json_safe(message)
49
49
 
50
- msg = iris.cls('IOP.Message')._New()
50
+ msg = _iris.get_iris().cls('IOP.Message')._New()
51
51
  msg.classname = f"{message.__class__.__module__}.{message.__class__.__name__}"
52
52
 
53
53
  if hasattr(msg, 'buffer') and len(json_string) > msg.buffer:
@@ -57,13 +57,13 @@ class MessageSerializer:
57
57
  return msg
58
58
 
59
59
  @staticmethod
60
- def deserialize(serial: iris.cls, use_pickle: bool = False) -> Any:
60
+ def deserialize(serial: Any, use_pickle: bool = False) -> Any:
61
61
  if use_pickle:
62
62
  return MessageSerializer._deserialize_pickle(serial)
63
63
  return MessageSerializer._deserialize_json(serial)
64
64
 
65
65
  @staticmethod
66
- def _deserialize_json(serial: iris.cls) -> Any:
66
+ def _deserialize_json(serial: Any) -> Any:
67
67
  if not serial.classname:
68
68
  raise SerializationError("JSON message malformed, must include classname")
69
69
 
@@ -88,15 +88,15 @@ class MessageSerializer:
88
88
  raise SerializationError(f"Failed to deserialize JSON: {str(e)}")
89
89
 
90
90
  @staticmethod
91
- def _serialize_pickle(message: Any) -> iris.cls:
91
+ def _serialize_pickle(message: Any) -> Any:
92
92
  pickle_string = codecs.encode(pickle.dumps(message), "base64").decode()
93
- msg = iris.cls('IOP.PickleMessage')._New()
93
+ msg = _iris.get_iris().cls('IOP.PickleMessage')._New()
94
94
  msg.classname = f"{message.__class__.__module__}.{message.__class__.__name__}"
95
95
  msg.jstr = _Utils.string_to_stream(pickle_string)
96
96
  return msg
97
97
 
98
98
  @staticmethod
99
- def _deserialize_pickle(serial: iris.cls) -> Any:
99
+ def _deserialize_pickle(serial: Any) -> Any:
100
100
  string = _Utils.stream_to_string(serial.jstr)
101
101
  return pickle.loads(codecs.decode(string.encode(), "base64"))
102
102
 
@@ -149,12 +149,20 @@ def dataclass_from_dict(klass: Type, dikt: Dict) -> Any:
149
149
  def dataclass_to_dict(instance: Any) -> Dict:
150
150
  """Converts a class instance to a dictionary.
151
151
  Handles non attended fields."""
152
- dikt = asdict(instance)
153
- # assign any extra fields
154
- for k, v in vars(instance).items():
155
- if k not in dikt:
156
- dikt[k] = v
157
- return dikt
152
+ result = {}
153
+ for field in instance.__dict__:
154
+ value = getattr(instance, field)
155
+ if is_dataclass(value):
156
+ result[field] = dataclass_to_dict(value)
157
+ elif isinstance(value, list):
158
+ result[field] = [dataclass_to_dict(i) if is_dataclass(i) else i for i in value]
159
+ elif isinstance(value, dict):
160
+ result[field] = {k: dataclass_to_dict(v) if is_dataclass(v) else v for k, v in value.items()}
161
+ elif hasattr(value, '__dict__'):
162
+ result[field] = dataclass_to_dict(value)
163
+ else:
164
+ result[field] = value
165
+ return result
158
166
 
159
167
  # Maintain backwards compatibility
160
168
  serialize_pickle_message = lambda msg: MessageSerializer.serialize(msg, use_pickle=True)