Guru: Calling RPG Programs From Python, Part 1
March 30, 2020 Mike Larsen
In a prior article, I showed how to pass parameters to a Python script and execute the script from an RPG program. Based on feedback and my own curiosity, I wanted to see how I could pass parameters to an RPG program and call it from Python. After a bit of research, I found the Python interface itoolkit.
itoolkit is an open source project provided by IBM as an interface to the XMLSERVICE toolkit, which allows us to call RPG programs, service programs, CL programs, and PASE Shell commands. itoolkit can be installed using an SSH terminal with the following command:
pip3 install itoolkit
I use PuTTY as my SSH client. (Please refer to this article for instructions on how to install PuTTY.) Once you have PuTTY installed on your PC, you can access it easily from ACS (Access Client Solutions). ACS automatically detects the SSH client installed on your PC and then provides a link for you to access it (Figure 1).
Note that you need to have the SSH daemon running on your IBM i in order for you to launch an SSH terminal. You can start the SSH daemon by running the following CL command on IBM i:
STRTCPSVR *SSHD
When I launch the SSH terminal, I’m asked to provide my password. Once I’ve entered a valid password, I’m now able to execute the command to install itoolkit (Figure 2).
With the itoolkit installed, you’re ready to make Python talk to RPG. Let me show you a simple example that shows how to pass a string from Python to RPG as well as pass a string back from RPG.
This story contains code, which you can download here.
With the following code, I import the itoolkit modules that will allow me to call an RPG program. I also assign them to local names to make them easier to work with.
from itoolkit import * from itoolkit.lib.ilibcall import *
Now that I have the modules prepared, I can start working with the functions the modules provide. Since I’m calling an RPG program, I need to make sure the library in which the object exists is in my library list. To take care of that, I add a command (icmd) to the itoolkit module that allows me to execute a CL command. In this case, I added the ADDLIBLE command to ensure my program’s object library is in my library list.
itool.add(iCmd('addlible', 'addlible mllib'))
Next, I add the call to my program, Mlr100Tk , and pass both an input and output parameter.
itool.add( iPgm('my_results',' Mlr100Tk') .addParm(iData('InFirstName','10a','Mike')) .addParm(iData('OutLastName','10a',' ')) )
Inside the itool.add function, there are three lines of code I’d like to explain. The first line, iPgm, has two parameters. The first indicates where the results are to be stored, and the second indicates the name of the RPG program to be called. The next two lines define the parameters the RPG program is expecting, and are defined by iData. As this is a very basic example, I just pass a first name to the RPG program and it passes back a last name. I define both of the parameters as 10 positions with an alpha data type.
Finally, I use the iLibCall function (by way of the itransport variable) to execute the command to add the library list entry and call the RPG program.
itool.call(itransport)
When I call the program, the result is passed back as a JSON formatted dictionary. I assign the results to a variable so I can work with them further.
# results are returned as a dictionary formatted as Json mypgm_results = itool.dict_out('my_results')
Before I execute the Python script, it makes sense to show the RPG program being called. There isn’t much going in the program, so I’m showing the entire program in one shot.
// Prototypes (entry parameters) dcl-pr Mlr100Tk ExtPgm; inFirstName char(10); outLastName char(10); End-pr; // - - - - - - - // Main procedure interface dcl-pi Mlr100Tk; inFirstName char(10); outLastName char(10); end-pi; //--------------------- If %trim(inFirstName) = 'Mike'; outLastName = 'Larsen'; Else; outLastName = 'Smith'; Endif; *Inlr = *On; Return;
All I do is set up my parameters and populate the output parameter being sent back to the Python script.
Now that the script and program are in place, I open an SSH terminal and execute the Python script (Figure 3).
When I execute the script and print the output (code below), I get a JSON object that contains the input and output parameters and also a return message that indicates the program was called successfully. Then I scan the output for the string Success. If I find it, I print the word Success to the terminal, otherwise I indicate that there were errors.
print(mypgm_results) if 'success' in mypgm_results: print('Success!') else: print('Errors occurred.')
Since the output returned in this example is small, I can easily read the results. But what if the JSON being returned was larger and more complex? I added some extra code in the script to parse the JSON into individual components to make it easier to decipher.
# parse the Json response from the dictionary print('\n') print("Parameter passed TO Rpg: ", mypgm_results['InFirstName']) print("Parameter passed FROM Rpg: ", mypgm_results['OutLastName']) print("Status of the program call: ", mypgm_results['success'])
The parsed response is shown in Figure 4.
Hopefully this brief introduction will get you started integrating Python and RPG and allowing you to integrate with your legacy system. In a future article, I’ll take this a step further to show how to pass data structures to RPG.
Mike Larsen is a project manager and senior developer at Central Park Data Systems and has been working with IBM i systems for over 20 years. He specializes in RPG, CL, and SQL and recently has been working with PHP and Python. Current projects have given Mike the opportunity to work with generating and parsing XML and JSON from SQL and consuming SOAP and REST web services. Although his main area of expertise is on IBM i, Mike has a passion for learning other languages and how he can integrate other platforms with IBM i.
RELATED STORIES
Guru: Finding Large Files With Python
I had some difficulty getting this working.
For one, I did not have all the required open source components installed.
Never having used PIP3 before I had to install that. So I found it in the ACS Open Source Package Management application and installed it. Then I tried the PIP3 Install itoolkit. It ran but failed. I saw a version of ITOOLKIT in the package manager and wondered if I could use that instead. It did not work. Searching around the web for similar errors, I found a suggestion to instill the dev tools for Python. So I installed Python3-devel. I think I got PIP3 to install correctly after that but still the python program failed. So I searched around for similar errors, installed some other open source components, Eventually I found an article that said itoolkit.lib.ilibcall does not work in 64 bit mode. https://github.com/IBM/python-itoolkit/issues/17 They suggested using from itoolkit.db2.idb2call import *, itransport = iDB2Call(). I tried that and got a different error related to ibm_db. Then I installed python3-ibm_db. finally it worked!
So getting this up and running involved a lot of required open source components. It would be good to know exactly which components are required. Another thing that I don’t understand is regarding the itoolkit. Is the itoolkit installed with PIP3 the same as the python3-itoolkit that I installed via the open source package manager? Is it one or the other? Is one preferred over the other?
All the difficulty aside, I was surprised how well it worked once it was up and running. The performance was excellent. It does not behave like a java program. The response is quick and immediate. I can definitely see using this capability in future projects.
Hi, Doug. Thanks for reading my article and for the great questions. My understanding is installing via pip or ACS Open Source Package Management should yield the same results. In my case, I had already installed everything related to Python using Open Source Package Manager, so I had all Python packages available to me. With that said, I did some additional research and found that python3, python3-pip, python-ibm_db, and python-itoolkit are required to use iToolkit.
I installed iToolkit using Open Source Package Manager and it worked fine for me. To address your question about whether iToolkit is installed via Open Source Package Manager or pip is the same, I tried loading it via pip and received a message ‘ Requirement already satisfied: itoolkit in /QOpenSys/pkgs/lib/python3.6/site-packages’. Since I had already loaded it using Open Source Package Manager, that indicated to me that they are the same.
I’m glad you got it working and I hope you find it useful. Part 2 of the article came out a few weeks ago. Please look when you have a chance and let me know if you have any additional questions. Mike
Hi Mike, the pre-condition is to install SSH in iACS? I find my iACS does not have it.