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.
- grongier/cls/Grongier/PEX/BusinessOperation.cls +1 -28
- grongier/cls/Grongier/PEX/BusinessProcess.cls +1 -100
- grongier/cls/Grongier/PEX/BusinessService.cls +1 -28
- grongier/cls/Grongier/PEX/Common.cls +1 -194
- grongier/cls/Grongier/PEX/Director.cls +1 -48
- grongier/cls/Grongier/PEX/Duplex/Operation.cls +1 -26
- grongier/cls/Grongier/PEX/Duplex/Process.cls +1 -217
- grongier/cls/Grongier/PEX/Duplex/Service.cls +1 -6
- grongier/cls/Grongier/PEX/InboundAdapter.cls +1 -15
- grongier/cls/Grongier/PEX/Message.cls +1 -116
- grongier/cls/Grongier/PEX/OutboundAdapter.cls +1 -29
- grongier/cls/Grongier/PEX/PickleMessage.cls +1 -46
- grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +1 -253
- grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +1 -19
- grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +1 -19
- grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +1 -19
- grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +1 -35
- grongier/cls/Grongier/PEX/Test.cls +1 -53
- grongier/cls/Grongier/PEX/Utils.cls +1 -365
- grongier/cls/Grongier/Service/WSGI.cls +1 -307
- grongier/pex/__init__.py +11 -11
- grongier/pex/__main__.py +1 -1
- grongier/pex/_business_host.py +1 -511
- grongier/pex/_cli.py +2 -150
- grongier/pex/_common.py +1 -347
- grongier/pex/_director.py +1 -286
- grongier/pex/_utils.py +1 -369
- intersystems_iris/_ConnectionInformation.py +22 -20
- intersystems_iris/dbapi/_DBAPI.py +6 -1
- intersystems_iris/dbapi/_ResultSetRow.py +26 -15
- intersystems_iris/dbapi/preparser/_PreParser.py +4 -1
- iop/__init__.py +24 -0
- iop/__main__.py +4 -0
- iop/_business_host.py +675 -0
- iop/_business_operation.py +71 -0
- iop/_business_process.py +220 -0
- {grongier/pex → iop}/_business_service.py +2 -2
- iop/_cli.py +141 -0
- iop/_common.py +352 -0
- iop/_director.py +301 -0
- {grongier/pex → iop}/_inbound_adapter.py +1 -1
- iop/_log_manager.py +81 -0
- {grongier/pex → iop}/_message.py +1 -1
- {grongier/pex → iop}/_outbound_adapter.py +1 -1
- {grongier/pex → iop}/_private_session_duplex.py +3 -2
- {grongier/pex → iop}/_private_session_process.py +2 -2
- iop/_utils.py +458 -0
- iop/cls/IOP/BusinessOperation.cls +35 -0
- iop/cls/IOP/BusinessProcess.cls +124 -0
- iop/cls/IOP/BusinessService.cls +35 -0
- iop/cls/IOP/Common.cls +344 -0
- iop/cls/IOP/Director.cls +62 -0
- iop/cls/IOP/Duplex/Operation.cls +29 -0
- iop/cls/IOP/Duplex/Process.cls +229 -0
- iop/cls/IOP/Duplex/Service.cls +9 -0
- iop/cls/IOP/InboundAdapter.cls +22 -0
- iop/cls/IOP/Message/JSONSchema.cls +125 -0
- iop/cls/IOP/Message.cls +729 -0
- iop/cls/IOP/OutboundAdapter.cls +36 -0
- iop/cls/IOP/PickleMessage.cls +58 -0
- iop/cls/IOP/PrivateSession/Duplex.cls +260 -0
- iop/cls/IOP/PrivateSession/Message/Ack.cls +32 -0
- iop/cls/IOP/PrivateSession/Message/Poll.cls +32 -0
- iop/cls/IOP/PrivateSession/Message/Start.cls +32 -0
- iop/cls/IOP/PrivateSession/Message/Stop.cls +48 -0
- iop/cls/IOP/Service/WSGI.cls +310 -0
- iop/cls/IOP/Test.cls +85 -0
- iop/cls/IOP/Utils.cls +378 -0
- iop/wsgi/handlers.py +104 -0
- iris_pex_embedded_python-3.2.1b2.dist-info/METADATA +90 -0
- iris_pex_embedded_python-3.2.1b2.dist-info/RECORD +139 -0
- {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b2.dist-info}/WHEEL +1 -1
- iris_pex_embedded_python-3.2.1b2.dist-info/entry_points.txt +2 -0
- {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b2.dist-info}/top_level.txt +1 -1
- grongier/pex/_business_operation.py +0 -70
- grongier/pex/_business_process.py +0 -215
- iris/__init__.py +0 -60
- iris/__init__.pyi +0 -236
- iris/iris_ipm.py +0 -40
- iris/iris_ipm.pyi +0 -17
- iris_pex_embedded_python-2.3.27b2.dist-info/METADATA +0 -1384
- iris_pex_embedded_python-2.3.27b2.dist-info/RECORD +0 -113
- iris_pex_embedded_python-2.3.27b2.dist-info/entry_points.txt +0 -2
- {grongier/pex → iop}/_pickle_message.py +0 -0
- {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b2.dist-info}/LICENSE +0 -0
grongier/pex/_business_host.py
CHANGED
|
@@ -1,511 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import pickle
|
|
3
|
-
import codecs
|
|
4
|
-
import uuid
|
|
5
|
-
import decimal
|
|
6
|
-
import base64
|
|
7
|
-
import json
|
|
8
|
-
import importlib
|
|
9
|
-
import iris
|
|
10
|
-
|
|
11
|
-
from functools import wraps
|
|
12
|
-
|
|
13
|
-
from inspect import signature, getsource
|
|
14
|
-
|
|
15
|
-
from dacite import from_dict, Config
|
|
16
|
-
|
|
17
|
-
from grongier.pex._common import _Common
|
|
18
|
-
from grongier.pex._utils import _Utils
|
|
19
|
-
|
|
20
|
-
class _BusinessHost(_Common):
|
|
21
|
-
""" This is a superclass for BusinessService, BusinesProcess, and BusinessOperation that
|
|
22
|
-
defines common methods. It is a subclass of Common.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
buffer:int = 64000
|
|
26
|
-
DISPATCH = []
|
|
27
|
-
|
|
28
|
-
def input_serialzer(fonction):
|
|
29
|
-
"""
|
|
30
|
-
It takes a function as an argument, and returns a function that takes the same arguments as the
|
|
31
|
-
original function, but serializes the arguments before passing them to the original function
|
|
32
|
-
|
|
33
|
-
:param fonction: the function that will be decorated
|
|
34
|
-
:return: The function dispatch_serializer is being returned.
|
|
35
|
-
"""
|
|
36
|
-
def dispatch_serializer(self,*params, **param2):
|
|
37
|
-
# Handle positional arguments
|
|
38
|
-
serialized=[]
|
|
39
|
-
for param in params:
|
|
40
|
-
serialized.append(self._dispatch_serializer(param))
|
|
41
|
-
# Handle keyword arguments
|
|
42
|
-
for key, value in param2.items():
|
|
43
|
-
param2[key] = self._dispatch_serializer(value)
|
|
44
|
-
return fonction(self,*serialized, **param2)
|
|
45
|
-
return dispatch_serializer
|
|
46
|
-
|
|
47
|
-
def input_serialzer_param(position:int,name:str):
|
|
48
|
-
"""
|
|
49
|
-
It takes a function as an argument, and returns a function that takes the same arguments as the
|
|
50
|
-
original function, but serializes the arguments before passing them to the original function
|
|
51
|
-
|
|
52
|
-
:param fonction: the function that will be decorated
|
|
53
|
-
:return: The function dispatch_serializer is being returned.
|
|
54
|
-
"""
|
|
55
|
-
def input_serialzer_param(fonction):
|
|
56
|
-
@wraps(fonction)
|
|
57
|
-
def dispatch_serializer(self,*params, **param2):
|
|
58
|
-
# Handle positional arguments
|
|
59
|
-
serialized=[]
|
|
60
|
-
for i,param in enumerate(params):
|
|
61
|
-
if i == position:
|
|
62
|
-
serialized.append(self._dispatch_serializer(param))
|
|
63
|
-
else:
|
|
64
|
-
serialized.append(param)
|
|
65
|
-
# Handle keyword arguments
|
|
66
|
-
for key, value in param2.items():
|
|
67
|
-
if key == name:
|
|
68
|
-
param2[key] = self._dispatch_serializer(value)
|
|
69
|
-
return fonction(self,*serialized, **param2)
|
|
70
|
-
return dispatch_serializer
|
|
71
|
-
return input_serialzer_param
|
|
72
|
-
|
|
73
|
-
def output_deserialzer(fonction):
|
|
74
|
-
"""
|
|
75
|
-
It takes a function as an argument, and returns a function that takes the same arguments as the
|
|
76
|
-
original function, but returns the result of the original function passed to the
|
|
77
|
-
`_dispatch_deserializer` function
|
|
78
|
-
|
|
79
|
-
:param fonction: the function that will be decorated
|
|
80
|
-
:return: The function dispatch_deserializer is being returned.
|
|
81
|
-
"""
|
|
82
|
-
def dispatch_deserializer(self,*params, **param2):
|
|
83
|
-
return self._dispatch_deserializer(fonction(self,*params, **param2))
|
|
84
|
-
|
|
85
|
-
return dispatch_deserializer
|
|
86
|
-
|
|
87
|
-
def input_deserialzer(fonction):
|
|
88
|
-
"""
|
|
89
|
-
It takes a function as input, and returns a function that takes the same arguments as the input
|
|
90
|
-
function, but deserializes the arguments before passing them to the input function
|
|
91
|
-
|
|
92
|
-
:param fonction: the function that will be decorated
|
|
93
|
-
:return: The function dispatch_deserializer is being returned.
|
|
94
|
-
"""
|
|
95
|
-
def dispatch_deserializer(self,*params, **param2):
|
|
96
|
-
# Handle positional arguments
|
|
97
|
-
serialized=[]
|
|
98
|
-
for param in params:
|
|
99
|
-
serialized.append(self._dispatch_deserializer(param))
|
|
100
|
-
# Handle keyword arguments
|
|
101
|
-
for key, value in param2.items():
|
|
102
|
-
param2[key] = self._dispatch_deserializer(value)
|
|
103
|
-
return fonction(self,*serialized, **param2)
|
|
104
|
-
return dispatch_deserializer
|
|
105
|
-
|
|
106
|
-
def output_serialzer(fonction):
|
|
107
|
-
"""
|
|
108
|
-
It takes a function as an argument, and returns a function that takes the same arguments as the
|
|
109
|
-
original function, and returns the result of the original function, after passing it through the
|
|
110
|
-
_dispatch_serializer function
|
|
111
|
-
|
|
112
|
-
:param fonction: The function that is being decorated
|
|
113
|
-
:return: The function dispatch_serializer is being returned.
|
|
114
|
-
"""
|
|
115
|
-
def dispatch_serializer(self,*params, **param2):
|
|
116
|
-
return self._dispatch_serializer(fonction(self,*params, **param2))
|
|
117
|
-
return dispatch_serializer
|
|
118
|
-
|
|
119
|
-
@input_serialzer_param(1,'request')
|
|
120
|
-
@output_deserialzer
|
|
121
|
-
def send_request_sync(self, target, request, timeout=-1, description=None):
|
|
122
|
-
""" Send the specified message to the target business process or business operation synchronously.
|
|
123
|
-
|
|
124
|
-
Parameters:
|
|
125
|
-
target: a string that specifies the name of the business process or operation to receive the request.
|
|
126
|
-
The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.
|
|
127
|
-
request: specifies the message to send to the target. The request is either an instance of a class that is a subclass of Message class or of IRISObject class.
|
|
128
|
-
If the target is a build-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.
|
|
129
|
-
timeout: an optional integer that specifies the number of seconds to wait before treating the send request as a failure. The default value is -1, which means wait forever.
|
|
130
|
-
description: an optional string parameter that sets a description property in the message header. The default is None.
|
|
131
|
-
Returns:
|
|
132
|
-
the response object from target.
|
|
133
|
-
Raises:
|
|
134
|
-
TypeError: if request is not of type Message or IRISObject.
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
-
return self.iris_handle.dispatchSendRequestSync(target,request,timeout,description)
|
|
138
|
-
|
|
139
|
-
@input_serialzer_param(1,'request')
|
|
140
|
-
def send_request_async(self, target, request, description=None):
|
|
141
|
-
""" Send the specified message to the target business process or business operation asynchronously.
|
|
142
|
-
Parameters:
|
|
143
|
-
target: a string that specifies the name of the business process or operation to receive the request.
|
|
144
|
-
The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.
|
|
145
|
-
request: specifies the message to send to the target. The request is an instance of IRISObject or of a subclass of Message.
|
|
146
|
-
If the target is a built-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.
|
|
147
|
-
description: an optional string parameter that sets a description property in the message header. The default is None.
|
|
148
|
-
|
|
149
|
-
Raises:
|
|
150
|
-
TypeError: if request is not of type Message or IRISObject.
|
|
151
|
-
"""
|
|
152
|
-
|
|
153
|
-
return self.iris_handle.dispatchSendRequestAsync(target,request,description)
|
|
154
|
-
|
|
155
|
-
def _serialize_pickle_message(self,message):
|
|
156
|
-
""" Converts a python dataclass message into an iris grongier.pex.message.
|
|
157
|
-
|
|
158
|
-
Parameters:
|
|
159
|
-
message: The message to serialize, an instance of a class that is a subclass of Message.
|
|
160
|
-
|
|
161
|
-
Returns:
|
|
162
|
-
string: The message in json format.
|
|
163
|
-
"""
|
|
164
|
-
|
|
165
|
-
pickle_string = codecs.encode(pickle.dumps(message), "base64").decode()
|
|
166
|
-
module = message.__class__.__module__
|
|
167
|
-
classname = message.__class__.__name__
|
|
168
|
-
|
|
169
|
-
msg = iris.cls('Grongier.PEX.PickleMessage')._New()
|
|
170
|
-
msg.classname = module + "." + classname
|
|
171
|
-
|
|
172
|
-
stream = _Utils.string_to_stream(pickle_string)
|
|
173
|
-
msg.jstr = stream
|
|
174
|
-
|
|
175
|
-
return msg
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def _dispatch_serializer(self,message):
|
|
179
|
-
"""
|
|
180
|
-
If the message is a message instance, serialize it as a message, otherwise, if it's a pickle message
|
|
181
|
-
instance, serialize it as a pickle message, otherwise, return the message
|
|
182
|
-
|
|
183
|
-
:param message: The message to be serialized
|
|
184
|
-
:return: The serialized message
|
|
185
|
-
"""
|
|
186
|
-
if (message is not None and self._is_message_instance(message)):
|
|
187
|
-
return self._serialize_message(message)
|
|
188
|
-
elif (message is not None and self._is_pickle_message_instance(message)):
|
|
189
|
-
return self._serialize_pickle_message(message)
|
|
190
|
-
elif (message is not None and self._is_iris_object_instance(message)):
|
|
191
|
-
return message
|
|
192
|
-
elif (message is None or message == ""):
|
|
193
|
-
return message
|
|
194
|
-
else:
|
|
195
|
-
# todo : decorator takes care of all the parameters, so this should never happen
|
|
196
|
-
# return message
|
|
197
|
-
raise TypeError("The message must be an instance of a class that is a subclass of Message or IRISObject %Persistent class.")
|
|
198
|
-
|
|
199
|
-
def _serialize_message(self,message):
|
|
200
|
-
""" Converts a python dataclass message into an iris grongier.pex.message.
|
|
201
|
-
|
|
202
|
-
Parameters:
|
|
203
|
-
message: The message to serialize, an instance of a class that is a subclass of Message.
|
|
204
|
-
|
|
205
|
-
Returns:
|
|
206
|
-
string: The message in json format.
|
|
207
|
-
"""
|
|
208
|
-
json_string = json.dumps(message, cls=IrisJSONEncoder, ensure_ascii=False)
|
|
209
|
-
module = message.__class__.__module__
|
|
210
|
-
classname = message.__class__.__name__
|
|
211
|
-
|
|
212
|
-
msg = iris.cls('Grongier.PEX.Message')._New()
|
|
213
|
-
msg.classname = module + "." + classname
|
|
214
|
-
|
|
215
|
-
stream = _Utils.string_to_stream(json_string)
|
|
216
|
-
msg.jstr = stream
|
|
217
|
-
|
|
218
|
-
return msg
|
|
219
|
-
|
|
220
|
-
def _deserialize_pickle_message(self,serial):
|
|
221
|
-
"""
|
|
222
|
-
Converts an iris grongier.pex.message into an python dataclass message.
|
|
223
|
-
|
|
224
|
-
"""
|
|
225
|
-
string = _Utils.stream_to_string(serial.jstr)
|
|
226
|
-
|
|
227
|
-
msg = pickle.loads(codecs.decode(string.encode(), "base64"))
|
|
228
|
-
return msg
|
|
229
|
-
|
|
230
|
-
def _dispatch_deserializer(self,serial):
|
|
231
|
-
"""
|
|
232
|
-
If the serialized object is a Message, deserialize it as a Message, otherwise deserialize it as a
|
|
233
|
-
PickleMessage
|
|
234
|
-
|
|
235
|
-
:param serial: The serialized object
|
|
236
|
-
:return: The return value is a tuple of the form (serial, serial_type)
|
|
237
|
-
"""
|
|
238
|
-
if (serial is not None and type(serial).__module__.find('iris') == 0) and serial._IsA("Grongier.PEX.Message"):
|
|
239
|
-
return self._deserialize_message(serial)
|
|
240
|
-
elif (serial is not None and type(serial).__module__.find('iris') == 0) and serial._IsA("Grongier.PEX.PickleMessage"):
|
|
241
|
-
return self._deserialize_pickle_message(serial)
|
|
242
|
-
else:
|
|
243
|
-
return serial
|
|
244
|
-
|
|
245
|
-
def _deserialize_message(self,serial):
|
|
246
|
-
"""
|
|
247
|
-
Converts an iris grongier.pex.message into an python dataclass message.
|
|
248
|
-
"""
|
|
249
|
-
|
|
250
|
-
if (serial.classname is None):
|
|
251
|
-
raise ValueError("JSON message malformed, must include classname")
|
|
252
|
-
classname = serial.classname
|
|
253
|
-
|
|
254
|
-
j = classname.rindex(".")
|
|
255
|
-
if (j <=0):
|
|
256
|
-
raise ValueError("Classname must include a module: " + classname)
|
|
257
|
-
try:
|
|
258
|
-
module = importlib.import_module(classname[:j])
|
|
259
|
-
msg = getattr(module, classname[j+1:])
|
|
260
|
-
except Exception:
|
|
261
|
-
raise ImportError("Class not found: " + classname)
|
|
262
|
-
|
|
263
|
-
string = _Utils.stream_to_string(serial.jstr)
|
|
264
|
-
|
|
265
|
-
jdict = json.loads(string, cls=IrisJSONDecoder)
|
|
266
|
-
msg = self._dataclass_from_dict(msg,jdict)
|
|
267
|
-
return msg
|
|
268
|
-
|
|
269
|
-
def _dataclass_from_dict(self,klass, dikt):
|
|
270
|
-
"""
|
|
271
|
-
> If the field is not in the dataclass, then add it as an attribute
|
|
272
|
-
|
|
273
|
-
:param klass: The dataclass to convert to
|
|
274
|
-
:param dikt: the dictionary to convert to a dataclass
|
|
275
|
-
:return: A dataclass object with the fields of the dataclass and the fields of the dictionary.
|
|
276
|
-
"""
|
|
277
|
-
ret = from_dict(klass, dikt, Config(check_types=False))
|
|
278
|
-
|
|
279
|
-
try:
|
|
280
|
-
fieldtypes = klass.__annotations__
|
|
281
|
-
except Exception as e:
|
|
282
|
-
fieldtypes = []
|
|
283
|
-
|
|
284
|
-
for key,val in dikt.items():
|
|
285
|
-
if key not in fieldtypes:
|
|
286
|
-
setattr(ret, key, val)
|
|
287
|
-
return ret
|
|
288
|
-
|
|
289
|
-
def _dispach_message(self, request):
|
|
290
|
-
"""
|
|
291
|
-
It takes a request object, and returns a response object
|
|
292
|
-
|
|
293
|
-
:param request: The request object
|
|
294
|
-
:return: The return value is the result of the method call.
|
|
295
|
-
"""
|
|
296
|
-
|
|
297
|
-
call = 'on_message'
|
|
298
|
-
|
|
299
|
-
module = request.__class__.__module__
|
|
300
|
-
classname = request.__class__.__name__
|
|
301
|
-
|
|
302
|
-
for msg,method in self.DISPATCH:
|
|
303
|
-
if msg == module+"."+classname:
|
|
304
|
-
call = method
|
|
305
|
-
|
|
306
|
-
return getattr(self,call)(request)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
def _create_dispatch(self):
|
|
310
|
-
"""
|
|
311
|
-
It creates a list of tuples, where each tuple contains the name of a class and the name of a method
|
|
312
|
-
that takes an instance of that class as its only argument
|
|
313
|
-
:return: A list of tuples.
|
|
314
|
-
"""
|
|
315
|
-
if len(self.DISPATCH) == 0:
|
|
316
|
-
#get all function in current BO
|
|
317
|
-
method_list = [func for func in dir(self) if callable(getattr(self, func)) and not func.startswith("_")]
|
|
318
|
-
for method in method_list:
|
|
319
|
-
#get signature of current function
|
|
320
|
-
try:
|
|
321
|
-
param = signature(getattr(self, method)).parameters
|
|
322
|
-
# Handle staticmethod
|
|
323
|
-
except ValueError as e:
|
|
324
|
-
param=''
|
|
325
|
-
#one parameter
|
|
326
|
-
if (len(param)==1):
|
|
327
|
-
#get parameter type
|
|
328
|
-
annotation = str(param[list(param)[0]].annotation)
|
|
329
|
-
#trim annotation format <class 'toto'>
|
|
330
|
-
i = annotation.find("'")
|
|
331
|
-
j = annotation.rfind("'")
|
|
332
|
-
#if end is not found
|
|
333
|
-
if j == -1:
|
|
334
|
-
j = None
|
|
335
|
-
classname = annotation[i+1:j]
|
|
336
|
-
self.DISPATCH.append((classname,method))
|
|
337
|
-
return
|
|
338
|
-
|
|
339
|
-
@staticmethod
|
|
340
|
-
def OnGetConnections():
|
|
341
|
-
""" The OnGetConnections() method returns all of the targets of any SendRequestSync or SendRequestAsync
|
|
342
|
-
calls for the class. Implement this method to allow connections between components to show up in
|
|
343
|
-
the interoperability UI.
|
|
344
|
-
|
|
345
|
-
Returns:
|
|
346
|
-
An IRISList containing all targets for this class. Default is None.
|
|
347
|
-
"""
|
|
348
|
-
return None
|
|
349
|
-
|
|
350
|
-
def SendRequestSync(self, target, request, timeout=-1, description=None):
|
|
351
|
-
""" DEPRECATED : use send_request_sync
|
|
352
|
-
`SendRequestSync` is a function that sends a request to a target and waits for a response
|
|
353
|
-
|
|
354
|
-
:param target: The target of the request
|
|
355
|
-
:param request: The request to send
|
|
356
|
-
:param timeout: The timeout in seconds. If the timeout is negative, the default timeout will be used
|
|
357
|
-
:param description: A string that describes the request. This is used for logging purposes
|
|
358
|
-
:return: The return value is a tuple of (response, status).
|
|
359
|
-
"""
|
|
360
|
-
return self.send_request_sync(target,request,timeout,description)
|
|
361
|
-
|
|
362
|
-
def SendRequestAsync(self, target, request, description=None):
|
|
363
|
-
""" DEPRECATED : use send_request_async
|
|
364
|
-
It takes a target, a request, and a description, and returns a send_request_async function
|
|
365
|
-
|
|
366
|
-
:param target: The target of the request. This is the name of the function you want to call
|
|
367
|
-
:param request: The request to send
|
|
368
|
-
:param description: A string that describes the request
|
|
369
|
-
:return: The return value is a Future object.
|
|
370
|
-
"""
|
|
371
|
-
return self.send_request_async(target,request,description)
|
|
372
|
-
|
|
373
|
-
@staticmethod
|
|
374
|
-
def getAdapterType():
|
|
375
|
-
""" DEPRECATED : use get_adapter_type
|
|
376
|
-
Name of the registred Adapter
|
|
377
|
-
"""
|
|
378
|
-
return
|
|
379
|
-
|
|
380
|
-
@staticmethod
|
|
381
|
-
def get_adapter_type():
|
|
382
|
-
"""
|
|
383
|
-
Name of the registred Adapter
|
|
384
|
-
"""
|
|
385
|
-
return
|
|
386
|
-
|
|
387
|
-
def on_get_connections(self) -> list:
|
|
388
|
-
"""
|
|
389
|
-
The OnGetConnections() method returns all of the targets of any SendRequestSync or SendRequestAsync
|
|
390
|
-
calls for the class. Implement this method to allow connections between components to show up in
|
|
391
|
-
the interoperability UI.
|
|
392
|
-
|
|
393
|
-
Returns:
|
|
394
|
-
An IRISList containing all targets for this class. Default is None.
|
|
395
|
-
"""
|
|
396
|
-
## Parse the class code to find all invocations of send_request_sync and send_request_async
|
|
397
|
-
## and return the targets
|
|
398
|
-
targer_list = []
|
|
399
|
-
# get the source code of the class
|
|
400
|
-
source = getsource(self.__class__)
|
|
401
|
-
# find all invocations of send_request_sync and send_request_async
|
|
402
|
-
for method in ['send_request_sync','send_request_async','SendRequestSync','SendRequestAsync']:
|
|
403
|
-
i = source.find(method)
|
|
404
|
-
while i != -1:
|
|
405
|
-
j = source.find("(",i)
|
|
406
|
-
if j != -1:
|
|
407
|
-
k = source.find(",",j)
|
|
408
|
-
if k != -1:
|
|
409
|
-
target = source[j+1:k]
|
|
410
|
-
if target.find("=") != -1:
|
|
411
|
-
# it's a keyword argument, remove the keyword
|
|
412
|
-
target = target[target.find("=")+1:].strip()
|
|
413
|
-
if target not in targer_list:
|
|
414
|
-
targer_list.append(target)
|
|
415
|
-
i = source.find(method,i+1)
|
|
416
|
-
|
|
417
|
-
for target in targer_list:
|
|
418
|
-
# if target is a string, remove the quotes
|
|
419
|
-
if target[0] == "'" and target[-1] == "'":
|
|
420
|
-
targer_list[targer_list.index(target)] = target[1:-1]
|
|
421
|
-
elif target[0] == '"' and target[-1] == '"':
|
|
422
|
-
targer_list[targer_list.index(target)] = target[1:-1]
|
|
423
|
-
# if target is a variable, try to find the value of the variable
|
|
424
|
-
else:
|
|
425
|
-
self.on_init()
|
|
426
|
-
try:
|
|
427
|
-
if target.find("self.") != -1:
|
|
428
|
-
# it's a class variable
|
|
429
|
-
targer_list[targer_list.index(target)] = getattr(self,target[target.find(".")+1:])
|
|
430
|
-
elif target.find(".") != -1:
|
|
431
|
-
# it's a class variable
|
|
432
|
-
targer_list[targer_list.index(target)] = getattr(getattr(self,target[:target.find(".")]),target[target.find(".")+1:])
|
|
433
|
-
else:
|
|
434
|
-
targer_list[targer_list.index(target)] = getattr(self,target)
|
|
435
|
-
except Exception as e:
|
|
436
|
-
pass
|
|
437
|
-
|
|
438
|
-
return targer_list
|
|
439
|
-
|
|
440
|
-
# It's a subclass of the standard JSONEncoder class that knows how to encode date/time, decimal types,
|
|
441
|
-
# and UUIDs.
|
|
442
|
-
class IrisJSONEncoder(json.JSONEncoder):
|
|
443
|
-
"""
|
|
444
|
-
JSONEncoder subclass that knows how to encode date/time, decimal types, and
|
|
445
|
-
UUIDs.
|
|
446
|
-
"""
|
|
447
|
-
|
|
448
|
-
def default(self, o):
|
|
449
|
-
if o.__class__.__name__ == 'DataFrame':
|
|
450
|
-
return 'dataframe:'+o.to_json(orient="table")
|
|
451
|
-
elif isinstance(o, datetime.datetime):
|
|
452
|
-
r = o.isoformat()
|
|
453
|
-
if o.microsecond:
|
|
454
|
-
r = r[:23] + r[26:]
|
|
455
|
-
if r.endswith("+00:00"):
|
|
456
|
-
r = r[:-6] + "Z"
|
|
457
|
-
return 'datetime:'+r
|
|
458
|
-
elif isinstance(o, datetime.date):
|
|
459
|
-
return 'date:'+o.isoformat()
|
|
460
|
-
elif isinstance(o, datetime.time):
|
|
461
|
-
r = o.isoformat()
|
|
462
|
-
if o.microsecond:
|
|
463
|
-
r = r[:12]
|
|
464
|
-
return 'time:'+r
|
|
465
|
-
elif isinstance(o, decimal.Decimal):
|
|
466
|
-
return 'decimal:'+str(o)
|
|
467
|
-
elif isinstance(o, uuid.UUID):
|
|
468
|
-
return 'uuid:'+str(o)
|
|
469
|
-
elif isinstance(o, bytes):
|
|
470
|
-
return 'bytes:'+base64.b64encode(o).decode("UTF-8")
|
|
471
|
-
elif hasattr(o, '__dict__'):
|
|
472
|
-
return o.__dict__
|
|
473
|
-
else:
|
|
474
|
-
return super().default(o)
|
|
475
|
-
|
|
476
|
-
# It's a JSON decoder that looks for a colon in the value of a key/value pair. If it finds one, it
|
|
477
|
-
# assumes the value is a string that represents a type and a value. It then converts the value to the
|
|
478
|
-
# appropriate type
|
|
479
|
-
class IrisJSONDecoder(json.JSONDecoder):
|
|
480
|
-
def __init__(self, *args, **kwargs):
|
|
481
|
-
json.JSONDecoder.__init__(
|
|
482
|
-
self, object_hook=self.object_hook, *args, **kwargs)
|
|
483
|
-
|
|
484
|
-
def object_hook(self, obj):
|
|
485
|
-
ret = {}
|
|
486
|
-
for key, value in obj.items():
|
|
487
|
-
i = 0
|
|
488
|
-
if isinstance(value, str):
|
|
489
|
-
i = value.find(":")
|
|
490
|
-
if (i>0):
|
|
491
|
-
typ = value[:i]
|
|
492
|
-
if typ == 'datetime':
|
|
493
|
-
ret[key] = datetime.datetime.fromisoformat(value[i+1:])
|
|
494
|
-
elif typ == 'date':
|
|
495
|
-
ret[key] = datetime.date.fromisoformat(value[i+1:])
|
|
496
|
-
elif typ == 'time':
|
|
497
|
-
ret[key] = datetime.time.fromisoformat(value[i+1:])
|
|
498
|
-
elif typ == 'dataframe':
|
|
499
|
-
module = importlib.import_module('pandas')
|
|
500
|
-
ret[key] = module.read_json(value[i+1:],orient="table")
|
|
501
|
-
elif typ == 'decimal':
|
|
502
|
-
ret[key] = decimal.Decimal(value[i+1:])
|
|
503
|
-
elif typ == 'uuid':
|
|
504
|
-
ret[key] = uuid.UUID(value[i+1:])
|
|
505
|
-
elif typ == 'bytes':
|
|
506
|
-
ret[key] = base64.b64decode((value[i+1:].encode("UTF-8")))
|
|
507
|
-
else:
|
|
508
|
-
ret[key] = value
|
|
509
|
-
else:
|
|
510
|
-
ret[key] = value
|
|
511
|
-
return ret
|
|
1
|
+
from iop._business_host import _BusinessHost
|
grongier/pex/_cli.py
CHANGED
|
@@ -1,152 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
# it's a command line interface to manage productions
|
|
3
|
-
# eg :
|
|
4
|
-
# python3 -m grongier.pex -h : display help and the default production name
|
|
5
|
-
# python3 -m grongier.pex -l : list productions
|
|
6
|
-
# python3 -m grongier.pex -d <production_name> : set the default production to <production_name>
|
|
7
|
-
# python3 -m grongier.pex -s <production_name> : start a production named <production_name> if <production_name> is not set, the default production is started
|
|
8
|
-
# python3 -m grongier.pex -k <production_name> : stop a production named <production_name> if <production_name> is not set, the default production is killed
|
|
9
|
-
# python3 -m grongier.pex -r <production_name> : restart a production named <production_name> if <production_name> is not set, the default production is restarted
|
|
10
|
-
# python3 -m grongier.pex -m <settings_file> : migrate a production and classes with the settings file <settings_file>
|
|
11
|
-
# python3 -m grongier.pex -x <production_name> : export a production named <production_name> if <production_name> is not set, the default production is exported
|
|
12
|
-
from grongier.pex._director import _Director
|
|
13
|
-
from grongier.pex._utils import _Utils
|
|
14
|
-
|
|
15
|
-
import argparse
|
|
16
|
-
import json
|
|
17
|
-
import os
|
|
18
|
-
from importlib.metadata import version
|
|
19
|
-
|
|
20
|
-
def parse_args():
|
|
21
|
-
# parse arguments
|
|
22
|
-
main_parser = argparse.ArgumentParser()
|
|
23
|
-
parser = main_parser.add_mutually_exclusive_group()
|
|
24
|
-
parser.add_argument('-d', '--default', help='set the default production', nargs='?', const='not_set')
|
|
25
|
-
parser.add_argument('-l', '--list', help='list productions', action='store_true')
|
|
26
|
-
parser.add_argument('-s', '--start', help='start a production', nargs='?', const='not_set')
|
|
27
|
-
start = main_parser.add_argument_group('start arguments')
|
|
28
|
-
start.add_argument('-D', '--detach', help='start a production in detach mode', action='store_true')
|
|
29
|
-
parser.add_argument('-S', '--stop', help='stop a production', action='store_true')
|
|
30
|
-
parser.add_argument('-k', '--kill', help='kill a production', action='store_true')
|
|
31
|
-
parser.add_argument('-r', '--restart', help='restart a production', action='store_true')
|
|
32
|
-
parser.add_argument('-x', '--status', help='status a production', action='store_true')
|
|
33
|
-
parser.add_argument('-m', '-M', '--migrate', help='migrate production and classes with settings file')
|
|
34
|
-
parser.add_argument('-e', '--export', help='export a production', nargs='?', const='not_set')
|
|
35
|
-
parser.add_argument('-v', '--version', help='display version', action='store_true')
|
|
36
|
-
parser.add_argument('-L', '--log', help='display log', nargs='?', const='not_set')
|
|
37
|
-
parser.add_argument('-i', '--init', help='init the pex module in iris', nargs='?', const='not_set')
|
|
38
|
-
parser.add_argument('-t', '--test', help='test the pex module in iris', nargs='?', const='not_set')
|
|
39
|
-
test = main_parser.add_argument_group('test arguments')
|
|
40
|
-
# add classname argument
|
|
41
|
-
test.add_argument('-C', '--classname', help='test classname', nargs='?', const='not_set')
|
|
42
|
-
# body argument
|
|
43
|
-
test.add_argument('-B', '--body', help='test body', nargs='?', const='not_set')
|
|
44
|
-
return main_parser
|
|
45
|
-
|
|
46
|
-
def main(argv=None):
|
|
47
|
-
# build arguments
|
|
48
|
-
parser = parse_args()
|
|
49
|
-
args = parser.parse_args(argv)
|
|
50
|
-
|
|
51
|
-
if args.default:
|
|
52
|
-
# set default production
|
|
53
|
-
if args.default == 'not_set':
|
|
54
|
-
# display default production name
|
|
55
|
-
print(_Director.get_default_production())
|
|
56
|
-
else:
|
|
57
|
-
_Director.set_default_production(args.default)
|
|
58
|
-
|
|
59
|
-
elif args.list:
|
|
60
|
-
# display list of productions
|
|
61
|
-
dikt = _Director.list_productions()
|
|
62
|
-
print(json.dumps(dikt, indent=4))
|
|
63
|
-
|
|
64
|
-
elif args.start:
|
|
65
|
-
production_name = None
|
|
66
|
-
if args.start == 'not_set':
|
|
67
|
-
# start default production
|
|
68
|
-
production_name = _Director.get_default_production()
|
|
69
|
-
else:
|
|
70
|
-
# start production with name
|
|
71
|
-
production_name = args.start
|
|
72
|
-
if args.detach:
|
|
73
|
-
# start production in detach mode
|
|
74
|
-
_Director.start_production(production_name)
|
|
75
|
-
print(f"Production {production_name} started")
|
|
76
|
-
else:
|
|
77
|
-
_Director.start_production_with_log(production_name)
|
|
78
|
-
|
|
79
|
-
elif args.init:
|
|
80
|
-
if args.init == 'not_set':
|
|
81
|
-
# set arg to None
|
|
82
|
-
args.init = None
|
|
83
|
-
_Utils.setup(args.start)
|
|
84
|
-
|
|
85
|
-
elif args.kill:
|
|
86
|
-
# kill a production
|
|
87
|
-
_Director.shutdown_production()
|
|
88
|
-
|
|
89
|
-
elif args.restart:
|
|
90
|
-
# restart a production
|
|
91
|
-
_Director.restart_production()
|
|
92
|
-
|
|
93
|
-
elif args.migrate:
|
|
94
|
-
# check if migrate is absolute path
|
|
95
|
-
if os.path.isabs(args.migrate):
|
|
96
|
-
# migrate a production with absolute path
|
|
97
|
-
_Utils.migrate(args.migrate)
|
|
98
|
-
else:
|
|
99
|
-
# migrate a production with relative path
|
|
100
|
-
_Utils.migrate(os.path.join(os.getcwd(), args.migrate))
|
|
101
|
-
|
|
102
|
-
elif args.version:
|
|
103
|
-
# display version
|
|
104
|
-
print(version('iris-pex-embedded-python'))
|
|
105
|
-
|
|
106
|
-
elif args.log:
|
|
107
|
-
# display log
|
|
108
|
-
if args.log == 'not_set':
|
|
109
|
-
# display default production log
|
|
110
|
-
_Director.log_production()
|
|
111
|
-
else:
|
|
112
|
-
_Director.log_production_top(args.log)
|
|
113
|
-
|
|
114
|
-
elif args.stop:
|
|
115
|
-
# stop a production
|
|
116
|
-
_Director.stop_production()
|
|
117
|
-
print(f"Production {_Director.get_default_production()} stopped")
|
|
118
|
-
|
|
119
|
-
elif args.status:
|
|
120
|
-
dikt=_Director.status_production()
|
|
121
|
-
print(json.dumps(dikt, indent=4))
|
|
122
|
-
|
|
123
|
-
elif args.test:
|
|
124
|
-
classname = None
|
|
125
|
-
body = None
|
|
126
|
-
if args.test == 'not_set':
|
|
127
|
-
# set arg to None
|
|
128
|
-
args.test = None
|
|
129
|
-
if args.classname:
|
|
130
|
-
classname = args.classname
|
|
131
|
-
if args.body:
|
|
132
|
-
body = args.body
|
|
133
|
-
response = _Director.test_component(args.test, classname=classname, body=body)
|
|
134
|
-
print(response)
|
|
135
|
-
|
|
136
|
-
elif args.export:
|
|
137
|
-
if args.export == 'not_set':
|
|
138
|
-
# export default production
|
|
139
|
-
args.export=_Director.get_default_production()
|
|
140
|
-
|
|
141
|
-
dikt = _Utils.export_production(args.export)
|
|
142
|
-
print(json.dumps(dikt, indent=4))
|
|
143
|
-
|
|
144
|
-
else:
|
|
145
|
-
# display help
|
|
146
|
-
parser.print_help()
|
|
147
|
-
print()
|
|
148
|
-
print("Default production : " + _Director.get_default_production())
|
|
149
|
-
|
|
1
|
+
from iop._cli import main
|
|
150
2
|
|
|
151
3
|
if __name__ == '__main__':
|
|
152
|
-
main()
|
|
4
|
+
main()
|