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:
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.
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.