Skip to content

Commit 0758a39

Browse files
committed
feat: Convert DirectTransport to use ctypes
1 parent ae3ad85 commit 0758a39

File tree

5 files changed

+236
-207
lines changed

5 files changed

+236
-207
lines changed

setup.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from setuptools import setup, find_packages, Extension
2-
from platform import system
1+
from setuptools import setup, find_packages
32

43

54
setup(
@@ -14,10 +13,6 @@
1413
zip_safe=False,
1514
packages=find_packages(where="src"),
1615
package_dir={"": "src"},
17-
ext_modules=[
18-
Extension('itoolkit/transport/_direct',
19-
['src/itoolkit/transport/direct.c'])
20-
] if system() == 'OS400' else [],
2116
classifiers=[
2217
"License :: OSI Approved :: MIT License",
2318
"Programming Language :: Python :: 2.7",

src/itoolkit/transport/_direct.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import os
2+
3+
from ctypes import c_int, c_uint, c_int16, c_ulonglong, c_void_p, c_char_p, \
4+
CDLL, DEFAULT_MODE, POINTER, Structure, addressof, sizeof, \
5+
create_string_buffer
6+
7+
RTLD_MEMBER = 0x00040000
8+
9+
10+
class ILEPointer(Structure):
11+
"An ILE pointer type"
12+
_pack_ = 16
13+
_fields_ = [
14+
("hi", c_ulonglong),
15+
("lo", c_ulonglong)
16+
]
17+
18+
19+
try:
20+
_LIBC = CDLL("/QOpenSys/usr/lib/libc.a(shr_64.o)",
21+
DEFAULT_MODE | RTLD_MEMBER)
22+
23+
_SETSPP = _LIBC._SETSPP
24+
_SETSPP.argtypes = [POINTER(ILEPointer), c_void_p]
25+
26+
_ILELOADX = _LIBC._ILELOADX
27+
_ILELOADX.argtypes = [c_char_p, c_uint]
28+
_ILELOADX.restype = c_ulonglong
29+
30+
_ILESYMX = _LIBC._ILESYMX
31+
_ILESYMX.argtypes = [POINTER(ILEPointer), c_ulonglong, c_char_p]
32+
33+
_ILECALLX = _LIBC._ILECALLX
34+
_ILECALLX.argtypes = [
35+
POINTER(ILEPointer),
36+
c_void_p,
37+
POINTER(c_int16),
38+
c_int16,
39+
c_int
40+
]
41+
except OSError:
42+
# Either we couldn't load libc or we couldn't find the necessary syscalls
43+
# exported from libc. Either way, this platform is unsupported so we raise
44+
# an import error to prevent it from being used.
45+
raise ImportError
46+
47+
ILELOAD_LIBOBJ = 0x00000001
48+
ILESYM_PROCEDURE = 1
49+
50+
RESULT_VOID = 0
51+
RESULT_INT8 = -1
52+
RESULT_UINT8 = -2
53+
RESULT_INT16 = -3
54+
RESULT_UINT16 = -4
55+
RESULT_INT32 = -5
56+
RESULT_UINT32 = -6
57+
RESULT_INT64 = -7
58+
RESULT_UINT64 = -8
59+
RESULT_FLOAT64 = -10
60+
RESULT_FLOAT128 = -18
61+
62+
ARG_END = 0
63+
ARG_MEMPTR = -11
64+
65+
66+
class MemPointer(ILEPointer):
67+
"An ILE pointer type to be used with ARG_MEMPTR"
68+
_pack_ = 16
69+
70+
def __init__(self, addr=0):
71+
super(MemPointer, self).__int__()
72+
self.hi = 0
73+
self.lo = addr
74+
75+
@property
76+
def addr(self):
77+
return self.lo
78+
79+
@addr.setter
80+
def addr(self, addr):
81+
self.lo = addr
82+
83+
84+
class ILEArglistBase(Structure):
85+
"ILECALL argument list base member"
86+
_pack_ = 16
87+
_fields_ = [
88+
('descriptor', ILEPointer),
89+
('result', ILEPointer),
90+
]
91+
92+
93+
class RunASCIIArglist(Structure):
94+
"Argument list definition for the RUNASCII procedure"
95+
_pack_ = 16
96+
_fields_ = [
97+
('base', ILEArglistBase),
98+
('ipc', MemPointer),
99+
('ipc_len', MemPointer),
100+
('ctl', MemPointer),
101+
('ctl_len', MemPointer),
102+
('xmlin', MemPointer),
103+
('xmlin_len', MemPointer),
104+
('xmlout', MemPointer),
105+
('xmlout_len', MemPointer),
106+
('pase_ccsid', MemPointer),
107+
('ile_ccsid', MemPointer),
108+
]
109+
110+
111+
RunASCIISignature = c_int16 * 11
112+
113+
_SIGNATURE = RunASCIISignature(
114+
ARG_MEMPTR, ARG_MEMPTR, ARG_MEMPTR, ARG_MEMPTR, ARG_MEMPTR,
115+
ARG_MEMPTR, ARG_MEMPTR, ARG_MEMPTR, ARG_MEMPTR, ARG_MEMPTR,
116+
ARG_END
117+
)
118+
119+
120+
class _XMLSERVICE:
121+
def __init__(self, library):
122+
self.library = library
123+
124+
path = "{library}/XMLSTOREDP".format(library=library)
125+
actgrp = _ILELOADX(path.encode(), ILELOAD_LIBOBJ)
126+
if actgrp == 0xffffffffffffffff:
127+
raise OSError("{path} not found".format(path))
128+
129+
self._RUNASCII = ILEPointer()
130+
if _ILESYMX(self._RUNASCII, actgrp, b"RUNASCII") != ILESYM_PROCEDURE:
131+
raise OSError("RUNASCII procedure not found in {path}".format(path))
132+
133+
def __call__(self, xmlin, ipc, ctl):
134+
ipc = c_char_p(ipc.encode())
135+
ipc_len = c_int(len(ipc.value))
136+
137+
ctl = c_char_p(ctl.encode())
138+
ctl_len = c_int(len(ctl.value))
139+
140+
xmlin = c_char_p(xmlin.encode())
141+
xmlin_len = c_int(len(xmlin.value))
142+
143+
xmlout = create_string_buffer(0x1000000) # 16MiB
144+
xmlout_len = c_int(sizeof(xmlout))
145+
146+
pase_ccsid = c_int(1208)
147+
ile_ccsid = c_int(0)
148+
149+
# RUNASCII doesn't just take ILE pointers, it takes ILE pointers to ILE
150+
# pointers, so we first copy the PASE pointer in to a space pointer for
151+
# each pointer parameter... *except* for the integer parameters, which
152+
# are just pointers to integers for some reason...
153+
ipc_spp = ILEPointer()
154+
ctl_spp = ILEPointer()
155+
xmlin_spp = ILEPointer()
156+
xmlout_spp = ILEPointer()
157+
158+
_SETSPP(ipc_spp, ipc)
159+
_SETSPP(ctl_spp, ctl)
160+
_SETSPP(xmlin_spp, xmlin)
161+
_SETSPP(xmlout_spp, xmlout)
162+
163+
arglist = RunASCIIArglist()
164+
arglist.ipc.addr = addressof(ipc_spp)
165+
arglist.ipc_len.addr = addressof(ipc_len)
166+
arglist.ctl.addr = addressof(ctl_spp)
167+
arglist.ctl_len.addr = addressof(ctl_len)
168+
arglist.xmlin.addr = addressof(xmlin_spp)
169+
arglist.xmlin_len.addr = addressof(xmlin_len)
170+
arglist.xmlout.addr = addressof(xmlout_spp)
171+
arglist.xmlout_len.addr = addressof(xmlout_len)
172+
arglist.pase_ccsid.addr = addressof(pase_ccsid)
173+
arglist.ile_ccsid.addr = addressof(ile_ccsid)
174+
175+
if _ILECALLX(self._RUNASCII, addressof(arglist), _SIGNATURE,
176+
RESULT_INT32, 0):
177+
raise RuntimeError("Failed to call XMLSERVICE with _ILECALL")
178+
179+
if arglist.base.result.lo & 0xffffffff:
180+
raise RuntimeError("XMLSERVICE returned an error")
181+
182+
return xmlout.value
183+
184+
185+
def xmlservice(xmlin, ipc, ctl):
186+
# If we haven't yet initialized XMLSERVICE, do that now
187+
if not hasattr(xmlservice, 'cached_obj'):
188+
189+
libraries = [os.getenv("XMLSERVICE"), "QXMLSERV", "XMLSERVICE"]
190+
191+
found = False
192+
for library in libraries:
193+
if not library:
194+
continue
195+
196+
try:
197+
xmlservice.cached_obj = _XMLSERVICE(library)
198+
found = True
199+
except OSError:
200+
continue
201+
202+
if not found:
203+
# TODO: Message string
204+
raise OSError("Couldn't load RUNASCII function from XMLSERVICE")
205+
206+
return xmlservice.cached_obj(xmlin, ipc, ctl)

0 commit comments

Comments
 (0)