iris-pex-embedded-python 2.3.27b2__py3-none-any.whl → 3.2.1b2__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 (85) hide show
  1. grongier/cls/Grongier/PEX/BusinessOperation.cls +1 -28
  2. grongier/cls/Grongier/PEX/BusinessProcess.cls +1 -100
  3. grongier/cls/Grongier/PEX/BusinessService.cls +1 -28
  4. grongier/cls/Grongier/PEX/Common.cls +1 -194
  5. grongier/cls/Grongier/PEX/Director.cls +1 -48
  6. grongier/cls/Grongier/PEX/Duplex/Operation.cls +1 -26
  7. grongier/cls/Grongier/PEX/Duplex/Process.cls +1 -217
  8. grongier/cls/Grongier/PEX/Duplex/Service.cls +1 -6
  9. grongier/cls/Grongier/PEX/InboundAdapter.cls +1 -15
  10. grongier/cls/Grongier/PEX/Message.cls +1 -116
  11. grongier/cls/Grongier/PEX/OutboundAdapter.cls +1 -29
  12. grongier/cls/Grongier/PEX/PickleMessage.cls +1 -46
  13. grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +1 -253
  14. grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +1 -19
  15. grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +1 -19
  16. grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +1 -19
  17. grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +1 -35
  18. grongier/cls/Grongier/PEX/Test.cls +1 -53
  19. grongier/cls/Grongier/PEX/Utils.cls +1 -365
  20. grongier/cls/Grongier/Service/WSGI.cls +1 -307
  21. grongier/pex/__init__.py +11 -11
  22. grongier/pex/__main__.py +1 -1
  23. grongier/pex/_business_host.py +1 -511
  24. grongier/pex/_cli.py +2 -150
  25. grongier/pex/_common.py +1 -347
  26. grongier/pex/_director.py +1 -286
  27. grongier/pex/_utils.py +1 -369
  28. intersystems_iris/_ConnectionInformation.py +22 -20
  29. intersystems_iris/dbapi/_DBAPI.py +6 -1
  30. intersystems_iris/dbapi/_ResultSetRow.py +26 -15
  31. intersystems_iris/dbapi/preparser/_PreParser.py +4 -1
  32. iop/__init__.py +24 -0
  33. iop/__main__.py +4 -0
  34. iop/_business_host.py +675 -0
  35. iop/_business_operation.py +71 -0
  36. iop/_business_process.py +220 -0
  37. {grongier/pex → iop}/_business_service.py +2 -2
  38. iop/_cli.py +141 -0
  39. iop/_common.py +352 -0
  40. iop/_director.py +301 -0
  41. {grongier/pex → iop}/_inbound_adapter.py +1 -1
  42. iop/_log_manager.py +81 -0
  43. {grongier/pex → iop}/_message.py +1 -1
  44. {grongier/pex → iop}/_outbound_adapter.py +1 -1
  45. {grongier/pex → iop}/_private_session_duplex.py +3 -2
  46. {grongier/pex → iop}/_private_session_process.py +2 -2
  47. iop/_utils.py +458 -0
  48. iop/cls/IOP/BusinessOperation.cls +35 -0
  49. iop/cls/IOP/BusinessProcess.cls +124 -0
  50. iop/cls/IOP/BusinessService.cls +35 -0
  51. iop/cls/IOP/Common.cls +344 -0
  52. iop/cls/IOP/Director.cls +62 -0
  53. iop/cls/IOP/Duplex/Operation.cls +29 -0
  54. iop/cls/IOP/Duplex/Process.cls +229 -0
  55. iop/cls/IOP/Duplex/Service.cls +9 -0
  56. iop/cls/IOP/InboundAdapter.cls +22 -0
  57. iop/cls/IOP/Message/JSONSchema.cls +125 -0
  58. iop/cls/IOP/Message.cls +729 -0
  59. iop/cls/IOP/OutboundAdapter.cls +36 -0
  60. iop/cls/IOP/PickleMessage.cls +58 -0
  61. iop/cls/IOP/PrivateSession/Duplex.cls +260 -0
  62. iop/cls/IOP/PrivateSession/Message/Ack.cls +32 -0
  63. iop/cls/IOP/PrivateSession/Message/Poll.cls +32 -0
  64. iop/cls/IOP/PrivateSession/Message/Start.cls +32 -0
  65. iop/cls/IOP/PrivateSession/Message/Stop.cls +48 -0
  66. iop/cls/IOP/Service/WSGI.cls +310 -0
  67. iop/cls/IOP/Test.cls +85 -0
  68. iop/cls/IOP/Utils.cls +378 -0
  69. iop/wsgi/handlers.py +104 -0
  70. iris_pex_embedded_python-3.2.1b2.dist-info/METADATA +90 -0
  71. iris_pex_embedded_python-3.2.1b2.dist-info/RECORD +139 -0
  72. {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b2.dist-info}/WHEEL +1 -1
  73. iris_pex_embedded_python-3.2.1b2.dist-info/entry_points.txt +2 -0
  74. {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b2.dist-info}/top_level.txt +1 -1
  75. grongier/pex/_business_operation.py +0 -70
  76. grongier/pex/_business_process.py +0 -215
  77. iris/__init__.py +0 -60
  78. iris/__init__.pyi +0 -236
  79. iris/iris_ipm.py +0 -40
  80. iris/iris_ipm.pyi +0 -17
  81. iris_pex_embedded_python-2.3.27b2.dist-info/METADATA +0 -1384
  82. iris_pex_embedded_python-2.3.27b2.dist-info/RECORD +0 -113
  83. iris_pex_embedded_python-2.3.27b2.dist-info/entry_points.txt +0 -2
  84. {grongier/pex → iop}/_pickle_message.py +0 -0
  85. {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b2.dist-info}/LICENSE +0 -0
iop/_business_host.py ADDED
@@ -0,0 +1,675 @@
1
+ import abc
2
+ import asyncio
3
+ import base64
4
+ import codecs
5
+ import datetime
6
+ import decimal
7
+ import importlib
8
+ import inspect
9
+ import iris
10
+ import json
11
+ import pickle
12
+ import uuid
13
+ from dataclasses import dataclass
14
+ from functools import wraps
15
+ from inspect import getsource, signature
16
+ from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple, Type, Union
17
+
18
+ from dacite import Config, from_dict
19
+
20
+ from iop._common import _Common
21
+ from iop._message import _Message as Message
22
+ from iop._utils import _Utils
23
+
24
+ class _BusinessHost(_Common):
25
+ """Base class for business components that defines common methods.
26
+
27
+ This is a superclass for BusinessService, BusinessProcess, and BusinessOperation that
28
+ defines common functionality like message serialization/deserialization and request handling.
29
+ """
30
+
31
+ buffer: int = 64000
32
+ DISPATCH: List[Tuple[str, str]] = []
33
+
34
+ def input_serialzer(fonction: Callable) -> Callable:
35
+ """Decorator that serializes input arguments before passing to function.
36
+
37
+ Args:
38
+ fonction: Function to decorate
39
+
40
+ Returns:
41
+ Decorated function that handles serialization
42
+ """
43
+ def dispatch_serializer(self, *params: Any, **param2: Any) -> Any:
44
+ # Handle positional arguments using list comprehension
45
+ serialized = [self._dispatch_serializer(param) for param in params]
46
+
47
+ # Handle keyword arguments using dictionary comprehension
48
+ param2 = {key: self._dispatch_serializer(value) for key, value in param2.items()}
49
+
50
+ return fonction(self, *serialized, **param2)
51
+ return dispatch_serializer
52
+
53
+ def input_serialzer_param(position: int, name: str) -> Callable:
54
+ """Decorator that serializes specific parameter by position or name.
55
+
56
+ Args:
57
+ position: Position of parameter to serialize
58
+ name: Name of parameter to serialize
59
+
60
+ Returns:
61
+ Decorator function
62
+ """
63
+ def input_serialzer_param(fonction: Callable) -> Callable:
64
+ @wraps(fonction)
65
+ def dispatch_serializer(self, *params: Any, **param2: Any) -> Any:
66
+ # Handle positional arguments using list comprehension
67
+ serialized = [
68
+ self._dispatch_serializer(param) if i == position else param
69
+ for i, param in enumerate(params)
70
+ ]
71
+
72
+ # Handle keyword arguments using dictionary comprehension
73
+ param2 = {
74
+ key: self._dispatch_serializer(value) if key == name else value
75
+ for key, value in param2.items()
76
+ }
77
+
78
+ return fonction(self, *serialized, **param2)
79
+ return dispatch_serializer
80
+ return input_serialzer_param
81
+
82
+ def output_deserialzer(fonction: Callable) -> Callable:
83
+ """Decorator that deserializes output of function.
84
+
85
+ Args:
86
+ fonction: Function to decorate
87
+
88
+ Returns:
89
+ Decorated function that handles deserialization
90
+ """
91
+ def dispatch_deserializer(self, *params: Any, **param2: Any) -> Any:
92
+ return self._dispatch_deserializer(fonction(self, *params, **param2))
93
+
94
+ return dispatch_deserializer
95
+
96
+ def input_deserialzer(fonction: Callable) -> Callable:
97
+ """Decorator that deserializes input arguments before passing to function.
98
+
99
+ Args:
100
+ fonction: Function to decorate
101
+
102
+ Returns:
103
+ Decorated function that handles deserialization
104
+ """
105
+ def dispatch_deserializer(self, *params: Any, **param2: Any) -> Any:
106
+ # Handle positional arguments using list comprehension
107
+ serialized = [self._dispatch_deserializer(param) for param in params]
108
+
109
+ # Handle keyword arguments using dictionary comprehension
110
+ param2 = {key: self._dispatch_deserializer(value) for key, value in param2.items()}
111
+
112
+ return fonction(self, *serialized, **param2)
113
+ return dispatch_deserializer
114
+
115
+ def output_serialzer(fonction: Callable) -> Callable:
116
+ """Decorator that serializes output of function.
117
+
118
+ Args:
119
+ fonction: Function to decorate
120
+
121
+ Returns:
122
+ Decorated function that handles serialization
123
+ """
124
+ def dispatch_serializer(self, *params: Any, **param2: Any) -> Any:
125
+ return self._dispatch_serializer(fonction(self, *params, **param2))
126
+ return dispatch_serializer
127
+
128
+ @input_serialzer_param(1, 'request')
129
+ @output_deserialzer
130
+ def send_request_sync(self, target: str, request: Union[Message, Any],
131
+ timeout: int = -1, description: Optional[str] = None) -> Any:
132
+ """Send message synchronously to target component.
133
+
134
+ Args:
135
+ target: Name of target component
136
+ request: Message to send
137
+ timeout: Timeout in seconds, -1 means wait forever
138
+ description: Optional description for logging
139
+
140
+ Returns:
141
+ Response from target component
142
+
143
+ Raises:
144
+ TypeError: If request is invalid type
145
+ """
146
+ return self.iris_handle.dispatchSendRequestSync(target, request, timeout, description)
147
+
148
+ @input_serialzer_param(1, 'request')
149
+ def send_request_async(self, target: str, request: Union[Message, Any],
150
+ description: Optional[str] = None) -> None:
151
+ """Send message asynchronously to target component.
152
+
153
+ Args:
154
+ target: Name of target component
155
+ request: Message to send
156
+ description: Optional description for logging
157
+
158
+ Raises:
159
+ TypeError: If request is invalid type
160
+ """
161
+ return self.iris_handle.dispatchSendRequestAsync(target, request, description)
162
+
163
+ async def send_request_async_ng(self, target: str, request: Union[Message, Any],
164
+ timeout: int = -1, description: Optional[str] = None) -> Any:
165
+ """Send message asynchronously to target component with asyncio.
166
+
167
+ Args:
168
+ target: Name of target component
169
+ request: Message to send
170
+ timeout: Timeout in seconds, -1 means wait forever
171
+ description: Optional description for logging
172
+
173
+ Returns:
174
+ Response from target component
175
+ """
176
+ return await _send_request_async_ng(target, request, timeout, description, self)
177
+
178
+ def send_multi_request_sync(self, target_request: List[Tuple[str, Union[Message, Any]]],
179
+ timeout: int = -1, description: Optional[str] = None) -> List[Tuple[str, Union[Message, Any], Any, int]]:
180
+ """Send multiple messages synchronously to target components.
181
+
182
+ Args:
183
+ target_request: List of tuples (target, request) to send
184
+ timeout: Timeout in seconds, -1 means wait forever
185
+ description: Optional description for logging
186
+
187
+ Returns:
188
+ List of tuples (target, request, response, status)
189
+ """
190
+ # create a list of iris.Ens.CallStructure for each target_request
191
+ call_list = []
192
+ # sanity check
193
+ if not isinstance(target_request, list):
194
+ raise TypeError("The target_request parameter must be a list")
195
+ if len(target_request) == 0:
196
+ raise ValueError("The target_request parameter must not be empty")
197
+ # check if the target_request is a list of tuple of 2 elements
198
+ if not all(isinstance(item, tuple) and len(item) == 2 for item in target_request):
199
+ raise TypeError("The target_request parameter must be a list of tuple of 2 elements")
200
+
201
+ for target, request in target_request:
202
+ call = iris.cls("Ens.CallStructure")._New()
203
+ call.TargetDispatchName = target
204
+ call.Request = self._dispatch_serializer(request)
205
+ call_list.append(call)
206
+ # call the dispatchSendMultiRequestSync method
207
+ response_list = self.iris_handle.dispatchSendRequestSyncMultiple(call_list, timeout)
208
+ # create a list of tuple (target, request, response, status)
209
+ result = []
210
+ for i in range(len(target_request)):
211
+ result.append(
212
+ (target_request[i][0],
213
+ target_request[i][1],
214
+ self._dispatch_deserializer(response_list[i].Response),
215
+ response_list[i].ResponseCode
216
+ ))
217
+ return result
218
+
219
+
220
+ def _serialize_pickle_message(self, message: Any) -> iris.cls:
221
+ """Converts a python dataclass message into an iris iop.message.
222
+
223
+ Args:
224
+ message: The message to serialize, an instance of a class that is a subclass of Message.
225
+
226
+ Returns:
227
+ The message in json format.
228
+ """
229
+ pickle_string = codecs.encode(pickle.dumps(message), "base64").decode()
230
+ module = message.__class__.__module__
231
+ classname = message.__class__.__name__
232
+
233
+ msg = iris.cls('IOP.PickleMessage')._New()
234
+ msg.classname = module + "." + classname
235
+
236
+ stream = _Utils.string_to_stream(pickle_string)
237
+ msg.jstr = stream
238
+
239
+ return msg
240
+
241
+
242
+ def _dispatch_serializer(self, message: Any) -> Any:
243
+ """Serializes the message based on its type.
244
+
245
+ Args:
246
+ message: The message to serialize
247
+
248
+ Returns:
249
+ The serialized message
250
+
251
+ Raises:
252
+ TypeError: If message is invalid type
253
+ """
254
+ if message is not None:
255
+ if self._is_message_instance(message):
256
+ return self._serialize_message(message)
257
+ elif self._is_pickle_message_instance(message):
258
+ return self._serialize_pickle_message(message)
259
+ elif self._is_iris_object_instance(message):
260
+ return message
261
+
262
+ if message == "" or message is None:
263
+ return message
264
+
265
+ # todo : decorator takes care of all the parameters, so this should never happen
266
+ # return message
267
+ raise TypeError("The message must be an instance of a class that is a subclass of Message or IRISObject %Persistent class.")
268
+
269
+ def _serialize_message(self, message: Any) -> iris.cls:
270
+ """Converts a python dataclass message into an iris iop.message.
271
+
272
+ Args:
273
+ message: The message to serialize, an instance of a class that is a subclass of Message.
274
+
275
+ Returns:
276
+ The message in json format.
277
+ """
278
+ json_string = json.dumps(message, cls=IrisJSONEncoder, ensure_ascii=False)
279
+ module = message.__class__.__module__
280
+ classname = message.__class__.__name__
281
+
282
+ msg = iris.cls('IOP.Message')._New()
283
+ msg.classname = module + "." + classname
284
+
285
+ if hasattr(msg, 'buffer') and len(json_string) > msg.buffer:
286
+ msg.json = _Utils.string_to_stream(json_string, msg.buffer)
287
+ else:
288
+ msg.json = json_string
289
+
290
+ return msg
291
+
292
+ def _deserialize_pickle_message(self, serial: iris.cls) -> Any:
293
+ """Converts an iris iop.message into a python dataclass message.
294
+
295
+ Args:
296
+ serial: The serialized message
297
+
298
+ Returns:
299
+ The deserialized message
300
+ """
301
+ string = _Utils.stream_to_string(serial.jstr)
302
+
303
+ msg = pickle.loads(codecs.decode(string.encode(), "base64"))
304
+ return msg
305
+
306
+ def _dispatch_deserializer(self, serial: Any) -> Any:
307
+ """Deserializes the message based on its type.
308
+
309
+ Args:
310
+ serial: The serialized message
311
+
312
+ Returns:
313
+ The deserialized message
314
+ """
315
+ if (
316
+ serial is not None
317
+ and type(serial).__module__.startswith('iris')
318
+ and (
319
+ serial._IsA("IOP.Message")
320
+ or serial._IsA("Grongier.PEX.Message")
321
+ )
322
+ ):
323
+ return self._deserialize_message(serial)
324
+ elif (
325
+ serial is not None
326
+ and type(serial).__module__.startswith('iris')
327
+ and (
328
+ serial._IsA("IOP.PickleMessage")
329
+ or serial._IsA("Grongier.PEX.PickleMessage")
330
+ )
331
+ ):
332
+ return self._deserialize_pickle_message(serial)
333
+ else:
334
+ return serial
335
+
336
+ def _deserialize_message(self, serial: iris.cls) -> Any:
337
+ """Converts an iris iop.message into a python dataclass message.
338
+
339
+ Args:
340
+ serial: The serialized message
341
+
342
+ Returns:
343
+ The deserialized message
344
+ """
345
+ if (serial.classname is None):
346
+ raise ValueError("JSON message malformed, must include classname")
347
+ classname = serial.classname
348
+
349
+ j = classname.rindex(".")
350
+ if (j <= 0):
351
+ raise ValueError("Classname must include a module: " + classname)
352
+ try:
353
+ module = importlib.import_module(classname[:j])
354
+ msg = getattr(module, classname[j+1:])
355
+ except Exception:
356
+ raise ImportError("Class not found: " + classname)
357
+
358
+ string = ""
359
+ if (serial.type == 'Stream'):
360
+ string = _Utils.stream_to_string(serial.json)
361
+ else:
362
+ string = serial.json
363
+
364
+ jdict = json.loads(string, cls=IrisJSONDecoder)
365
+ msg = self._dataclass_from_dict(msg, jdict)
366
+ return msg
367
+
368
+ def _dataclass_from_dict(self, klass: Type, dikt: Dict) -> Any:
369
+ """Converts a dictionary to a dataclass instance.
370
+
371
+ Args:
372
+ klass: The dataclass to convert to
373
+ dikt: The dictionary to convert to a dataclass
374
+
375
+ Returns:
376
+ A dataclass object with the fields of the dataclass and the fields of the dictionary.
377
+ """
378
+ ret = from_dict(klass, dikt, Config(check_types=False))
379
+
380
+ try:
381
+ fieldtypes = klass.__annotations__
382
+ except Exception as e:
383
+ fieldtypes = []
384
+
385
+ for key, val in dikt.items():
386
+ if key not in fieldtypes:
387
+ setattr(ret, key, val)
388
+ return ret
389
+
390
+ def _dispach_message(self, request: Any) -> Any:
391
+ """Dispatches the message to the appropriate method.
392
+
393
+ Args:
394
+ request: The request object
395
+
396
+ Returns:
397
+ The response object
398
+ """
399
+ call = 'on_message'
400
+
401
+ module = request.__class__.__module__
402
+ classname = request.__class__.__name__
403
+
404
+ for msg, method in self.DISPATCH:
405
+ if msg == module + "." + classname:
406
+ call = method
407
+
408
+ return getattr(self, call)(request)
409
+
410
+
411
+ def _create_dispatch(self) -> None:
412
+ """Creates a list of tuples, where each tuple contains the name of a class and the name of a method
413
+ that takes an instance of that class as its only argument.
414
+ """
415
+ if len(self.DISPATCH) == 0:
416
+ # get all function in current BO
417
+ method_list = [func for func in dir(self) if callable(getattr(self, func)) and not func.startswith("_")]
418
+ for method in method_list:
419
+ # get signature of current function
420
+ try:
421
+ param = signature(getattr(self, method)).parameters
422
+ # Handle staticmethod
423
+ except ValueError as e:
424
+ param = ''
425
+ # one parameter
426
+ if (len(param) == 1):
427
+ # get parameter type
428
+ annotation = str(param[list(param)[0]].annotation)
429
+ # trim annotation format <class 'toto'>
430
+ i = annotation.find("'")
431
+ j = annotation.rfind("'")
432
+ # if end is not found
433
+ if j == -1:
434
+ j = None
435
+ classname = annotation[i+1:j]
436
+ self.DISPATCH.append((classname, method))
437
+ return
438
+
439
+ @staticmethod
440
+ def OnGetConnections() -> Optional[List[str]]:
441
+ """The OnGetConnections() method returns all of the targets of any SendRequestSync or SendRequestAsync
442
+ calls for the class. Implement this method to allow connections between components to show up in
443
+ the interoperability UI.
444
+
445
+ Returns:
446
+ An IRISList containing all targets for this class. Default is None.
447
+ """
448
+ return None
449
+
450
+ def SendRequestSync(self, target: str, request: Union[Message, Any],
451
+ timeout: int = -1, description: Optional[str] = None) -> Any:
452
+ """DEPRECATED: use send_request_sync.
453
+
454
+ Args:
455
+ target: The target of the request
456
+ request: The request to send
457
+ timeout: The timeout in seconds, -1 means wait forever
458
+ description: A string that describes the request
459
+
460
+ Returns:
461
+ The response from the target component
462
+ """
463
+ return self.send_request_sync(target, request, timeout, description)
464
+
465
+ def SendRequestAsync(self, target: str, request: Union[Message, Any],
466
+ description: Optional[str] = None) -> None:
467
+ """DEPRECATED: use send_request_async.
468
+
469
+ Args:
470
+ target: The target of the request
471
+ request: The request to send
472
+ description: A string that describes the request
473
+ """
474
+ return self.send_request_async(target, request, description)
475
+
476
+ @staticmethod
477
+ def getAdapterType() -> Optional[str]:
478
+ """DEPRECATED: use get_adapter_type.
479
+
480
+ Returns:
481
+ Name of the registered Adapter
482
+ """
483
+ return
484
+
485
+ @staticmethod
486
+ def get_adapter_type() -> Optional[str]:
487
+ """Returns the name of the registered Adapter.
488
+
489
+ Returns:
490
+ Name of the registered Adapter
491
+ """
492
+ return
493
+
494
+ def on_get_connections(self) -> List[str]:
495
+ """The OnGetConnections() method returns all of the targets of any SendRequestSync or SendRequestAsync
496
+ calls for the class. Implement this method to allow connections between components to show up in
497
+ the interoperability UI.
498
+
499
+ Returns:
500
+ A list containing all targets for this class.
501
+ """
502
+ ## Parse the class code to find all invocations of send_request_sync and send_request_async
503
+ ## and return the targets
504
+ targer_list = []
505
+ # get the source code of the class
506
+ source = getsource(self.__class__)
507
+ # find all invocations of send_request_sync and send_request_async
508
+ for method in ['send_request_sync', 'send_request_async', 'SendRequestSync', 'SendRequestAsync']:
509
+ i = source.find(method)
510
+ while i != -1:
511
+ j = source.find("(", i)
512
+ if j != -1:
513
+ k = source.find(",", j)
514
+ if k != -1:
515
+ target = source[j+1:k]
516
+ if target.find("=") != -1:
517
+ # it's a keyword argument, remove the keyword
518
+ target = target[target.find("=")+1:].strip()
519
+ if target not in targer_list:
520
+ targer_list.append(target)
521
+ i = source.find(method, i+1)
522
+
523
+ for target in targer_list:
524
+ # if target is a string, remove the quotes
525
+ if target[0] == "'" and target[-1] == "'":
526
+ targer_list[targer_list.index(target)] = target[1:-1]
527
+ elif target[0] == '"' and target[-1] == '"':
528
+ targer_list[targer_list.index(target)] = target[1:-1]
529
+ # if target is a variable, try to find the value of the variable
530
+ else:
531
+ self.on_init()
532
+ try:
533
+ if target.find("self.") != -1:
534
+ # it's a class variable
535
+ targer_list[targer_list.index(target)] = getattr(self, target[target.find(".")+1:])
536
+ elif target.find(".") != -1:
537
+ # it's a class variable
538
+ targer_list[targer_list.index(target)] = getattr(getattr(self, target[:target.find(".")]), target[target.find(".")+1:])
539
+ else:
540
+ targer_list[targer_list.index(target)] = getattr(self, target)
541
+ except Exception as e:
542
+ pass
543
+
544
+ return targer_list
545
+
546
+ # It's a subclass of the standard JSONEncoder class that knows how to encode date/time, decimal types,
547
+ # and UUIDs.
548
+ class IrisJSONEncoder(json.JSONEncoder):
549
+ """
550
+ JSONEncoder subclass that knows how to encode date/time, decimal types, and
551
+ UUIDs.
552
+ """
553
+
554
+ def default(self, o: Any) -> Any:
555
+ if o.__class__.__name__ == 'DataFrame':
556
+ return 'dataframe:' + o.to_json(orient="table")
557
+ elif isinstance(o, datetime.datetime):
558
+ r = o.isoformat()
559
+ if o.microsecond:
560
+ r = r[:23] + r[26:]
561
+ if r.endswith("+00:00"):
562
+ r = r[:-6] + "Z"
563
+ return 'datetime:' + r
564
+ elif isinstance(o, datetime.date):
565
+ return 'date:' + o.isoformat()
566
+ elif isinstance(o, datetime.time):
567
+ r = o.isoformat()
568
+ if o.microsecond:
569
+ r = r[:12]
570
+ return 'time:' + r
571
+ elif isinstance(o, decimal.Decimal):
572
+ return 'decimal:' + str(o)
573
+ elif isinstance(o, uuid.UUID):
574
+ return 'uuid:' + str(o)
575
+ elif isinstance(o, bytes):
576
+ return 'bytes:' + base64.b64encode(o).decode("UTF-8")
577
+ elif hasattr(o, '__dict__'):
578
+ return o.__dict__
579
+ else:
580
+ return super().default(o)
581
+
582
+ # It's a JSON decoder that looks for a colon in the value of a key/value pair. If it finds one, it
583
+ # assumes the value is a string that represents a type and a value. It then converts the value to the
584
+ # appropriate type
585
+ class IrisJSONDecoder(json.JSONDecoder):
586
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
587
+ json.JSONDecoder.__init__(
588
+ self, object_hook=self.object_hook, *args, **kwargs)
589
+
590
+ def object_hook(self, obj: Dict) -> Dict:
591
+ ret = {}
592
+ for key, value in obj.items():
593
+ i = 0
594
+ if isinstance(value, str):
595
+ i = value.find(":")
596
+ if (i > 0):
597
+ typ = value[:i]
598
+ if typ == 'datetime':
599
+ ret[key] = datetime.datetime.fromisoformat(value[i+1:])
600
+ elif typ == 'date':
601
+ ret[key] = datetime.date.fromisoformat(value[i+1:])
602
+ elif typ == 'time':
603
+ ret[key] = datetime.time.fromisoformat(value[i+1:])
604
+ elif typ == 'dataframe':
605
+ module = importlib.import_module('pandas')
606
+ ret[key] = module.read_json(value[i+1:], orient="table")
607
+ elif typ == 'decimal':
608
+ ret[key] = decimal.Decimal(value[i+1:])
609
+ elif typ == 'uuid':
610
+ ret[key] = uuid.UUID(value[i+1:])
611
+ elif typ == 'bytes':
612
+ ret[key] = base64.b64decode((value[i+1:].encode("UTF-8")))
613
+ else:
614
+ ret[key] = value
615
+ else:
616
+ ret[key] = value
617
+ return ret
618
+
619
+ class _send_request_async_ng(asyncio.Future):
620
+
621
+ _message_header_id: int = 0
622
+ _queue_name: str = ""
623
+ _end_time: int = 0
624
+ _response: Any = None
625
+ _done: bool = False
626
+
627
+ def __init__(self, target: str, request: Union[Message, Any],
628
+ timeout: int = -1, description: Optional[str] = None, host: Optional[_BusinessHost] = None) -> None:
629
+ super().__init__()
630
+ self.target = target
631
+ self.request = request
632
+ self.timeout = timeout
633
+ self.description = description
634
+ self.host = host
635
+ self._iris_handle = host.iris_handle
636
+ asyncio.create_task(self.send())
637
+
638
+ async def send(self) -> None:
639
+ # init parameters
640
+ message_header_id = iris.ref()
641
+ queue_name = iris.ref()
642
+ end_time = iris.ref()
643
+ request = self.host._dispatch_serializer(self.request)
644
+
645
+ # send request
646
+ self._iris_handle.dispatchSendRequestAsyncNG(
647
+ self.target, request, self.timeout, self.description,
648
+ message_header_id, queue_name, end_time)
649
+
650
+ # get byref values
651
+ self._message_header_id = message_header_id.value
652
+ self._queue_name = queue_name.value
653
+ self._end_time = end_time.value
654
+
655
+ while not self._done:
656
+ await asyncio.sleep(0.1)
657
+ self.is_done()
658
+
659
+ self.set_result(self._response)
660
+
661
+ def is_done(self) -> None:
662
+ response = iris.ref()
663
+ status = self._iris_handle.dispatchIsRequestDone(self.timeout, self._end_time,
664
+ self._queue_name, self._message_header_id,
665
+ response)
666
+
667
+ self._response = self.host._dispatch_deserializer(response.value)
668
+
669
+ if status == 2: # message found
670
+ self._done = True
671
+ elif status == 1: # message not found
672
+ pass
673
+ else:
674
+ self._done = True
675
+ self.set_exception(RuntimeError(iris.system.Status.GetOneStatusText(status)))