mapepire-python
is a Python client implementation for Mapepire that provides a simple interface for connecting to an IBM i server and running SQL queries. The client is designed to work with the Mapepire Server Component
mapepire-python
requires Python 3.10 or later.
pip
mapepire-python
is available on PyPi. Just Run
pip install mapepire-python
To use mapire-python, you will need to have the Mapepire Server Component running on your IBM i server. Follow these instructions to set up the server component: Mapepire Server Installation
There are three ways to configure mapepire server connection details using mapepire-python
:
DaemonServer
object.ini
) to store the connection detailsDaemonServer
objectto use the DaemonServer
object, you will need to import the DaemonServer
class from the mapepire_python.data_types
module:
from mapepire_python.data_types import DaemonServer
creds = DaemonServer(
host="SERVER",
port="PORT",
user="USER",
password="PASSWORD",
ignoreUnauthorized=True
)
Once you have created the DaemonServer
object, you can pass it to the SQLJob
object to connect to the mapepire server:
from mapepire_python.client.sql_job import SQLJob
from mapepire_python.data_types import DaemonServer
creds = DaemonServer(
host="SERVER",
port="PORT",
user="USER",
password="PASSWORD",
ignoreUnauthorized=True
)
job = SQLJob(creds)
You can also use a dictionary to configure the connection details:
from mapepire_python.client.sql_job import SQLJob
creds = {
"host": "SERVER",
"port": "port",
"user": "USER",
"password": "PASSWORD",
}
job = SQLJob(creds)
this is a convenient way to pass the connection details to the mapepire server.
.ini
) to store the connection detailsIf you use a config file (.ini
), you can pass the path to the file as an argument:
First create a mapepire.ini
file in the root of your project with the following required fields:
[mapepire]
SERVER="SERVER"
PORT="PORT"
USER="USER"
PASSWORD="PASSWORD"
Then you can create a SQLJob
object by passing the path to the .ini
file which will handle the connection details
from mapepire_python.client.sql_job import SQLJob
job = SQLJob("./mapepire.ini", section="mapepire")
The section
argument is optional and allows you to specify a specific section in the .ini
file where the connection details are stored. This allows you to store multiple connection details to different systems in the same file. If you do not specify a section
, the first section in the file will be used.
Depending on your setup and use case, you can choose the most convenient way to configure the connection details. The following usage examples are compatible with all three connection options detailed above. For simplicity, we assume there is a mapepire.ini
file in the root of the project with the connection details.
There are four main ways to run queries using mapepire-python
:
SQLJob
object to run queries synchronouslyPoolJob
object to run queries asynchronouslyPool
object to run queries "concurrently"SQLJob
object to run queries synchronouslyfrom mapepire_python.client.sql_job import SQLJob
with SQLJob("./mapepire.ini") as sql_job:
with sql_job.query("select * from sample.employee") as query:
result = query.run(rows_to_fetch=1)
print(result)
Here is the output from the script above:
{
"id":"query3",
"has_results":true,
"update_count":-1,
"metadata":{
"column_count":14,
"job":"330955/QUSER/QZDASOINIT",
"columns":[
{
"name":"EMPNO",
"type":"CHAR",
"display_size":6,
"label":"EMPNO"
},
{
"name":"FIRSTNME",
"type":"VARCHAR",
"display_size":12,
"label":"FIRSTNME"
},
{
"name":"MIDINIT",
"type":"CHAR",
"display_size":1,
"label":"MIDINIT"
},
{
"name":"LASTNAME",
"type":"VARCHAR",
"display_size":15,
"label":"LASTNAME"
},
{
"name":"WORKDEPT",
"type":"CHAR",
"display_size":3,
"label":"WORKDEPT"
},
{
"name":"PHONENO",
"type":"CHAR",
"display_size":4,
"label":"PHONENO"
},
{
"name":"HIREDATE",
"type":"DATE",
"display_size":10,
"label":"HIREDATE"
},
{
"name":"JOB",
"type":"CHAR",
"display_size":8,
"label":"JOB"
},
{
"name":"EDLEVEL",
"type":"SMALLINT",
"display_size":6,
"label":"EDLEVEL"
},
{
"name":"SEX",
"type":"CHAR",
"display_size":1,
"label":"SEX"
},
{
"name":"BIRTHDATE",
"type":"DATE",
"display_size":10,
"label":"BIRTHDATE"
},
{
"name":"SALARY",
"type":"DECIMAL",
"display_size":11,
"label":"SALARY"
},
{
"name":"BONUS",
"type":"DECIMAL",
"display_size":11,
"label":"BONUS"
},
{
"name":"COMM",
"type":"DECIMAL",
"display_size":11,
"label":"COMM"
}
]
},
"data":[
{
"EMPNO":"000010",
"FIRSTNME":"CHRISTINE",
"MIDINIT":"I",
"LASTNAME":"HAAS",
"WORKDEPT":"A00",
"PHONENO":"3978",
"HIREDATE":"01/01/65",
"JOB":"PRES",
"EDLEVEL":18,
"SEX":"F",
"BIRTHDATE":"None",
"SALARY":52750.0,
"BONUS":1000.0,
"COMM":4220.0
}
],
"is_done":false,
"success":true
}
The results object is a JSON object that contains the metadata and data from the query. Here are the different fields returned:
id
field contains the query IDhas_results
field indicates whether the query returned any resultsupdate_count
field indicates the number of rows updated by the query (-1 if the query did not update any rows)metadata
field contains information about the columns returned by the querydata
field contains the results of the query is_done
field indicates whether the query has finished executingsuccess
field indicates whether the query was successful. In the ouput above, the query was successful and returned one row of data.
To create and run a query in a single step, use the query_and_run
method:
from mapepire_python.client.sql_job import SQLJob
with SQLJob("./mapepire.ini") as sql_job:
# query automatically closed after running
results = sql_job.query_and_run("select * from sample.employee", rows_to_fetch=1)
print(result)
PoolJob
object to run queries asynchronouslyThe PoolJob
object can be used to create and run queries asynchronously:
import asyncio
from mapepire_python.pool.pool_job import PoolJob
async def main():
async with PoolJob("./mapepire.ini") as pool_job:
async with pool_job.query('select * from sample.employee') as query:
res = await query.run(rows_to_fetch=1)
if __name__ == '__main__':
asyncio.run(main())
To run a create and run a query asynchronously in a single step, use the query_and_run
method:
import asyncio
from mapepire_python.pool.pool_job import PoolJob
async def main():
async with PoolJob("./mapepire.ini") as pool_job:
res = await pool_job.query_and_run("select * from sample.employee", rows_to_fetch=1)
print(res)
if __name__ == '__main__':
asyncio.run(main())
Pool
object to run queries "concurrently"The Pool
object can be used to create a pool of PoolJob
objects to run queries concurrently.
import asyncio
from mapepire_python.pool.pool_client import Pool, PoolOptions
async def main():
async with Pool(
options=PoolOptions(
creds="./mapepire.ini",
opts=None,
max_size=5,
starting_size=3
)
) as pool:
job_names = []
resultsA = await asyncio.gather(
pool.execute('values (job_name)'),
pool.execute('values (job_name)'),
pool.execute('values (job_name)')
)
job_names = [res['data'][0]['00001'] for res in resultsA]
print(job_names)
if __name__ == '__main__':
asyncio.run(main())
This script will create a pool of 3 PoolJob
objects and run the query values (job_name)
concurrently. The results will be printed to the console.
['004460/QUSER/QZDASOINIT', '005096/QUSER/QZDASOINIT', '005319/QUSER/QZDASOINIT']
PEP 249 is the Python Database API Specification v2.0. The mapepire-python
client provides a PEP 249 implementation that allows you to use the Connection
and Cursor
objects to interact with the Mapepire server. Like the examples above, we can pass the mapepire.ini
file to the connect
function to create a connection to the server:
from mapepire_python import connect
with connect("./mapepire.ini") as conn:
with conn.execute("select * from sample.employee") as cursor:
result = cursor.fetchone()
print(result)
fetchmany()
and fetchall()
methodsThe Cursor
object provides the fetchmany()
and fetchall()
methods to fetch multiple rows from the result set:
with connect("./mapepire.ini") as conn:
with conn.execute("select * from sample.employee") as cursor:
results = cursor.fetchmany(size=2)
print(results)
with connect("./mapepire.ini") as conn:
with conn.execute("select * from sample.employee") as cursor:
results = cursor.fetchall()
print(results)
The PEP 249 implementation also provides an asynchronous interface for running queries. The connect
function returns an asynchronous context manager that can be used with the async with
statement:
import asyncio
from mapepire_python.asycnio import connect
async def main():
async with connect("./mapepire.ini") as conn:
async with await conn.execute("select * from sample.employee") as cursor:
result = await cursor.fetchone()
print(result)
if __name__ == '__main__':
asyncio.run(main())
This guide provides instructions for setting up a Python virtual environment using either venv
or conda
.
requirements-dev.txt
Note: This applies to supported versions of Python 3.10 and higher
navigate to the project's directory and run the following command. This will create a new virtual environment in a local folder named .venv
cd mapepire-python/
python3 -m venv .venv
py -m venv .venv
The second argument is the location of the virtual environment, which will create a the virtual environment in the mapepire-python project root directory: mapepire-python/.venv
before installing the project dependencies, activate
the virtual environment to put the environment-specific python
and pip
executables into your shell's PATH
source .venv/bin/activate
.venv\Scripts\activate
Confirm the virtual environment is activated, check the location of the Python interpreter:
which python
where python
Expected output should be:
.venv/bin/python # Unix/macOS
.venv\Scripts\python # Windows
To deactivate the virtual environment, run:
deactivate
from the mapepire-python
project directory
Make sure pip is up to date:
python3 -m pip install --upgrade pip
python3 -m pip --version
py -m pip install --upgrade pip
py -m pip --version
requirements-dev.txt
Run the following to install the project dependencies:
python3 -m pip install -r requirements-dev.txt
py -m pip install -r requirements-dev.txt
First, install Conda if you haven't already by following the instructions in this guide. There are installers for macOS/Windows and Linux. I recommend the following installers for this project:
In a terminal, navigate to the mapepire-python
project directory and run the following command:
cd mapepire-python/
conda env create -f environment-dev.yml
The conda env create
command will create a python environment called mapepire-dev
.
conda activate mapepire-dev
conda env list
You can also use conda info --envs.
To deactivate, call:
conda deactivate
First, create a pytest.ini
file in the tests
directory.
tests/pytest.ini
[pytest]
env =
VITE_SERVER=IBMI_SERVER
VITE_DB_USER=USER
VITE_DB_PASS=PASS
Run the test suite from the mapepire-python
directory:
# activate python development environment first
pytest tests/