I enjoy using Python on Linux, because it is very powerful. I thought it would be interesting to port the MQ Python interface pymqi to z/OS. This exposed many of the challenges of running Python on z/OS.
I’ll cover some of the lessons I learned in doing this work. Thanks to Steve Pitman who helped me package the extension.
IBM Open Enterprise Python for z/OS, V3.8, user’s guide is a useful book.
Packaging Python programs
See Python import, packages and modules.
You need a package for Python source, and a separate package for load module(s).
Creating files that would compile was a challenge.
See here.
With experience, I’ve found an easier way -just compile the source with the right options.
Compiling files.
I copied the pymqi C code to z/OS Unix Services, and tried to compile it. This was a mistake, as it took me a long time to get the compile options right. I found that using the setup.py script was the right way to go.
My directory tree
/u/pymqi
..setup.py
..include
....sample.h
..code
....pymqi
......__init__.py
......CMQCFC.py
......CMQXC.py
......CMQZC.py
......const.py
......CMQC.py
......pymqe.c
Setup.py
This script needs export _C89_CCMODE=1, otherwise you get FSUM3008 message
Specify a file with the correct suffix (.c, .i, .s, .o, .x, .p, .I, or .a), or a corresponding data set name, instead of -L
import setuptools
from distutils.core import setup, Extension
import os
import sysconfig
#
# This script needs export _C89_CCMODE=1
# Otherwise you get FSUM3008 messages
#
import os
os.environ['_C89_CCMODE'] = '1'
bindings_mode = 1
version = '1.12.0'
setup(name = 'pymqi',
version = version,
description = 'Python...',
platforms='OS Independent',
package_dir = {'': 'code'},
packages = ['pymqi'],
py_modules = ['pymqi.CMQC', 'pymqi.CMQCFC', 'pymqi.CMQXC', 'pymqi.CMQZC'],
ext_modules = [Extension('pymqi.pymqe',['code/pymqi/pymqe.c'], define_macros=[('PYQMI_BINDINGS_MODE_BUILD',
bindings_mode)],
include_dirs=["//'COLIN.MQ924.SCSQC370'"],
extra_link_args=["//'COLIN.MQ924.SCSQDEFS.OBJ(CSQBMQ2X)'"],
)]
)
# I had extra_link_args=["-Wl,INFO,LIST,MAP",.... when setting
# this up
# I used
# extra_compile_args=["-Wc,LIST(c.lst),XREF"],
# to get out a listing and cross reference.
Which says
- The package name is packages = [‘pymqi’],
- The Python files are py_modules = [‘pymqi.CMQC’….
- There is an extension .. ext_modules=.. with the source program code/pymqi/pymqe.c
- It needs “//’COLIN.MQ924.SCSQC370′” to compile and “//’COLIN.MQ924.SCSQDEFS.OBJ(CSQBMQ2X)'” at bind time. This file contains the MQ Binder input
- When I wanted the binder output – “-Wl,INFO,LIST,MAP”. This goes to the terminal. I used a ‘>’ command to pipe the output of the python3 setup build it to a file.
- and a C listing “-Wc,LIST(c.lst),XREF”. The listing goes to c.lst
You need
- import setuptools so that the setup bdist_wheel packaging works. You also need the wheel package installed.
Setup
There is a buglet in the compile set up. You need to specify
export _C89_CCMODE=1
Without it you get
FSUM3008 Specify a file with the correct suffix (.c, .i, .s, .o, .x, .p, .I, or .a), or a corresponding data set name, instead of -obuild/lib.os390-27.00-1090-3.8/pymqi/pymqe.so.
You also need the binder input in a data set with the correct suffix. For example .OBJ
“//’COLIN.MQ924.SCSQDEFS.OBJ(CSQBMQ2X)'”
If you do not have the correct suffix you get
FSUM3218 xlc: File //’COLIN.MQ924.SCSQDEFS(CSQBMQ2X)’ contains an incorrect file suffix.
Doing the compile and test install
I used a shell script to do the compiles and install
touch code/pymqi/*.c
rm a b c d
export _C89_CCMODE=1
#python3 setup.py clean
python3 setup.py build 1>a 2>b
python3 setup.py install 1>c 2>d
I captured the output from the setup.py jobs using 1>a etc because I could not see how to direct the binder output to a file. It comes out on the terminal – and there was a lot of it!.
Packaging the package
Python build which worked
I had to install wheel package. See How to install software in an isolated environment, or just use python3 -m pip install wheel if your z/OS image is connected to the network.
The command I used was
python3 -m pip install –user –no-cache-dir /u/tmp/py/wheel-0.37.1-py2.py3-none-any.whl-f /u/tmp/py/wheel-0.37.1-py2.py3-none
I had to add import setuptools to my setup.py file (at the top). (This converted the install package from a dist-utils to a setuptools packaging)
python3 setup.py bdist_wheel
This created a file “/u/pymqi/dist/pymqi-1.12.0-cp310-cp310-os390_27_00_1090.whl“
This file is specific to python 3.10
For a wheel package, you’ll need to build it for all major versions and cannot just use one. Note that there were some issues in 3.8/3.9 with wheels that have been resolved in 3.10, so it’s recommended you to use 3.10.
Steven Pitman
This means you need to have multiple levels of Python installed, and build for each one!
This also has the operating system level (os390_27_00 – this may be constant across machines) and the hardware 1090. For this to work on other hardware, one solution would be to manually rename the file to pretend it is for a different machine, but this both not supported nor recommended, and has no guarantee to work. So it is hard to know the best thing to do. I do not have every 390 machine from IBM to do a build on !
Failing build. This built but did not install.
python3 setup.py bdist –format=tar
It built the package and create a file
./dist/pymqi-1.12.0.os390-27.00-1090.tar
This tar file is not completely readable by the z/OS tar command.
When I used tar -tf ….tar it gave
FSUMF371 Value 1641318408.0 is not valid for keyword mtime. Keyword not set.
It uses a Python tar command, not the operating system tar command.
You can display the contents using a Python program like
import tarfile
tar = tarfile.open("dist/pymqi-1.12.0.os390-27.00-1090.tar.gz")
# tar.extractall()
for x in tar:
print(x)
This gave output like
<TarInfo ‘.’ at 0x5008ad3880>
<TarInfo ‘./usr’ at 0x5008ad3dc0>
<TarInfo ‘./usr/lpp’ at 0x5008ad3a00>
…
Installing the package
From an authorised user in OMVS,
python3 -m pip install –no-cache-dir /u/pymqi/dist/pymqi-1.12.0-cp310-cp310-os390_27_00_1090.whl /u/pymqi/dist/pymqi-1.12.0-cp310-cp310-os390_27_00_1090.whl
Processing /u/pymqi/dist/pymqi-1.12.0-cp310-cp310-os390_27_00_1090.whl
Installing collected packages: pymqi
Successfully installed pymqi-1.12.0
If you do not use –no-cache-dir, you may get
-[33]WARNING: The directory ‘/u/.cache/pip’ or its parent directory is not owned or is not writable by the current user. The cache has been disabled. Check the permissions and owner of that directory. If executing pip with sudo, you should use sudo’s -H flag.-[0]
The compile options
The following text is the compile and bind options used for my code. Some of the options are pymqi specific.
/bin/xlc -DNDEBUG -O3 -qarch=10 -qlanglvl=extc99 -q64
-Wc,DLL
-D_XOPEN_SOURCE_EXTENDED
-D_UNIX03_THREADS
-D_POSIX_THREADS
-D_OPEN_SYS_FILE_EXT
-qexportall -qascii -qstrict -qnocsect
-Wa,asa,goff -Wa,xplink
-qgonumber -qenum=int
-DPYQMI_BINDINGS_MODE_BUILD=1 -I//’COLIN.MQ924.SCSQC370′
-I/u/tmp/python/usr/lpp/IBM/cyp/v3r10/pyz/include/python3.10
-c code/pymqi/cpmqe.c
-o build/temp.os390-27.00-1090-3.10/code/pymqi/cpmqe.o
/bin/xlc build/temp.os390-27.00-1090-3.10/code/pymqi/cpmqe.o -L.
-o build/lib.os390-27.00-1090-3.10/pymqi/cpmqe.cpython-310.so -Wl,INFO,LIST,MAP,DLL //’COLIN.MQ924.SCSQDEFS.OBJ(CSQBMQ2X)’
-Wl,dll
/u/tmp/python/usr/lpp/IBM/cyp/v3r10/pyz/lib/python3.10/config-3.10/libpython3.10.x
-q64