iris-pex-embedded-python 2.3.27b2__py3-none-any.whl → 3.2.1b3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +196 -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.1b3.dist-info/METADATA +90 -0
- iris_pex_embedded_python-3.2.1b3.dist-info/RECORD +139 -0
- {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b3.dist-info}/WHEEL +1 -1
- iris_pex_embedded_python-3.2.1b3.dist-info/entry_points.txt +2 -0
- {iris_pex_embedded_python-2.3.27b2.dist-info → iris_pex_embedded_python-3.2.1b3.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.1b3.dist-info}/LICENSE +0 -0
grongier/pex/_utils.py
CHANGED
|
@@ -1,369 +1 @@
|
|
|
1
|
-
import
|
|
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
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import intersystems_iris._Constant
|
|
2
2
|
|
|
3
|
-
class _ConnectionInformation(object):
|
|
4
3
|
|
|
4
|
+
class _ConnectionInformation(object):
|
|
5
5
|
def __init__(self):
|
|
6
6
|
self.protocol_version = intersystems_iris._Constant._Constant.PROTOCOL_VERSION
|
|
7
7
|
self._is_unicode = True
|
|
@@ -20,9 +20,10 @@ class _ConnectionInformation(object):
|
|
|
20
20
|
def _parse_server_version(self, server_version):
|
|
21
21
|
split_1 = server_version.split("|")
|
|
22
22
|
self._server_version = split_1[0]
|
|
23
|
-
if len(split_1)>1:
|
|
23
|
+
if len(split_1) > 1:
|
|
24
|
+
self._iris_install_dir = split_1[1]
|
|
24
25
|
if self._server_version.find("Version") > 0:
|
|
25
|
-
version = server_version[server_version.find("Version")+8:]
|
|
26
|
+
version = server_version[server_version.find("Version") + 8 :]
|
|
26
27
|
self._server_version_major = version.split(".")[0]
|
|
27
28
|
self._server_version_minor = version.split(".")[1]
|
|
28
29
|
return
|
|
@@ -31,24 +32,25 @@ class _ConnectionInformation(object):
|
|
|
31
32
|
def _map_server_locale(locale):
|
|
32
33
|
# we need to map IRIS locale literals to Python locale literals
|
|
33
34
|
_locales = {
|
|
34
|
-
"LATIN1": "latin_1",
|
|
35
|
-
"LATIN2": "iso8859_2",
|
|
36
|
-
"LATINC": "iso8859_5",
|
|
37
|
-
"LATINA": "iso8859_6",
|
|
38
|
-
"LATING": "iso8859_7",
|
|
39
|
-
"LATINH": "iso8859_8",
|
|
40
|
-
"LATINT": "iso8859_11",
|
|
41
|
-
"LATIN9": "iso8859_15",
|
|
42
|
-
"CP1250": "cp1250",
|
|
43
|
-
"CP1251": "cp1251",
|
|
44
|
-
"CP1252": "cp1252",
|
|
45
|
-
"CP1253": "cp1253",
|
|
46
|
-
"CP1255": "cp1255",
|
|
47
|
-
"CP1256": "cp1256",
|
|
48
|
-
"CP1257": "cp1257",
|
|
49
|
-
"CP874": "cp874",
|
|
35
|
+
"LATIN1": "latin_1",
|
|
36
|
+
"LATIN2": "iso8859_2",
|
|
37
|
+
"LATINC": "iso8859_5",
|
|
38
|
+
"LATINA": "iso8859_6",
|
|
39
|
+
"LATING": "iso8859_7",
|
|
40
|
+
"LATINH": "iso8859_8",
|
|
41
|
+
"LATINT": "iso8859_11",
|
|
42
|
+
"LATIN9": "iso8859_15",
|
|
43
|
+
"CP1250": "cp1250",
|
|
44
|
+
"CP1251": "cp1251",
|
|
45
|
+
"CP1252": "cp1252",
|
|
46
|
+
"CP1253": "cp1253",
|
|
47
|
+
"CP1255": "cp1255",
|
|
48
|
+
"CP1256": "cp1256",
|
|
49
|
+
"CP1257": "cp1257",
|
|
50
|
+
"CP874": "cp874",
|
|
50
51
|
"UNICODE": "utf-8",
|
|
51
52
|
}
|
|
52
53
|
return _locales[locale.upper()] if locale.upper() in _locales else locale
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
def __repr__(self) -> str:
|
|
56
|
+
return f"<{self._server_version}>"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from typing import Union
|
|
1
2
|
import struct
|
|
2
3
|
import copy
|
|
3
4
|
import enum
|
|
@@ -25,6 +26,7 @@ from ._SQLType import SQLType
|
|
|
25
26
|
|
|
26
27
|
from .._IRISNative import connect as native_connect
|
|
27
28
|
from .._IRISEmbedded import _IRISEmbedded
|
|
29
|
+
from intersystems_iris._IRISConnection import _IRISConnection
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def NotImplementedErrorDBAPI(msg=None):
|
|
@@ -42,7 +44,7 @@ def embedded_connect(*args, hostname=None, port=None, namespace=None, username=N
|
|
|
42
44
|
return connection
|
|
43
45
|
|
|
44
46
|
|
|
45
|
-
def connect(*args, embedded=False, hostname=None, port=None, namespace=None, username=None, password=None, **kw):
|
|
47
|
+
def connect(*args, embedded=False, hostname=None, port=None, namespace=None, username=None, password=None, **kw) -> Union[_IRISConnection, _IRISEmbedded]:
|
|
46
48
|
try:
|
|
47
49
|
if not embedded:
|
|
48
50
|
return native_connect(
|
|
@@ -1180,6 +1182,8 @@ class Cursor(_BaseCursor):
|
|
|
1180
1182
|
sets = self._parameter_sets or 1
|
|
1181
1183
|
self.params = list(self.params).copy()
|
|
1182
1184
|
param_types = [param.type for param in self._params._params_list]
|
|
1185
|
+
if not self.params:
|
|
1186
|
+
return
|
|
1183
1187
|
|
|
1184
1188
|
for i in range(sets):
|
|
1185
1189
|
params = self._params.collect(i)
|
|
@@ -2149,6 +2153,7 @@ class Cursor(_BaseCursor):
|
|
|
2149
2153
|
|
|
2150
2154
|
if retval is None:
|
|
2151
2155
|
return retval
|
|
2156
|
+
# print('retval', retval[:])
|
|
2152
2157
|
return retval.as_tuple()
|
|
2153
2158
|
# return tuple(retval[:])
|
|
2154
2159
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
from datetime import datetime, date, time
|
|
2
|
+
from datetime import datetime, date, time, timezone
|
|
3
3
|
from collections import namedtuple
|
|
4
4
|
from ._SQLType import SQLType
|
|
5
5
|
from .._DBList import _DBList
|
|
@@ -11,20 +11,26 @@ from ._Column import _Column
|
|
|
11
11
|
def from_timestamp_posix(posix):
|
|
12
12
|
time = int(posix)
|
|
13
13
|
if time > 0:
|
|
14
|
-
time ^= 0x1000000000000000
|
|
14
|
+
time ^= 0x1000000000000000
|
|
15
15
|
else:
|
|
16
|
-
time |= 0xF000000000000000
|
|
16
|
+
time |= 0xF000000000000000
|
|
17
17
|
|
|
18
18
|
time /= 1000000
|
|
19
19
|
|
|
20
|
-
value = datetime.
|
|
20
|
+
value = datetime.fromtimestamp(time, timezone.utc).replace(tzinfo=None)
|
|
21
21
|
return value
|
|
22
22
|
|
|
23
|
+
|
|
23
24
|
class _ResultSetRow:
|
|
24
25
|
_locale = "latin-1"
|
|
25
26
|
_connection = None
|
|
26
27
|
|
|
27
|
-
def __init__(
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
connection,
|
|
31
|
+
columns=None,
|
|
32
|
+
rowcount=0,
|
|
33
|
+
):
|
|
28
34
|
self._connection = connection
|
|
29
35
|
# index from user-inputted columns to columns received from server
|
|
30
36
|
self.col_index = []
|
|
@@ -55,7 +61,7 @@ class _ResultSetRow:
|
|
|
55
61
|
self.colCount = len(columns) if columns != None else 0
|
|
56
62
|
|
|
57
63
|
# number of columns received from server, aka number of items per row
|
|
58
|
-
#self.colCount = rowcount
|
|
64
|
+
# self.colCount = rowcount
|
|
59
65
|
|
|
60
66
|
# list of _ListItems corresponding to the various entries in the row, plus the offset of next row's first _ListItem
|
|
61
67
|
self.rowItems = None
|
|
@@ -126,9 +132,9 @@ class _ResultSetRow:
|
|
|
126
132
|
|
|
127
133
|
def get(self):
|
|
128
134
|
return self[:]
|
|
129
|
-
|
|
135
|
+
|
|
130
136
|
def as_tuple(self):
|
|
131
|
-
row = namedtuple(
|
|
137
|
+
row = namedtuple("Row", [col.name for col in self._columns], rename=True)
|
|
132
138
|
values = self[:]
|
|
133
139
|
return row(*values)
|
|
134
140
|
|
|
@@ -164,15 +170,18 @@ class _ResultSetRow:
|
|
|
164
170
|
HOROLOG_ORDINAL = date(1840, 12, 31).toordinal()
|
|
165
171
|
if item:
|
|
166
172
|
item = date.fromordinal(HOROLOG_ORDINAL + item)
|
|
173
|
+
if ctype == SQLType.TIMESTAMP and item:
|
|
174
|
+
item = item + '.000' if '.' not in item else item
|
|
175
|
+
item = datetime.strptime(item, '%Y-%m-%d %H:%M:%S.%f')
|
|
167
176
|
if ctype == SQLType.TIME_HOROLOG:
|
|
168
177
|
if item:
|
|
169
178
|
item = time(item // 3600, item % 3600 // 60, item % 3600 % 60)
|
|
170
179
|
if ctype == SQLType.GUID:
|
|
171
180
|
item = uuid.UUID(item)
|
|
172
|
-
if ctype == SQLType.TIMESTAMP_POSIX:
|
|
181
|
+
if ctype == SQLType.TIMESTAMP_POSIX and item:
|
|
173
182
|
item = from_timestamp_posix(item)
|
|
174
183
|
|
|
175
|
-
if _column.tableName ==
|
|
184
|
+
if _column.tableName == "None" and _column.schema == "None":
|
|
176
185
|
# Ignore for anonymous tables
|
|
177
186
|
pass
|
|
178
187
|
elif item is None:
|
|
@@ -285,8 +294,9 @@ class _ResultSetRow:
|
|
|
285
294
|
rowItems[i] = curr_offset
|
|
286
295
|
prev_offset = curr_offset
|
|
287
296
|
except IndexError:
|
|
288
|
-
raise IndexError(
|
|
289
|
-
|
|
297
|
+
raise IndexError(
|
|
298
|
+
"Row incomplete: " + str(self.colCount) + " items expected, but " + str(i) + " were found"
|
|
299
|
+
)
|
|
290
300
|
self.update(rowItems)
|
|
291
301
|
return True
|
|
292
302
|
|
|
@@ -319,8 +329,9 @@ class _ResultSetRow:
|
|
|
319
329
|
rowItems[i] = curr_offset
|
|
320
330
|
prev_offset = curr_offset
|
|
321
331
|
except IndexError:
|
|
322
|
-
raise IndexError(
|
|
323
|
-
|
|
332
|
+
raise IndexError(
|
|
333
|
+
"Row incomplete: " + str(self.colCount) + " items expected, but " + str(i) + " were found"
|
|
334
|
+
)
|
|
324
335
|
return self.update(rowItems)
|
|
325
336
|
|
|
326
337
|
def update(self, rowItems):
|
|
@@ -330,7 +341,7 @@ class _ResultSetRow:
|
|
|
330
341
|
colIndexOffsets[idx] = rowItems[i]
|
|
331
342
|
colIndexOffsets[-1] = self._last_list_item.next_offset
|
|
332
343
|
self.rowItems = colIndexOffsets
|
|
333
|
-
return self.rowItems[:self.colCount]
|
|
344
|
+
return self.rowItems[: self.colCount]
|
|
334
345
|
self.rowItems = rowItems
|
|
335
346
|
self._offsets = self.DataRow(self)
|
|
336
347
|
self._new_buffer = False
|
|
@@ -580,7 +580,10 @@ class _PreParser(object):
|
|
|
580
580
|
break
|
|
581
581
|
if token.TokenType is TOKEN.CONSTANT:
|
|
582
582
|
values += '?'
|
|
583
|
-
|
|
583
|
+
param = token.Lexeme
|
|
584
|
+
if param.__len__ and param[0] == "'" and param[0] == param[-1]:
|
|
585
|
+
param = param[1: -1]
|
|
586
|
+
params += [param]
|
|
584
587
|
else:
|
|
585
588
|
values += token.Lexeme
|
|
586
589
|
values += ' '
|
iop/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from iop._business_operation import _BusinessOperation
|
|
2
|
+
from iop._business_process import _BusinessProcess
|
|
3
|
+
from iop._business_service import _BusinessService
|
|
4
|
+
from iop._director import _Director
|
|
5
|
+
from iop._inbound_adapter import _InboundAdapter
|
|
6
|
+
from iop._message import _Message
|
|
7
|
+
from iop._outbound_adapter import _OutboundAdapter
|
|
8
|
+
from iop._pickle_message import _PickleMessage
|
|
9
|
+
from iop._private_session_duplex import _PrivateSessionDuplex
|
|
10
|
+
from iop._private_session_process import _PrivateSessionProcess
|
|
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