mapepire-python 0.1.3__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/PKG-INFO +205 -27
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/README.md +201 -26
- mapepire_python-0.1.5/mapepire_python/base_job.py +42 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python/client/query.py +31 -17
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python/client/sql_job.py +80 -30
- mapepire_python-0.1.5/mapepire_python/client/websocket.py +37 -0
- mapepire_python-0.1.3/mapepire_python/types.py → mapepire_python-0.1.5/mapepire_python/data_types.py +8 -2
- mapepire_python-0.1.5/mapepire_python/pool/pool_client.py +141 -0
- mapepire_python-0.1.5/mapepire_python/pool/pool_job.py +303 -0
- mapepire_python-0.1.5/mapepire_python/pool/pool_query.py +136 -0
- mapepire_python-0.1.5/mapepire_python/query_manager.py +68 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python/ssl.py +1 -1
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python/version.py +1 -1
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python.egg-info/PKG-INFO +205 -27
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python.egg-info/SOURCES.txt +8 -3
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python.egg-info/requires.txt +3 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/pyproject.toml +3 -0
- mapepire_python-0.1.3/mapepire_python/pool.py +0 -29
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/LICENSE +0 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python/py.typed +0 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python.egg-info/dependency_links.txt +0 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/mapepire_python.egg-info/top_level.txt +0 -0
- {mapepire_python-0.1.3 → mapepire_python-0.1.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mapepire-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Author-email: Allen Institute for Artificial Intelligence <contact@allenai.org>
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -217,12 +217,15 @@ Description-Content-Type: text/markdown
|
|
|
217
217
|
License-File: LICENSE
|
|
218
218
|
Requires-Dist: dataclasses-json>=0.6.4
|
|
219
219
|
Requires-Dist: websocket-client>=1.2.1
|
|
220
|
+
Requires-Dist: websockets
|
|
221
|
+
Requires-Dist: pyee
|
|
220
222
|
Provides-Extra: dev
|
|
221
223
|
Requires-Dist: ruff; extra == "dev"
|
|
222
224
|
Requires-Dist: mypy<1.5,>=1.0; extra == "dev"
|
|
223
225
|
Requires-Dist: black<24.0,>=23.0; extra == "dev"
|
|
224
226
|
Requires-Dist: isort<5.13,>=5.12; extra == "dev"
|
|
225
227
|
Requires-Dist: pytest; extra == "dev"
|
|
228
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
226
229
|
Requires-Dist: pytest-sphinx; extra == "dev"
|
|
227
230
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
228
231
|
Requires-Dist: twine>=1.11.0; extra == "dev"
|
|
@@ -243,8 +246,8 @@ Requires-Dist: pre-commit; extra == "dev"
|
|
|
243
246
|
<a href="https://github.com/Mapepire-IBMi/mapepire-python/actions">
|
|
244
247
|
<img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Mapepire-IBMi/mapepire-python/main.yml">
|
|
245
248
|
</a>
|
|
246
|
-
<a href="https://pypi.org/project/python
|
|
247
|
-
<img alt="PyPI" src="https://img.shields.io/pypi/v/python
|
|
249
|
+
<a href="https://pypi.org/project/mapepire-python/">
|
|
250
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/mapepire-python">
|
|
248
251
|
</a>
|
|
249
252
|
<a href="https://github.com/Mapepire-IBMi/mapepire-python/blob/main/LICENSE">
|
|
250
253
|
<img alt="License" src="https://img.shields.io/github/license/allenai/tango.svg?color=blue&cachedrop">
|
|
@@ -252,35 +255,47 @@ Requires-Dist: pre-commit; extra == "dev"
|
|
|
252
255
|
<br/>
|
|
253
256
|
</div>
|
|
254
257
|
|
|
255
|
-
- [
|
|
256
|
-
- [
|
|
257
|
-
- [
|
|
258
|
-
|
|
259
|
-
- [
|
|
258
|
+
- [mapepire-python](#mapepire-python)
|
|
259
|
+
- [Overview](#overview)
|
|
260
|
+
- [Setup](#setup)
|
|
261
|
+
- [Install with `pip`](#install-with-pip)
|
|
262
|
+
- [Server Component Setup](#server-component-setup)
|
|
263
|
+
- [Example usage](#example-usage)
|
|
264
|
+
- [Query and run](#query-and-run)
|
|
265
|
+
- [Asynchronous Query Execution](#asynchronous-query-execution)
|
|
266
|
+
- [Pooling (beta)](#pooling-beta)
|
|
260
267
|
- [Development Setup](#development-setup)
|
|
261
268
|
- [Setup python virtual environment with pip and venv](#setup-python-virtual-environment-with-pip-and-venv)
|
|
262
269
|
- [Create a new virtual environment](#create-a-new-virtual-environment)
|
|
263
270
|
- [Unix/macOS](#unixmacos)
|
|
264
271
|
- [Windows](#windows)
|
|
265
272
|
- [Activate the virtual environment:](#activate-the-virtual-environment)
|
|
273
|
+
- [Unix.macOS](#unixmacos-1)
|
|
274
|
+
- [Windows](#windows-1)
|
|
275
|
+
- [Unix/macOS](#unixmacos-2)
|
|
276
|
+
- [Windows](#windows-2)
|
|
266
277
|
- [Prepare pip](#prepare-pip)
|
|
278
|
+
- [Unix/macOS](#unixmacos-3)
|
|
279
|
+
- [Windows](#windows-3)
|
|
267
280
|
- [Install Dependencies using `requirements-dev.txt`](#install-dependencies-using-requirements-devtxt)
|
|
281
|
+
- [Unix/macOS](#unixmacos-4)
|
|
282
|
+
- [Windows](#windows-4)
|
|
268
283
|
- [Setup Python virtual environment with Conda](#setup-python-virtual-environment-with-conda)
|
|
269
284
|
- [Create an environment from an environment-dev.yml file](#create-an-environment-from-an-environment-devyml-file)
|
|
270
285
|
- [1. Activate the new environment:](#1-activate-the-new-environment)
|
|
271
286
|
- [2. Verify the new environment was installed:](#2-verify-the-new-environment-was-installed)
|
|
272
|
-
- [Run local test suite](#run-local-test-suite)
|
|
287
|
+
- [Run local test suite](#run-local-test-suite)
|
|
273
288
|
|
|
274
289
|
|
|
275
290
|
|
|
291
|
+
## Overview
|
|
276
292
|
|
|
277
|
-
|
|
293
|
+
<img src="images/mapepire-logo.png" alt="logo" width="200"/>
|
|
278
294
|
|
|
279
|
-
|
|
295
|
+
---
|
|
280
296
|
|
|
281
|
-

|
|
282
297
|
|
|
283
|
-
`mapepire-python` is a Python client implementation that
|
|
298
|
+
`mapepire-python` is a Python client implementation for [Mapepire](https://github.com/Mapepire-IBMi) 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](https://github.com/Mapepire-IBMi/mapepire-server)
|
|
284
299
|
|
|
285
300
|
## Setup
|
|
286
301
|
|
|
@@ -288,36 +303,49 @@ Requires-Dist: pre-commit; extra == "dev"
|
|
|
288
303
|
|
|
289
304
|
### Install with `pip`
|
|
290
305
|
|
|
291
|
-
`mapepire-python` is available on [PyPi](https://pypi.org/project/python
|
|
306
|
+
`mapepire-python` is available on [PyPi](https://pypi.org/project/mapepire-python/). Just Run
|
|
292
307
|
|
|
293
308
|
```bash
|
|
294
|
-
pip install python
|
|
309
|
+
pip install mapepire-python
|
|
295
310
|
```
|
|
296
311
|
|
|
297
|
-
### Server Component Setup
|
|
312
|
+
### Server Component Setup
|
|
313
|
+
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](https://mapepire-ibmi.github.io/guides/sysadmin/)
|
|
298
314
|
|
|
299
315
|
## Example usage
|
|
300
316
|
|
|
301
|
-
|
|
317
|
+
Setup the server credentials used to connect to the server. One way to do this is to create a `mapepire.ini` file in the root of your project with the following content:
|
|
318
|
+
|
|
319
|
+
```ini
|
|
320
|
+
[mapepire]
|
|
321
|
+
SERVER="SERVER"
|
|
322
|
+
PORT="PORT"
|
|
323
|
+
USER="USER"
|
|
324
|
+
PASSWORD="PASSWORD"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
The following script sets up a `DaemonServer` object that will be used to connect with the Server Component. Then a single `SQLJob` is created to facilitate the connection from the client side.
|
|
302
328
|
|
|
303
329
|
```python
|
|
330
|
+
import configparser
|
|
304
331
|
from mapepire_python.client.sql_job import SQLJob
|
|
305
332
|
from mapepire_python.types import DaemonServer
|
|
306
333
|
|
|
334
|
+
config = configparser.ConfigParser()
|
|
335
|
+
config.read('mapepire.ini')
|
|
336
|
+
|
|
307
337
|
creds = DaemonServer(
|
|
308
|
-
host=
|
|
309
|
-
port=
|
|
310
|
-
user=
|
|
311
|
-
password=
|
|
338
|
+
host=config['mapepire']['SERVER'],
|
|
339
|
+
port=config['mapepire']['PORT'],
|
|
340
|
+
user=config['mapepire']['USER'],
|
|
341
|
+
password=config['mapepire']['PASSWORD'],
|
|
312
342
|
ignoreUnauthorized=True
|
|
313
343
|
)
|
|
314
344
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
result = query.run(rows_to_fetch=3)
|
|
320
|
-
print(result)
|
|
345
|
+
with SQLJob(creds) as sql_job:
|
|
346
|
+
with sql_job.query("select * from sample.employee") as query:
|
|
347
|
+
result = query.run(rows_to_fetch=1)
|
|
348
|
+
print(result)
|
|
321
349
|
```
|
|
322
350
|
|
|
323
351
|
Here is the output from the script above:
|
|
@@ -440,6 +468,156 @@ Here is the output from the script above:
|
|
|
440
468
|
}
|
|
441
469
|
|
|
442
470
|
```
|
|
471
|
+
The results object is a JSON object that contains the metadata and data from the query. Here are the different fields returned:
|
|
472
|
+
- `id` field contains the query ID
|
|
473
|
+
- `has_results` field indicates whether the query returned any results
|
|
474
|
+
- `update_count` field indicates the number of rows updated by the query (-1 if the query did not update any rows)
|
|
475
|
+
- `metadata` field contains information about the columns returned by the query
|
|
476
|
+
- `data` field contains the results of the query
|
|
477
|
+
- `is_done` field indicates whether the query has finished executing
|
|
478
|
+
- `success` field indicates whether the query was successful.
|
|
479
|
+
|
|
480
|
+
In the ouput above, the query was successful and returned one row of data.
|
|
481
|
+
|
|
482
|
+
### Query and run
|
|
483
|
+
|
|
484
|
+
To create and run a query in a single step, use the `query_and_run` method:
|
|
485
|
+
|
|
486
|
+
```python
|
|
487
|
+
import configparser
|
|
488
|
+
from mapepire_python.client.sql_job import SQLJob
|
|
489
|
+
from mapepire_python.types import DaemonServer
|
|
490
|
+
|
|
491
|
+
config = configparser.ConfigParser()
|
|
492
|
+
config.read('mapepire.ini')
|
|
493
|
+
|
|
494
|
+
creds = DaemonServer(
|
|
495
|
+
host=config['mapepire']['SERVER'],
|
|
496
|
+
port=config['mapepire']['PORT'],
|
|
497
|
+
user=config['mapepire']['USER'],
|
|
498
|
+
password=config['mapepire']['PASSWORD'],
|
|
499
|
+
ignoreUnauthorized=True
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
with SQLJob(creds) as sql_job:
|
|
503
|
+
# query automatically closed after running
|
|
504
|
+
results = sql_job.query_and_run("select * from sample.employee", rows_to_fetch=1)
|
|
505
|
+
print(result)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Asynchronous Query Execution
|
|
509
|
+
|
|
510
|
+
The `PoolJob` object can be used to create and run queries asynchronously:
|
|
511
|
+
|
|
512
|
+
```python
|
|
513
|
+
import asyncio
|
|
514
|
+
import configparser
|
|
515
|
+
from mapepire_python.pool.pool_job import PoolJob
|
|
516
|
+
from mapepire_python.types import DaemonServer
|
|
517
|
+
|
|
518
|
+
config = configparser.ConfigParser()
|
|
519
|
+
config.read('mapepire.ini')
|
|
520
|
+
|
|
521
|
+
creds = DaemonServer(
|
|
522
|
+
host=config['mapepire']['SERVER'],
|
|
523
|
+
port=config['mapepire']['PORT'],
|
|
524
|
+
user=config['mapepire']['USER'],
|
|
525
|
+
password=config['mapepire']['PASSWORD'],
|
|
526
|
+
ignoreUnauthorized=True
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
async def main():
|
|
530
|
+
async with PoolJob(creds=creds) as pool_job:
|
|
531
|
+
async with pool_job.query('select * from sample.employee') as query:
|
|
532
|
+
res = await query.run(rows_to_fetch=1)
|
|
533
|
+
|
|
534
|
+
if __name__ == '__main__':
|
|
535
|
+
asyncio.run(main())
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
To run a create and run a query asynchronously in a single step, use the `query_and_run` method:
|
|
540
|
+
|
|
541
|
+
```python
|
|
542
|
+
import asyncio
|
|
543
|
+
import configparser
|
|
544
|
+
from mapepire_python.pool.pool_job import PoolJob
|
|
545
|
+
from mapepire_python.types import DaemonServer
|
|
546
|
+
|
|
547
|
+
config = configparser.ConfigParser()
|
|
548
|
+
config.read('mapepire.ini')
|
|
549
|
+
|
|
550
|
+
creds = DaemonServer(
|
|
551
|
+
host=config['mapepire']['SERVER'],
|
|
552
|
+
port=config['mapepire']['PORT'],
|
|
553
|
+
user=config['mapepire']['USER'],
|
|
554
|
+
password=config['mapepire']['PASSWORD'],
|
|
555
|
+
ignoreUnauthorized=True
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
async def main():
|
|
559
|
+
async with PoolJob(creds=creds) as pool_job:
|
|
560
|
+
res = await pool_job.query_and_run(rows_to_fetch=1)
|
|
561
|
+
print(res)
|
|
562
|
+
|
|
563
|
+
if __name__ == '__main__':
|
|
564
|
+
asyncio.run(main())
|
|
565
|
+
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
## Pooling (beta)
|
|
570
|
+
|
|
571
|
+
The `Pool` object can be used to create a pool of `PoolJob` objects to run queries concurrently.
|
|
572
|
+
|
|
573
|
+
```python
|
|
574
|
+
import asyncio
|
|
575
|
+
import configparser
|
|
576
|
+
from mapepire_python.pool.pool_client import Pool, PoolOptions
|
|
577
|
+
from mapepire_python.types import DaemonServer
|
|
578
|
+
|
|
579
|
+
config = configparser.ConfigParser()
|
|
580
|
+
config.read('mapepire.ini')
|
|
581
|
+
|
|
582
|
+
creds = DaemonServer(
|
|
583
|
+
host=config['mapepire']['SERVER'],
|
|
584
|
+
port=config['mapepire']['PORT'],
|
|
585
|
+
user=config['mapepire']['USER'],
|
|
586
|
+
password=config['mapepire']['PASSWORD'],
|
|
587
|
+
ignoreUnauthorized=True
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
async def main():
|
|
592
|
+
async with Pool(
|
|
593
|
+
options=PoolOptions(
|
|
594
|
+
creds=creds,
|
|
595
|
+
opts=None,
|
|
596
|
+
max_size=5,
|
|
597
|
+
starting_size=3
|
|
598
|
+
)
|
|
599
|
+
) as pool:
|
|
600
|
+
job_names = []
|
|
601
|
+
resultsA = await asyncio.gather(
|
|
602
|
+
pool.execute('values (job_name)'),
|
|
603
|
+
pool.execute('values (job_name)'),
|
|
604
|
+
pool.execute('values (job_name)')
|
|
605
|
+
)
|
|
606
|
+
job_names = [res['data'][0]['00001'] for res in resultsA]
|
|
607
|
+
|
|
608
|
+
print(job_names)
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
if __name__ == '__main__':
|
|
612
|
+
asyncio.run(main())
|
|
613
|
+
|
|
614
|
+
```
|
|
615
|
+
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.
|
|
616
|
+
|
|
617
|
+
```bash
|
|
618
|
+
['004460/QUSER/QZDASOINIT', '005096/QUSER/QZDASOINIT', '005319/QUSER/QZDASOINIT']
|
|
619
|
+
```
|
|
620
|
+
|
|
443
621
|
|
|
444
622
|
# Development Setup
|
|
445
623
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<a href="https://github.com/Mapepire-IBMi/mapepire-python/actions">
|
|
4
4
|
<img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Mapepire-IBMi/mapepire-python/main.yml">
|
|
5
5
|
</a>
|
|
6
|
-
<a href="https://pypi.org/project/python
|
|
7
|
-
<img alt="PyPI" src="https://img.shields.io/pypi/v/python
|
|
6
|
+
<a href="https://pypi.org/project/mapepire-python/">
|
|
7
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/mapepire-python">
|
|
8
8
|
</a>
|
|
9
9
|
<a href="https://github.com/Mapepire-IBMi/mapepire-python/blob/main/LICENSE">
|
|
10
10
|
<img alt="License" src="https://img.shields.io/github/license/allenai/tango.svg?color=blue&cachedrop">
|
|
@@ -12,35 +12,47 @@
|
|
|
12
12
|
<br/>
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
|
|
19
|
-
- [
|
|
15
|
+
- [mapepire-python](#mapepire-python)
|
|
16
|
+
- [Overview](#overview)
|
|
17
|
+
- [Setup](#setup)
|
|
18
|
+
- [Install with `pip`](#install-with-pip)
|
|
19
|
+
- [Server Component Setup](#server-component-setup)
|
|
20
|
+
- [Example usage](#example-usage)
|
|
21
|
+
- [Query and run](#query-and-run)
|
|
22
|
+
- [Asynchronous Query Execution](#asynchronous-query-execution)
|
|
23
|
+
- [Pooling (beta)](#pooling-beta)
|
|
20
24
|
- [Development Setup](#development-setup)
|
|
21
25
|
- [Setup python virtual environment with pip and venv](#setup-python-virtual-environment-with-pip-and-venv)
|
|
22
26
|
- [Create a new virtual environment](#create-a-new-virtual-environment)
|
|
23
27
|
- [Unix/macOS](#unixmacos)
|
|
24
28
|
- [Windows](#windows)
|
|
25
29
|
- [Activate the virtual environment:](#activate-the-virtual-environment)
|
|
30
|
+
- [Unix.macOS](#unixmacos-1)
|
|
31
|
+
- [Windows](#windows-1)
|
|
32
|
+
- [Unix/macOS](#unixmacos-2)
|
|
33
|
+
- [Windows](#windows-2)
|
|
26
34
|
- [Prepare pip](#prepare-pip)
|
|
35
|
+
- [Unix/macOS](#unixmacos-3)
|
|
36
|
+
- [Windows](#windows-3)
|
|
27
37
|
- [Install Dependencies using `requirements-dev.txt`](#install-dependencies-using-requirements-devtxt)
|
|
38
|
+
- [Unix/macOS](#unixmacos-4)
|
|
39
|
+
- [Windows](#windows-4)
|
|
28
40
|
- [Setup Python virtual environment with Conda](#setup-python-virtual-environment-with-conda)
|
|
29
41
|
- [Create an environment from an environment-dev.yml file](#create-an-environment-from-an-environment-devyml-file)
|
|
30
42
|
- [1. Activate the new environment:](#1-activate-the-new-environment)
|
|
31
43
|
- [2. Verify the new environment was installed:](#2-verify-the-new-environment-was-installed)
|
|
32
|
-
- [Run local test suite](#run-local-test-suite)
|
|
44
|
+
- [Run local test suite](#run-local-test-suite)
|
|
33
45
|
|
|
34
46
|
|
|
35
47
|
|
|
48
|
+
## Overview
|
|
36
49
|
|
|
37
|
-
|
|
50
|
+
<img src="images/mapepire-logo.png" alt="logo" width="200"/>
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
---
|
|
40
53
|
|
|
41
|
-

|
|
42
54
|
|
|
43
|
-
`mapepire-python` is a Python client implementation that
|
|
55
|
+
`mapepire-python` is a Python client implementation for [Mapepire](https://github.com/Mapepire-IBMi) 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](https://github.com/Mapepire-IBMi/mapepire-server)
|
|
44
56
|
|
|
45
57
|
## Setup
|
|
46
58
|
|
|
@@ -48,36 +60,49 @@
|
|
|
48
60
|
|
|
49
61
|
### Install with `pip`
|
|
50
62
|
|
|
51
|
-
`mapepire-python` is available on [PyPi](https://pypi.org/project/python
|
|
63
|
+
`mapepire-python` is available on [PyPi](https://pypi.org/project/mapepire-python/). Just Run
|
|
52
64
|
|
|
53
65
|
```bash
|
|
54
|
-
pip install python
|
|
66
|
+
pip install mapepire-python
|
|
55
67
|
```
|
|
56
68
|
|
|
57
|
-
### Server Component Setup
|
|
69
|
+
### Server Component Setup
|
|
70
|
+
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](https://mapepire-ibmi.github.io/guides/sysadmin/)
|
|
58
71
|
|
|
59
72
|
## Example usage
|
|
60
73
|
|
|
61
|
-
|
|
74
|
+
Setup the server credentials used to connect to the server. One way to do this is to create a `mapepire.ini` file in the root of your project with the following content:
|
|
75
|
+
|
|
76
|
+
```ini
|
|
77
|
+
[mapepire]
|
|
78
|
+
SERVER="SERVER"
|
|
79
|
+
PORT="PORT"
|
|
80
|
+
USER="USER"
|
|
81
|
+
PASSWORD="PASSWORD"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The following script sets up a `DaemonServer` object that will be used to connect with the Server Component. Then a single `SQLJob` is created to facilitate the connection from the client side.
|
|
62
85
|
|
|
63
86
|
```python
|
|
87
|
+
import configparser
|
|
64
88
|
from mapepire_python.client.sql_job import SQLJob
|
|
65
89
|
from mapepire_python.types import DaemonServer
|
|
66
90
|
|
|
91
|
+
config = configparser.ConfigParser()
|
|
92
|
+
config.read('mapepire.ini')
|
|
93
|
+
|
|
67
94
|
creds = DaemonServer(
|
|
68
|
-
host=
|
|
69
|
-
port=
|
|
70
|
-
user=
|
|
71
|
-
password=
|
|
95
|
+
host=config['mapepire']['SERVER'],
|
|
96
|
+
port=config['mapepire']['PORT'],
|
|
97
|
+
user=config['mapepire']['USER'],
|
|
98
|
+
password=config['mapepire']['PASSWORD'],
|
|
72
99
|
ignoreUnauthorized=True
|
|
73
100
|
)
|
|
74
101
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
result = query.run(rows_to_fetch=3)
|
|
80
|
-
print(result)
|
|
102
|
+
with SQLJob(creds) as sql_job:
|
|
103
|
+
with sql_job.query("select * from sample.employee") as query:
|
|
104
|
+
result = query.run(rows_to_fetch=1)
|
|
105
|
+
print(result)
|
|
81
106
|
```
|
|
82
107
|
|
|
83
108
|
Here is the output from the script above:
|
|
@@ -200,6 +225,156 @@ Here is the output from the script above:
|
|
|
200
225
|
}
|
|
201
226
|
|
|
202
227
|
```
|
|
228
|
+
The results object is a JSON object that contains the metadata and data from the query. Here are the different fields returned:
|
|
229
|
+
- `id` field contains the query ID
|
|
230
|
+
- `has_results` field indicates whether the query returned any results
|
|
231
|
+
- `update_count` field indicates the number of rows updated by the query (-1 if the query did not update any rows)
|
|
232
|
+
- `metadata` field contains information about the columns returned by the query
|
|
233
|
+
- `data` field contains the results of the query
|
|
234
|
+
- `is_done` field indicates whether the query has finished executing
|
|
235
|
+
- `success` field indicates whether the query was successful.
|
|
236
|
+
|
|
237
|
+
In the ouput above, the query was successful and returned one row of data.
|
|
238
|
+
|
|
239
|
+
### Query and run
|
|
240
|
+
|
|
241
|
+
To create and run a query in a single step, use the `query_and_run` method:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
import configparser
|
|
245
|
+
from mapepire_python.client.sql_job import SQLJob
|
|
246
|
+
from mapepire_python.types import DaemonServer
|
|
247
|
+
|
|
248
|
+
config = configparser.ConfigParser()
|
|
249
|
+
config.read('mapepire.ini')
|
|
250
|
+
|
|
251
|
+
creds = DaemonServer(
|
|
252
|
+
host=config['mapepire']['SERVER'],
|
|
253
|
+
port=config['mapepire']['PORT'],
|
|
254
|
+
user=config['mapepire']['USER'],
|
|
255
|
+
password=config['mapepire']['PASSWORD'],
|
|
256
|
+
ignoreUnauthorized=True
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
with SQLJob(creds) as sql_job:
|
|
260
|
+
# query automatically closed after running
|
|
261
|
+
results = sql_job.query_and_run("select * from sample.employee", rows_to_fetch=1)
|
|
262
|
+
print(result)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Asynchronous Query Execution
|
|
266
|
+
|
|
267
|
+
The `PoolJob` object can be used to create and run queries asynchronously:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
import asyncio
|
|
271
|
+
import configparser
|
|
272
|
+
from mapepire_python.pool.pool_job import PoolJob
|
|
273
|
+
from mapepire_python.types import DaemonServer
|
|
274
|
+
|
|
275
|
+
config = configparser.ConfigParser()
|
|
276
|
+
config.read('mapepire.ini')
|
|
277
|
+
|
|
278
|
+
creds = DaemonServer(
|
|
279
|
+
host=config['mapepire']['SERVER'],
|
|
280
|
+
port=config['mapepire']['PORT'],
|
|
281
|
+
user=config['mapepire']['USER'],
|
|
282
|
+
password=config['mapepire']['PASSWORD'],
|
|
283
|
+
ignoreUnauthorized=True
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
async def main():
|
|
287
|
+
async with PoolJob(creds=creds) as pool_job:
|
|
288
|
+
async with pool_job.query('select * from sample.employee') as query:
|
|
289
|
+
res = await query.run(rows_to_fetch=1)
|
|
290
|
+
|
|
291
|
+
if __name__ == '__main__':
|
|
292
|
+
asyncio.run(main())
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
To run a create and run a query asynchronously in a single step, use the `query_and_run` method:
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
import asyncio
|
|
300
|
+
import configparser
|
|
301
|
+
from mapepire_python.pool.pool_job import PoolJob
|
|
302
|
+
from mapepire_python.types import DaemonServer
|
|
303
|
+
|
|
304
|
+
config = configparser.ConfigParser()
|
|
305
|
+
config.read('mapepire.ini')
|
|
306
|
+
|
|
307
|
+
creds = DaemonServer(
|
|
308
|
+
host=config['mapepire']['SERVER'],
|
|
309
|
+
port=config['mapepire']['PORT'],
|
|
310
|
+
user=config['mapepire']['USER'],
|
|
311
|
+
password=config['mapepire']['PASSWORD'],
|
|
312
|
+
ignoreUnauthorized=True
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
async def main():
|
|
316
|
+
async with PoolJob(creds=creds) as pool_job:
|
|
317
|
+
res = await pool_job.query_and_run(rows_to_fetch=1)
|
|
318
|
+
print(res)
|
|
319
|
+
|
|
320
|
+
if __name__ == '__main__':
|
|
321
|
+
asyncio.run(main())
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
## Pooling (beta)
|
|
327
|
+
|
|
328
|
+
The `Pool` object can be used to create a pool of `PoolJob` objects to run queries concurrently.
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
import asyncio
|
|
332
|
+
import configparser
|
|
333
|
+
from mapepire_python.pool.pool_client import Pool, PoolOptions
|
|
334
|
+
from mapepire_python.types import DaemonServer
|
|
335
|
+
|
|
336
|
+
config = configparser.ConfigParser()
|
|
337
|
+
config.read('mapepire.ini')
|
|
338
|
+
|
|
339
|
+
creds = DaemonServer(
|
|
340
|
+
host=config['mapepire']['SERVER'],
|
|
341
|
+
port=config['mapepire']['PORT'],
|
|
342
|
+
user=config['mapepire']['USER'],
|
|
343
|
+
password=config['mapepire']['PASSWORD'],
|
|
344
|
+
ignoreUnauthorized=True
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
async def main():
|
|
349
|
+
async with Pool(
|
|
350
|
+
options=PoolOptions(
|
|
351
|
+
creds=creds,
|
|
352
|
+
opts=None,
|
|
353
|
+
max_size=5,
|
|
354
|
+
starting_size=3
|
|
355
|
+
)
|
|
356
|
+
) as pool:
|
|
357
|
+
job_names = []
|
|
358
|
+
resultsA = await asyncio.gather(
|
|
359
|
+
pool.execute('values (job_name)'),
|
|
360
|
+
pool.execute('values (job_name)'),
|
|
361
|
+
pool.execute('values (job_name)')
|
|
362
|
+
)
|
|
363
|
+
job_names = [res['data'][0]['00001'] for res in resultsA]
|
|
364
|
+
|
|
365
|
+
print(job_names)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
if __name__ == '__main__':
|
|
369
|
+
asyncio.run(main())
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
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.
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
['004460/QUSER/QZDASOINIT', '005096/QUSER/QZDASOINIT', '005319/QUSER/QZDASOINIT']
|
|
376
|
+
```
|
|
377
|
+
|
|
203
378
|
|
|
204
379
|
# Development Setup
|
|
205
380
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional, Union
|
|
2
|
+
|
|
3
|
+
from .data_types import DaemonServer, JobStatus, QueryOptions
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseJob:
|
|
7
|
+
def __init__(self, creds: DaemonServer = None, options: Dict[Any, Any] = {}) -> None:
|
|
8
|
+
self.creds = creds
|
|
9
|
+
self.options = options
|
|
10
|
+
|
|
11
|
+
def connect(self, db2_server: Union[DaemonServer, Dict[str, Any]]) -> Dict[str, Any]:
|
|
12
|
+
raise NotImplementedError()
|
|
13
|
+
|
|
14
|
+
def close(self) -> None:
|
|
15
|
+
raise NotImplementedError()
|
|
16
|
+
|
|
17
|
+
def get_status(self) -> JobStatus:
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
def query(
|
|
21
|
+
self,
|
|
22
|
+
sql: str,
|
|
23
|
+
opts: Optional[Union[Dict[str, Any], QueryOptions]] = None,
|
|
24
|
+
):
|
|
25
|
+
raise NotImplementedError()
|
|
26
|
+
|
|
27
|
+
def query_and_run(
|
|
28
|
+
self, sql: str, opts: Optional[Dict[str, Any]] = None, **kwargs
|
|
29
|
+
) -> Dict[str, Any]:
|
|
30
|
+
raise NotImplementedError()
|
|
31
|
+
|
|
32
|
+
def __enter__(self):
|
|
33
|
+
raise NotImplementedError()
|
|
34
|
+
|
|
35
|
+
def __exit__(self, *args, **kwargs):
|
|
36
|
+
raise NotImplementedError()
|
|
37
|
+
|
|
38
|
+
async def __aenter__(self):
|
|
39
|
+
raise NotImplementedError()
|
|
40
|
+
|
|
41
|
+
async def __aexit__(self, *args, **kwargs):
|
|
42
|
+
raise NotImplementedError()
|