Unifier Embedded Python et Native API avec `iris-embedded-python-wrapper`

Quand on développe en Python avec InterSystems IRIS, on peut rapidement se retrouver avec plusieurs contextes d'exécution :
- Python lancé directement par IRIS avec Embedded Python ;
- un
python3classique qui charge les bibliothèques Embedded Python d'une installation IRIS locale ; - une application Python externe qui se connecte à IRIS via le pilote natif officiel.
Ces trois cas sont utiles, mais ils n'ont pas exactement le même comportement côté imports, configuration système, API objet et accès SQL. Le projet iris-embedded-python-wrapper propose une façade Python stable pour réduire ces différences et garder un même point d'entrée : import iris.
Le problème
Dans un projet Python autour d'IRIS, le même code peut devoir tourner dans plusieurs environnements :
- dans un terminal IRIS avec
iris python irisouiris session irispuis:py; - dans un script Python local lancé avec
python3; - dans un service Python distant qui se connecte à une instance IRIS.
Sans couche d'abstraction, il faut souvent traiter séparément plusieurs détails :
- le module
irisd'Embedded Python n'est disponible que lorsque le runtime IRIS est correctement chargé ; - le SDK natif expose aussi un package
iris, ce qui peut créer des collisions ou des imports ambigus ; iris.cls(...)et la connexion DB-API ne suivent pas exactement les mêmes chemins entre embedded et remote ;- sous Linux et macOS, les chemins de bibliothèques dynamiques doivent être préparés avant le démarrage du processus Python ;
- certaines valeurs frontières, par exemple SQL
NULLet chaîne vide, peuvent être représentées différemment selon le backend.
L'objectif de ce wrapper est de permettre à l'application de déclarer clairement son contexte d'exécution, tout en conservant un code métier proche entre les modes embedded et natif.
Ce que fournit le wrapper
Le package fournit une façade import iris qui garde disponibles les points d'entrée les plus utilisés :
iris.cls(...)pour accéder aux classes IRIS ;iris.connect(...)pour configurer ou ouvrir une connexion selon le contexte ;iris.dbapipour un accès SQL de type PEP 249 ;iris.runtimepour inspecter et piloter explicitement le mode actif.
Les modes pris en charge sont :
| Mode | Description | Exemple |
|---|---|---|
embedded-kernel |
Python est lancé par IRIS | iris python iris ou :py |
embedded-local |
python3 charge les bibliothèques Embedded Python d'une installation IRIS locale |
IRISINSTALLDIR, iris.connect(path=...) |
native-remote |
Python se connecte à une instance IRIS distante | pilote natif officiel intersystems-irispython |
unavailable |
aucun backend IRIS n'est encore disponible | configuration requise avant usage |
Installation
Le package est installable avec pip :
pip install iris-embedded-python-wrapper
Le projet dépend du pilote officiel :
intersystems-irispython>=5.0.0
Pour le mode embedded, il faut aussi une installation InterSystems IRIS disponible localement. Pour un accès embedded depuis un processus Python externe, le service %Service_CallIn doit être activé.
Exemple : utiliser Embedded Python
Dans un shell Embedded Python lancé par IRIS :
iris python iris
ou depuis une session IRIS :
USER>:py
on peut importer iris comme d'habitude :
import iris
print(iris.system.Version.GetVersion())
print(iris.runtime.state)
Dans ce contexte, iris.runtime.state doit indiquer :
'embedded-kernel'
Si vous voulez forcer l'utilisation du wrapper depuis un répertoire local du projet :
PYTHONPATH=/path/to/iris-embedded-python-wrapper iris python iris
Exemple : charger IRIS depuis un python3 local
Le mode embedded-local permet de lancer un script Python classique tout en utilisant les bibliothèques Embedded Python d'une installation IRIS.
Sous Linux :
export IRISINSTALLDIR=/opt/iris
export LD_LIBRARY_PATH=$IRISINSTALLDIR/bin:$LD_LIBRARY_PATH
python3 my_script.py
Sous macOS :
export IRISINSTALLDIR=/opt/iris
export DYLD_LIBRARY_PATH=$IRISINSTALLDIR/bin:$DYLD_LIBRARY_PATH
python3 my_script.py
Le wrapper permet aussi de déclarer explicitement le chemin de l'installation IRIS :
import iris
iris.connect(path="/opt/iris")
obj = iris.cls("Ens.StringRequest")._New()
obj.StringValue = "hello from embedded-local"
Point important : sous Unix, iris.connect(path=...) peut configurer les chemins Python au runtime, mais il ne peut pas corriger rétroactivement la résolution des bibliothèques dynamiques si le processus Python a déjà démarré sans LD_LIBRARY_PATH ou DYLD_LIBRARY_PATH correct.
Exemple : garder iris.cls(...) en mode natif distant
Avec le SDK natif, le code distant utilise normalement un handle IRIS explicite :
import iris
conn = iris.connect("localhost", 1972, "USER", "SuperUser", "<password>")
db = iris.createIRIS(conn)
req = db.classMethodValue("Ens.StringRequest", "%New")
db.set(req, "StringValue", "hello")
value = db.get(req, "StringValue")
Avec le wrapper, on peut lier la connexion native une fois, puis garder une syntaxe proche d'Embedded Python :
import iris
conn = iris.connect("localhost", 1972, "USER", "SuperUser", "<password>")
iris.runtime.configure(native_connection=conn)
req = iris.cls("Ens.StringRequest")._New()
req.StringValue = "hello"
value = req.StringValue
Le proxy natif convertit le premier _ en %, ce qui permet d'écrire _New() en Python pour appeler %New.
Piloter le runtime explicitement
iris.runtime est la source de vérité du wrapper pour savoir ce qui est actif.
import iris
ctx = iris.runtime.get()
print(ctx.mode)
print(ctx.state)
print(ctx.embedded_available)
Quelques propriétés utiles :
iris.runtime.mode: politique sélectionnée, par exempleauto,embeddedounative;iris.runtime.state: état détecté, par exempleembedded-kernel,embedded-local,native-remoteouunavailable;iris.runtime.embedded_available: indique si le backend embedded est utilisable ;iris.runtime.iris: handle IRIS natif lié au runtime, si disponible ;iris.runtime.dbapi: connexion DB-API liée explicitement, si disponible.
On peut aussi forcer un mode :
import iris
iris.runtime.configure(mode="embedded")
ou revenir à la détection automatique :
iris.runtime.reset()
Accès SQL avec iris.dbapi
Le wrapper expose une façade DB-API compatible avec les usages courants :
connect();cursor();execute();fetchone(),fetchmany(),fetchall();commit(),rollback(),close();- exceptions PEP 249 comme
InterfaceError,OperationalError, etc.
En mode embedded :
import iris
conn = iris.dbapi.connect(mode="embedded")
cur = conn.cursor()
cur.execute("SELECT Name FROM Sample.Person")
rows = cur.fetchall()
cur.close()
conn.close()
En mode embedded-local avec chemin explicite :
import iris
conn = iris.dbapi.connect(path="/opt/iris", namespace="USER")
cur = conn.cursor()
cur.execute("SELECT 1")
print(cur.fetchone())
En mode natif distant :
import iris
conn = iris.dbapi.connect(
mode="native",
hostname="localhost",
port=1972,
namespace="USER",
username="SuperUser",
password="<password>",
)
cur = conn.cursor()
cur.execute("SELECT 1")
print(cur.fetchone())
En mode auto, le wrapper choisit le backend selon les arguments fournis et l'état de iris.runtime. Par exemple, si vous fournissez hostname, port, namespace, username et password, la connexion est routée vers le pilote natif.
Le wrapper normalise aussi certains cas embedded pour rapprocher le comportement de la DB-API native :
- SQL
NULLdevientNoneen Python ; - une chaîne SQL vide devient
""; Noneenvoyé comme paramètre reste SQLNULL;""envoyé comme paramètre reste une chaîne vide SQL.
Lier un environnement virtuel à Embedded Python
Le projet fournit deux commandes pratiques :
bind_iris
unbind_iris
bind_iris recherche la bibliothèque Python du virtualenv courant, met à jour la configuration Embedded Python d'IRIS et crée une sauvegarde du fichier iris.cpf.
Exemple :
python3 -m venv .venv
. .venv/bin/activate
pip install iris-embedded-python-wrapper
bind_iris
Selon la plateforme et la configuration IRIS, des droits administrateur IRIS peuvent être nécessaires. Sous Windows, un redémarrage de l'instance IRIS peut être requis après modification de la configuration.
Quand utiliser ce wrapper ?
Ce wrapper est utile si vous voulez :
- écrire du code Python IRIS qui peut tourner en embedded ou en distant ;
- réduire les différences entre Embedded Python et Native API ;
- rendre le mode d'exécution observable avec
iris.runtime; - utiliser une DB-API unique côté application ;
- simplifier la configuration d'un virtualenv Python pour Embedded Python ;
- tester plus facilement les chemins embedded et remote.
Ce n'est pas un remplacement d'InterSystems IRIS ni du SDK natif officiel. C'est une couche de confort qui s'appuie sur ces composants pour rendre le code applicatif plus portable.
Ressources
- Projet GitHub : https://github.com/grongierisc/iris-embedded-python-wrapper
- Documentation du projet : https://github.com/grongierisc/iris-embedded-python-wrapper/blob/master/README.md
- Installation :
pip install iris-embedded-python-wrapper - Licence : MIT
Conclusion
iris-embedded-python-wrapper apporte une façade simple autour des principaux modes Python d'InterSystems IRIS. Le bénéfice principal est de pouvoir écrire :
import iris
puis de décider explicitement si le code s'exécute en Embedded Python, en embedded-local ou via une connexion native distante. Pour les applications qui doivent passer d'un mode à l'autre, ou pour les équipes qui veulent partager plus de code entre scripts locaux, jobs embedded et services Python externes, cette approche réduit beaucoup de bruit technique.