iris-pex-embedded-python 2.3.28b3__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (71) hide show
  1. grongier/cls/Grongier/PEX/BusinessOperation.cls +1 -28
  2. grongier/cls/Grongier/PEX/BusinessProcess.cls +1 -112
  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 +1 -152
  25. grongier/pex/_common.py +1 -347
  26. grongier/pex/_director.py +1 -286
  27. grongier/pex/_utils.py +1 -369
  28. iop/__init__.py +24 -0
  29. iop/__main__.py +4 -0
  30. iop/_business_host.py +511 -0
  31. {grongier/pex → iop}/_business_operation.py +1 -1
  32. {grongier/pex → iop}/_business_process.py +1 -1
  33. {grongier/pex → iop}/_business_service.py +1 -1
  34. iop/_cli.py +152 -0
  35. iop/_common.py +349 -0
  36. iop/_director.py +286 -0
  37. {grongier/pex → iop}/_inbound_adapter.py +1 -1
  38. {grongier/pex → iop}/_outbound_adapter.py +1 -1
  39. {grongier/pex → iop}/_private_session_duplex.py +1 -1
  40. {grongier/pex → iop}/_private_session_process.py +2 -2
  41. iop/_utils.py +374 -0
  42. iop/cls/IOP/BusinessOperation.cls +35 -0
  43. iop/cls/IOP/BusinessProcess.cls +124 -0
  44. iop/cls/IOP/BusinessService.cls +35 -0
  45. iop/cls/IOP/Common.cls +203 -0
  46. iop/cls/IOP/Director.cls +57 -0
  47. iop/cls/IOP/Duplex/Operation.cls +29 -0
  48. iop/cls/IOP/Duplex/Process.cls +229 -0
  49. iop/cls/IOP/Duplex/Service.cls +9 -0
  50. iop/cls/IOP/InboundAdapter.cls +22 -0
  51. iop/cls/IOP/Message.cls +128 -0
  52. iop/cls/IOP/OutboundAdapter.cls +36 -0
  53. iop/cls/IOP/PickleMessage.cls +58 -0
  54. iop/cls/IOP/PrivateSession/Duplex.cls +260 -0
  55. iop/cls/IOP/PrivateSession/Message/Ack.cls +32 -0
  56. iop/cls/IOP/PrivateSession/Message/Poll.cls +32 -0
  57. iop/cls/IOP/PrivateSession/Message/Start.cls +32 -0
  58. iop/cls/IOP/PrivateSession/Message/Stop.cls +48 -0
  59. iop/cls/IOP/Service/WSGI.cls +310 -0
  60. iop/cls/IOP/Test.cls +62 -0
  61. iop/cls/IOP/Utils.cls +374 -0
  62. iop/wsgi/handlers.py +104 -0
  63. {iris_pex_embedded_python-2.3.28b3.dist-info → iris_pex_embedded_python-3.0.0.dist-info}/METADATA +28 -28
  64. {iris_pex_embedded_python-2.3.28b3.dist-info → iris_pex_embedded_python-3.0.0.dist-info}/RECORD +70 -42
  65. {iris_pex_embedded_python-2.3.28b3.dist-info → iris_pex_embedded_python-3.0.0.dist-info}/WHEEL +1 -1
  66. iris_pex_embedded_python-3.0.0.dist-info/entry_points.txt +2 -0
  67. {iris_pex_embedded_python-2.3.28b3.dist-info → iris_pex_embedded_python-3.0.0.dist-info}/top_level.txt +1 -0
  68. iris_pex_embedded_python-2.3.28b3.dist-info/entry_points.txt +0 -2
  69. {grongier/pex → iop}/_message.py +0 -0
  70. {grongier/pex → iop}/_pickle_message.py +0 -0
  71. {iris_pex_embedded_python-2.3.28b3.dist-info → iris_pex_embedded_python-3.0.0.dist-info}/LICENSE +0 -0
grongier/pex/_director.py CHANGED
@@ -1,286 +1 @@
1
- import iris
2
- import datetime
3
- import intersystems_iris.dbapi._DBAPI as irisdbapi
4
- import signal
5
- import functools
6
- import asyncio
7
- from dataclasses import dataclass
8
-
9
- from grongier.pex._business_host import _BusinessHost
10
- from grongier.pex._utils import _Utils
11
-
12
- class _Director():
13
- """ The Directorclass is used for nonpolling business services, that is, business services which are not automatically
14
- called by the production framework (through the inbound adapter) at the call interval.
15
- Instead these business services are created by a custom application by calling the Director.CreateBusinessService() method.
16
- """
17
-
18
- @staticmethod
19
- def CreateBusinessService(target):
20
- """ DEPRECATED : use create_business_service
21
- The CreateBusinessService() method initiates the specifiied business service.
22
-
23
- Parameters:
24
- connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
25
- target: a string that specifies the name of the business service in the production definition.
26
-
27
- Returns:
28
- an object that contains an instance of IRISBusinessService
29
- """
30
- return _Director.create_business_service(target)
31
-
32
- @staticmethod
33
- def create_business_service(target):
34
- """ The create_business_service() method initiates the specified business service.
35
-
36
- Parameters:
37
- connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
38
- target: a string that specifies the name of the business service in the production definition.
39
-
40
- Returns:
41
- an object that contains an instance of IRISBusinessService
42
- """
43
- iris_object = iris.cls("Grongier.PEX.Director").dispatchCreateBusinessService(target)
44
- return iris_object
45
-
46
- @staticmethod
47
- def create_python_business_service(target):
48
- """ The create_business_service() method initiates the specified business service.
49
-
50
- Parameters:
51
- connection: an IRISConnection object that specifies the connection to an IRIS instance for Java.
52
- target: a string that specifies the name of the business service in the production definition.
53
-
54
- Returns:
55
- an object that contains an instance of IRISBusinessService
56
- """
57
- iris_object = iris.cls("Grongier.PEX.Director").dispatchCreateBusinessService(target)
58
- return iris_object.GetClass()
59
-
60
- ### List of function to manage the production
61
- ### start production
62
- @staticmethod
63
- def start_production_with_log(production_name=None):
64
- if production_name is None or production_name == '':
65
- production_name = _Director.get_default_production()
66
- # create two async task
67
- loop = asyncio.get_event_loop()
68
- # add signal handler
69
- handler = SigintHandler()
70
- loop.add_signal_handler(signal.SIGINT, functools.partial(handler.signal_handler, signal.SIGINT, loop))
71
- loop.run_until_complete(asyncio.gather(
72
- _Director._start_production_async(production_name, handler),
73
- _Director._log_production_async(handler)
74
- ))
75
- loop.close()
76
-
77
- @staticmethod
78
- async def _start_production_async(production_name=None, handler=None):
79
- _Director.start_production(production_name)
80
- while True:
81
- if handler.sigint:
82
- _Director.stop_production()
83
- break
84
- await asyncio.sleep(1)
85
-
86
- @staticmethod
87
- def start_production(production_name=None):
88
- if production_name is None or production_name == '':
89
- production_name = _Director.get_default_production()
90
- iris.cls('Ens.Director').StartProduction(production_name)
91
-
92
- ### stop production
93
- @staticmethod
94
- def stop_production():
95
- iris.cls('Ens.Director').StopProduction()
96
-
97
- ### restart production
98
- @staticmethod
99
- def restart_production():
100
- iris.cls('Ens.Director').RestartProduction()
101
-
102
- ### shutdown production
103
- @staticmethod
104
- def shutdown_production():
105
- iris.cls('Ens.Director').StopProduction(10,1)
106
-
107
- ### update production
108
- @staticmethod
109
- def update_production():
110
- iris.cls('Ens.Director').UpdateProduction()
111
-
112
- ### list production
113
- @staticmethod
114
- def list_productions():
115
- return iris.cls('Grongier.PEX.Director').dispatchListProductions()
116
-
117
- ### status production
118
- @staticmethod
119
- def status_production():
120
- dikt = iris.cls('Grongier.PEX.Director').StatusProduction()
121
- if dikt['Production'] is None or dikt['Production'] == '':
122
- dikt['Production'] = _Director.get_default_production()
123
- return dikt
124
-
125
- ### set default production
126
- @staticmethod
127
- def set_default_production(production_name=''):
128
- #set ^Ens.Configuration("SuperUser","LastProduction")
129
- glb = iris.gref("^Ens.Configuration")
130
- glb['csp', "LastProduction"] = production_name
131
-
132
- ### get default production
133
- @staticmethod
134
- def get_default_production():
135
- glb = iris.gref("^Ens.Configuration")
136
- default_production_name = glb['csp', "LastProduction"]
137
- if default_production_name is None or default_production_name == '':
138
- default_production_name = 'Not defined'
139
- return default_production_name
140
-
141
- @staticmethod
142
- def format_log(row: list) -> str:
143
- # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
144
- # ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
145
- # yield all except stack aand tracecat
146
- # in first position, the timelogged
147
- # cast the result to string
148
- # convert Type to its string value
149
- # Assert,Error,Warning,Info,Trace,Alert
150
- typ = row[11]
151
- if typ == 1:
152
- typ = 'Assert'
153
- elif typ == 2:
154
- typ = 'Error'
155
- elif typ == 3:
156
- typ = 'Warning'
157
- elif typ == 4:
158
- typ = 'Info'
159
- elif typ == 5:
160
- typ = 'Trace'
161
- elif typ == 6:
162
- typ = 'Alert'
163
- 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])
164
-
165
- @staticmethod
166
- def read_top_log(cursor,top) -> list:
167
- sql = """
168
- SELECT top ?
169
- ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
170
- FROM Ens_Util.Log
171
- order by id desc
172
- """
173
- result = []
174
- cursor.execute(sql, top)
175
- for row in cursor:
176
- result.append(_Director.format_log(row))
177
- return result
178
-
179
- @staticmethod
180
- def read_log(cursor) -> list:
181
- sql = """
182
- SELECT
183
- ID, ConfigName, Job, MessageId, SessionId, SourceClass, SourceMethod, Stack, Text, TimeLogged, TraceCat, Type
184
- FROM Ens_Util.Log
185
- where TimeLogged >= ?
186
- order by id desc
187
- """
188
- result = []
189
- cursor.execute(sql, (datetime.datetime.now() - datetime.timedelta(seconds=1),))
190
- for row in cursor:
191
- result.append(_Director.format_log(row))
192
- return result
193
-
194
- @staticmethod
195
- async def _log_production_async(handler):
196
- """ Log production
197
- if ctrl+c is pressed, the log is stopped
198
- """
199
- with irisdbapi.connect(embedded=True) as conn:
200
- with conn.cursor() as cursor:
201
- while True:
202
- for row in reversed(_Director.read_log(cursor)):
203
- print(row)
204
- if handler.sigint_log:
205
- break
206
- await asyncio.sleep(1)
207
-
208
- @staticmethod
209
- def log_production_top(top):
210
- """
211
- Log the top N logs of the production
212
- Parameters:
213
- top: the number of log to display
214
- """
215
- with irisdbapi.connect(embedded=True) as conn:
216
- with conn.cursor() as cursor:
217
- for row in reversed(_Director.read_top_log(cursor, top)):
218
- print(row)
219
-
220
- @staticmethod
221
- def log_production():
222
- """ Log production
223
- if ctrl+c is pressed, the log is stopped
224
- """
225
- loop = asyncio.get_event_loop()
226
- handler = SigintHandler(log_only=True)
227
- loop.add_signal_handler(signal.SIGINT, functools.partial(handler.signal_handler, signal.SIGINT, loop))
228
- with irisdbapi.connect(embedded=True) as conn:
229
- with conn.cursor() as cursor:
230
- for row in reversed(_Director.read_top_log(cursor, 10)):
231
- print(row)
232
- loop.run_until_complete(_Director._log_production_async(handler))
233
- loop.close()
234
-
235
- @staticmethod
236
- def test_component(target,message=None,classname=None,body=None):
237
- """
238
- Test a component
239
- Parameters:
240
- target: the name of the component
241
- classname: the name of the class to test
242
- body: the body of the message
243
- """
244
- if not message:
245
- message = iris.cls('Ens.Request')._New()
246
- if classname:
247
- # if classname start with 'iris.' then create an iris object
248
- if classname.startswith('iris.'):
249
- # strip the iris. prefix
250
- classname = classname[5:]
251
- if body:
252
- message = iris.cls(classname)._New(body)
253
- else:
254
- message = iris.cls(classname)._New()
255
- # else create a python object
256
- else:
257
- # python message are casted to Grongier.PEX.Message
258
- message = iris.cls("Grongier.PEX.Message")._New()
259
- message.classname = classname
260
- if body:
261
- message.jstr = _Utils.string_to_stream(body)
262
- else:
263
- message.jstr = _Utils.string_to_stream("{}")
264
- # serialize the message
265
- business_host = _BusinessHost()
266
- serial_message = business_host._dispatch_serializer(message)
267
- response = iris.cls('Grongier.PEX.Utils').dispatchTestComponent(target,serial_message)
268
- try:
269
- deserialized_response = business_host._dispatch_deserializer(response)
270
- except ImportError as e:
271
- # can't import the class, return the string
272
- deserialized_response = f'{response.classname} : {_Utils.stream_to_string(response.jstr)}'
273
- return deserialized_response
274
-
275
-
276
- @dataclass
277
- class SigintHandler():
278
-
279
- sigint: bool = False
280
- sigint_log: bool = False
281
- log_only: bool = False
282
-
283
- def signal_handler(self, signal, frame):
284
- if self.sigint or self.log_only:
285
- self.sigint_log = True
286
- self.sigint = True
1
+ from iop._director import _Director
grongier/pex/_utils.py CHANGED
@@ -1,369 +1 @@
1
- import os
2
- import ast
3
- import iris
4
- import inspect
5
- import xmltodict
6
- import pkg_resources
7
-
8
- class _Utils():
9
- @staticmethod
10
- def raise_on_error(sc):
11
- """
12
- If the status code is an error, raise an exception
13
-
14
- :param sc: The status code returned by the Iris API
15
- """
16
- if iris.system.Status.IsError(sc):
17
- raise RuntimeError(iris.system.Status.GetOneStatusText(sc))
18
-
19
- @staticmethod
20
- def setup(path:str = None):
21
-
22
- if path is None:
23
- # get the path of the data folder with pkg_resources
24
- path = pkg_resources.resource_filename('grongier', 'cls')
25
-
26
- _Utils.raise_on_error(iris.cls('%SYSTEM.OBJ').LoadDir(path,'cubk',"*.cls",1))
27
-
28
- @staticmethod
29
- def register_component(module:str,classname:str,path:str,overwrite:int=1,iris_classname:str='Python'):
30
- """
31
- It registers a component in the Iris database.
32
-
33
- :param module: The name of the module that contains the class
34
- :type module: str
35
- :param classname: The name of the class you want to register
36
- :type classname: str
37
- :param path: The path to the component
38
- :type path: str
39
- :param overwrite: 0 = no, 1 = yes
40
- :type overwrite: int
41
- :param iris_classname: The name of the class in the Iris class hierarchy
42
- :type iris_classname: str
43
- :return: The return value is a string.
44
- """
45
- path = os.path.normpath(path)
46
- # get the absolute path of the folder
47
- path = os.path.abspath(path)
48
- return iris.cls('Grongier.PEX.Utils').dispatchRegisterComponent(module,classname,path,overwrite,iris_classname)
49
-
50
- @staticmethod
51
- def register_folder(path:str,overwrite:int=1,iris_package_name:str='Python'):
52
- """
53
- > This function takes a path to a folder, and registers all the Python files in that folder as IRIS
54
- classes
55
-
56
- :param path: the path to the folder containing the files you want to register
57
- :type path: str
58
- :param overwrite:
59
- :type overwrite: int
60
- :param iris_package_name: The name of the iris package you want to register the file to
61
- :type iris_package_name: str
62
- """
63
- path = os.path.normpath(path)
64
- # get the absolute path of the folder
65
- path = os.path.abspath(path)
66
- for filename in os.listdir(path):
67
- if filename.endswith(".py"):
68
- _Utils._register_file(filename, path, overwrite, iris_package_name)
69
- else:
70
- continue
71
-
72
- @staticmethod
73
- def register_file(file:str,overwrite:int=1,iris_package_name:str='Python'):
74
- """
75
- It takes a file name, a boolean to overwrite existing components, and the name of the Iris
76
- package that the file is in. It then opens the file, parses it, and looks for classes that extend
77
- BusinessOperation, BusinessProcess, or BusinessService. If it finds one, it calls register_component
78
- with the module name, class name, path, overwrite boolean, and the full Iris package name
79
-
80
- :param file: the name of the file containing the component
81
- :type file: str
82
- :param overwrite: if the component already exists, overwrite it
83
- :type overwrite: int
84
- :param iris_package_name: the name of the iris package that you want to register the components to
85
- :type iris_package_name: str
86
- """
87
- head_tail = os.path.split(file)
88
- return _Utils._register_file(head_tail[1],head_tail[0],overwrite,iris_package_name)
89
-
90
- @staticmethod
91
- def _register_file(filename:str,path:str,overwrite:int=1,iris_package_name:str='Python'):
92
- """
93
- It takes a file name, a path, a boolean to overwrite existing components, and the name of the Iris
94
- package that the file is in. It then opens the file, parses it, and looks for classes that extend
95
- BusinessOperation, BusinessProcess, or BusinessService. If it finds one, it calls register_component
96
- with the module name, class name, path, overwrite boolean, and the full Iris package name
97
-
98
- :param filename: the name of the file containing the component
99
- :type filename: str
100
- :param path: the path to the directory containing the files to be registered
101
- :type path: str
102
- :param overwrite: if the component already exists, overwrite it
103
- :type overwrite: int
104
- :param iris_package_name: the name of the iris package that you want to register the components to
105
- :type iris_package_name: str
106
- """
107
- #pour chaque classe dans le module, appeler register_component
108
- f = os.path.join(path,filename)
109
- with open(f) as file:
110
- node = ast.parse(file.read())
111
- #list of class in the file
112
- classes = [n for n in node.body if isinstance(n, ast.ClassDef)]
113
- for klass in classes:
114
- extend = ''
115
- if len(klass.bases) == 1:
116
- if hasattr(klass.bases[0],'id'):
117
- extend = klass.bases[0].id
118
- else:
119
- extend = klass.bases[0].attr
120
- if extend in ('BusinessOperation','BusinessProcess','BusinessService','DuplexService','DuplexProcess','DuplexOperation','InboundAdapter','OutboundAdapter'):
121
- module = _Utils.filename_to_module(filename)
122
- iris_class_name = f"{iris_package_name}.{module}.{klass.name}"
123
- # strip "_" for iris class name
124
- iris_class_name = iris_class_name.replace('_','')
125
- _Utils.register_component(module, klass.name, path, overwrite, iris_class_name)
126
- @staticmethod
127
- def register_package(package:str,path:str,overwrite:int=1,iris_package_name:str='Python'):
128
- """
129
- It takes a package name, a path to the package, a flag to overwrite existing files, and the name of
130
- the iris package to register the files to. It then loops through all the files in the package and
131
- registers them to the iris package
132
-
133
- :param package: the name of the package you want to register
134
- :type package: str
135
- :param path: the path to the directory containing the package
136
- :type path: str
137
- :param overwrite: 0 = don't overwrite, 1 = overwrite
138
- :type overwrite: int
139
- :param iris_package_name: The name of the package in the Iris package manager
140
- :type iris_package_name: str
141
- """
142
- for filename in os.listdir(os.path.join(path,package)):
143
- if filename.endswith(".py"):
144
- _Utils._register_file(filename, os.path.join(path,package), overwrite, iris_package_name)
145
- else:
146
- continue
147
-
148
- @staticmethod
149
- def filename_to_module(filename) -> str:
150
- """
151
- It takes a filename and returns the module name
152
-
153
- :param filename: The name of the file to be imported
154
- :return: The module name
155
- """
156
- module = ''
157
-
158
- path,file = os.path.split(filename)
159
- mod = file.split('.')[0]
160
- packages = path.replace(os.sep, ('.'))
161
- if len(packages) >1:
162
- module = packages+'.'+mod
163
- else:
164
- module = mod
165
-
166
- return module
167
-
168
- @staticmethod
169
- def migrate(filename=None,root_path=None):
170
- """
171
- Read the settings.py file and register all the components
172
- settings.py file has two dictionaries:
173
- * CLASSES
174
- * key: the name of the class
175
- * value: an instance of the class
176
- * PRODUCTIONS
177
- list of dictionaries:
178
- * key: the name of the production
179
- * value: a dictionary containing the settings for the production
180
- """
181
- # try to load the settings file
182
- if filename:
183
- import sys
184
- path = None
185
- # check if the filename is absolute or relative
186
- if os.path.isabs(filename):
187
- path = os.path.dirname(filename)
188
- else:
189
- raise ValueError("The filename must be absolute")
190
- # add the path to the system path
191
- sys.path.append(path)
192
- import settings
193
- # get the path of the settings file
194
- path = os.path.dirname(inspect.getfile(settings))
195
- try:
196
- # set the classes settings
197
- _Utils.set_classes_settings(settings.CLASSES,path)
198
- except AttributeError:
199
- print("No classes to register")
200
- try:
201
- # set the productions settings
202
- _Utils.set_productions_settings(settings.PRODUCTIONS,path)
203
- except AttributeError:
204
- print("No productions to register")
205
-
206
-
207
-
208
- @staticmethod
209
- def set_classes_settings(class_items,root_path=None):
210
- """
211
- It takes a dictionary of classes and returns a dictionary of settings for each class
212
-
213
- :param class_items: a dictionary of classes
214
- :return: a dictionary of settings for each class
215
- """
216
- for key, value in class_items.items():
217
- if inspect.isclass(value):
218
- path = None
219
- if root_path:
220
- path = root_path
221
- else:
222
- path = os.path.dirname(inspect.getfile(value))
223
- _Utils.register_component(value.__module__,value.__name__,path,1,key)
224
- elif inspect.ismodule(value):
225
- path = None
226
- if root_path:
227
- path = root_path
228
- else:
229
- path = os.path.dirname(inspect.getfile(value))
230
- _Utils._register_file(value.__name__+'.py',path,1,key)
231
- # if the value is a dict
232
- elif isinstance(value,dict):
233
- # if the dict has a key 'path' and a key 'module' and a key 'class'
234
- if 'path' in value and 'module' in value and 'class' in value:
235
- # register the component
236
- _Utils.register_component(value['module'],value['class'],value['path'],1,key)
237
- # if the dict has a key 'path' and a key 'package'
238
- elif 'path' in value and 'package' in value:
239
- # register the package
240
- _Utils.register_package(value['package'],value['path'],1,key)
241
- # if the dict has a key 'path' and a key 'file'
242
- elif 'path' in value and 'file' in value:
243
- # register the file
244
- _Utils._register_file(value['file'],value['path'],1,key)
245
- # if the dict has a key 'path'
246
- elif 'path' in value:
247
- # register folder
248
- _Utils.register_folder(value['path'],1,key)
249
- else:
250
- raise ValueError(f"Invalid value for {key}.")
251
-
252
- @staticmethod
253
- def set_productions_settings(production_list,root_path=None):
254
- """
255
- It takes a list of dictionaries and registers the productions
256
- """
257
- # for each production in the list
258
- for production in production_list:
259
- # get the production name (first key in the dictionary)
260
- production_name = list(production.keys())[0]
261
- # set the first key to 'production'
262
- production['Production'] = production.pop(production_name)
263
- # handle Items
264
- production = _Utils.handle_items(production,root_path)
265
- # transform the json as an xml
266
- xml = _Utils.dict_to_xml(production)
267
- # register the production
268
- _Utils.register_production(production_name,xml)
269
-
270
- @staticmethod
271
- def handle_items(production,root_path=None):
272
- # if an item is a class, register it and replace it with the name of the class
273
- if 'Item' in production['Production']:
274
- # for each item in the list
275
- for i,item in enumerate(production['Production']['Item']):
276
- # if the attribute "@ClassName" is a class, register it and replace it with the name of the class
277
- if '@ClassName' in item:
278
- if inspect.isclass(item['@ClassName']):
279
- path = None
280
- if root_path:
281
- path = root_path
282
- else:
283
- path = os.path.dirname(inspect.getfile(item['@ClassName']))
284
- _Utils.register_component(item['@ClassName'].__module__,item['@ClassName'].__name__,path,1,item['@Name'])
285
- # replace the class with the name of the class
286
- production['Production']['Item'][i]['@ClassName'] = item['@Name']
287
- # if the attribute "@ClassName" is a dict
288
- elif isinstance(item['@ClassName'],dict):
289
- # create a new dict where the key is the name of the class and the value is the dict
290
- class_dict = {item['@Name']:item['@ClassName']}
291
- # pass the new dict to set_classes_settings
292
- _Utils.set_classes_settings(class_dict)
293
- # replace the class with the name of the class
294
- production['Production']['Item'][i]['@ClassName'] = item['@Name']
295
- else:
296
- raise ValueError(f"Invalid value for {item['@Name']}.")
297
-
298
- return production
299
-
300
- @staticmethod
301
- def dict_to_xml(json):
302
- """
303
- It takes a json and returns an xml
304
-
305
- :param json: a json
306
- :return: an xml
307
- """
308
- xml = xmltodict.unparse(json,pretty=True)
309
- # remove the xml version tag
310
- xml = xml.replace('<?xml version="1.0" encoding="utf-8"?>','')
311
- # remove the new line at the beginning of the xml
312
- xml = xml[1:]
313
- return xml
314
-
315
- @staticmethod
316
- def register_production(production_name,xml):
317
- """
318
- It takes a production name and an xml and registers the production
319
-
320
- :param production_name: the name of the production
321
- :type production_name: str
322
- :param xml: the xml of the production
323
- :type xml: str
324
- """
325
- # split the production name in the package name and the production name
326
- # the production name is the last part of the string
327
- package = '.'.join(production_name.split('.')[:-1])
328
- production_name = production_name.split('.')[-1]
329
- stream = _Utils.string_to_stream(xml)
330
- # register the production
331
- _Utils.raise_on_error(iris.cls('Grongier.PEX.Utils').CreateProduction(package,production_name,stream))
332
-
333
- @staticmethod
334
- def export_production(production_name):
335
- """
336
- It takes a production name and exports the production
337
-
338
- :param production_name: the name of the production
339
- :type production_name: str
340
- """
341
- def postprocessor(path, key, value):
342
- if value is None:
343
- return key, ''
344
- return key, value
345
- # export the production
346
- xdata = iris.cls('Grongier.PEX.Utils').ExportProduction(production_name)
347
- # for each chunk of 1024 characters
348
- string = _Utils.stream_to_string(xdata)
349
- # convert the xml to a dictionary
350
- data = xmltodict.parse(string,postprocessor=postprocessor)
351
- # return the dictionary
352
- return data
353
-
354
- @staticmethod
355
- def stream_to_string(stream)-> str:
356
- string = ""
357
- stream.Rewind()
358
- while not stream.AtEnd:
359
- string += stream.Read(4092)
360
- return string
361
-
362
- @staticmethod
363
- def string_to_stream(string:str):
364
- stream = iris.cls('%Stream.GlobalCharacter')._New()
365
- n = 4092
366
- chunks = [string[i:i+n] for i in range(0, len(string), n)]
367
- for chunk in chunks:
368
- stream.Write(chunk)
369
- return stream
1
+ from iop._utils import _Utils
iop/__init__.py ADDED
@@ -0,0 +1,24 @@
1
+ from iop._business_service import _BusinessService
2
+ from iop._business_process import _BusinessProcess
3
+ from iop._private_session_duplex import _PrivateSessionDuplex
4
+ from iop._private_session_process import _PrivateSessionProcess
5
+ from iop._business_operation import _BusinessOperation
6
+ from iop._inbound_adapter import _InboundAdapter
7
+ from iop._outbound_adapter import _OutboundAdapter
8
+ from iop._message import _Message
9
+ from iop._pickle_message import _PickleMessage
10
+ from iop._director import _Director
11
+ from iop._utils import _Utils
12
+
13
+ class Utils(_Utils): pass
14
+ class InboundAdapter(_InboundAdapter): pass
15
+ class OutboundAdapter(_OutboundAdapter): pass
16
+ class BusinessService(_BusinessService): pass
17
+ class BusinessOperation(_BusinessOperation): pass
18
+ class BusinessProcess(_BusinessProcess): pass
19
+ class DuplexService(_PrivateSessionDuplex): pass
20
+ class DuplexOperation(_PrivateSessionDuplex): pass
21
+ class DuplexProcess(_PrivateSessionProcess): pass
22
+ class Message(_Message): pass
23
+ class PickleMessage(_PickleMessage): pass
24
+ class Director(_Director): pass
iop/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ # main entry is _cli.main()
2
+ if __name__ == '__main__':
3
+ import iop._cli as _cli
4
+ _cli.main()