文章 Kelly Huang · 六月 8 8m read

使用 iris-embedded-python-wrapper统一嵌入式 Python 与原生 API

在基于 InterSystems IRIS 开发 Python 应用时,你很快就会发现存在多种执行上下文:

  • 由 IRIS 直接启动的 Embedded Python;
  • 加载本地 IRIS 安装中 Embedded Python 库的常规 python3进程;
  • 通过官方原生驱动连接 IRIS 的外部 Python 应用。

这三种场景都非常有用,但在导入机制、系统配置、对象 API 以及 SQL 访问方面,它们的行为并不完全一致。iris-embedded-python-wrapper项目提供了一个稳定的 Python 门面(Facade),旨在减少这些差异,并提供一个统一的入口点:import iris。

 

存在的问题

在一个围绕 IRIS 构建的 Python 项目中,同一份代码可能需要在多种环境中运行:

  • 在 IRIS 终端中,通过 iris python iris或 iris session iris后输入 :py;
  • 通过 python3启动的本地 Python 脚本;
  • 连接到 IRIS 实例的远程 Python 服务。

如果没有抽象层,许多细节往往就需要分别处理:

  • Embedded Python 的 iris模块只有在 IRIS 运行时被正确加载时才可用;
  • 原生 SDK​ 同样暴露了一个 iris包,这可能导致冲突或产生歧义的导入;
  • iris.cls(...)和 DB-API 连接在嵌入式模式和远程模式下的执行路径并不完全一致;
  • 在 Linux​ 和 macOS​ 上,必须在 Python 进程启动前预先配置好动态库路径;
  • 边界值(如 SQL NULL 和空字符串)在不同的后端中可能有不同的表示方式。

该包装器(Wrapper)的目标在于,让应用程序能够清晰地声明其执行上下文,同时在嵌入式模式和原生模式下保持业务逻辑代码的相似性。

 

包装器提供的能力

该包提供了一个 import iris门面(Facade),保留了最常用的入口点:

  • iris.cls(...):用于访问 IRIS 类;
  • iris.connect(...):根据上下文配置或打开连接;
  • iris.dbapi:用于符合 PEP 249​ 规范的 SQL 访问;
  • iris.runtime:用于检查并显式控制当前的活动模式。

支持的模式如下:

 

模式 (Mode) 描述 (Description) 示例 (Example)
embedded-kernel​ Python 由 IRIS 启动 iris python iris或 :py
embedded-local​ python3从本地 IRIS 安装加载 Embedded Python 库 IRISINSTALLDIR, iris.connect(path=...)
native-remote​ Python 连接到远程 IRIS 实例 官方原生驱动 intersystems-irispython
unavailable​ 尚无可用的 IRIS 后端 使用前需要配置

安装

该包可以使用 pip进行安装:

pip install iris-embedded-python-wrapper

该项目依赖于官方驱动:

intersystems-irispython>=5.0.0

对于嵌入式模式,你还需要本地安装 InterSystems IRIS。若要从外部 Python 进程访问嵌入式模式,必须启用 %Service_CallIn服务。

 

示例:使用 Embedded Python

在由 IRIS 启动的 Embedded Python Shell 中:

iris python iris

或者在 IRIS 会话中:

USER>:py

你可以像往常一样导入 iris:

import iris

print(iris.system.Version.GetVersion())
print(iris.runtime.state)

在此上下文中,iris.runtime.state应报告:

'embedded-kernel'

如果你想强制使用本地项目目录中的包装器:

PYTHONPATH=/path/to/iris-embedded-python-wrapper iris python iris

示例:从本地 python3加载 IRIS

embedded-local模式允许你在运行常规 Python 脚本的同时,使用来自 IRIS 安装的 Embedded Python 库。

 

在 Linux​ 上:

export IRISINSTALLDIR=/opt/iris
export LD_LIBRARY_PATH=$IRISINSTALLDIR/bin:$LD_LIBRARY_PATH
python3 my_script.py

在 macOS​ 上:

export IRISINSTALLDIR=/opt/iris
export DYLD_LIBRARY_PATH=$IRISINSTALLDIR/bin:$DYLD_LIBRARY_PATH
python3 my_script.py

该包装器还允许你显式声明 IRIS 安装路径:

import iris

iris.connect(path="/opt/iris")

obj = iris.cls("Ens.StringRequest")._New()
obj.StringValue = "hello from embedded-local"

重要提示:在 Unix 系统上,iris.connect(path=...)可以在运行时配置 Python 路径,但如果 Python 进程在没有正确设置 LD_LIBRARY_PATH或 DYLD_LIBRARY_PATH的情况下已经启动,它无法追溯性地修复动态库解析问题。

 

 

以下是该部分文档的严格翻译,已按原文结构整理为 Markdown 格式:


示例:在远程原生模式下保留 iris.cls(...)

使用原生 SDK 时,远程代码通常直接使用显式的 IRIS 句柄:

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")

使用该包装器,你可以绑定一次原生连接,并保留与 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

原生代理(Native Proxy)会将首位的下划线 _转换为 %,这使得你可以在 Python 中编写 _New()来调用 %New。

 

显式控制运行时

iris.runtime是该包装器关于当前活动上下文的“单一事实来源(Source of Truth)”。

import iris

ctx = iris.runtime.get()

print(ctx.mode)
print(ctx.state)
print(ctx.embedded_available)

有用的属性包括:

  • iris.runtime.mode:选定的策略,例如 auto、embedded或 native;
  • iris.runtime.state:检测到的状态,例如 embedded-kernel、embedded-local、native-remote或 unavailable;
  • iris.runtime.embedded_available:嵌入式后端是否可用;
  • iris.runtime.iris:绑定到运行时的原生 IRIS 句柄(如果可用);
  • iris.runtime.dbapi:显式绑定的 DB-API 连接(如果可用)。

你也可以强制指定模式:

import iris

iris.runtime.configure(mode="embedded")

或者重置为自动检测:

import iris

iris.runtime.reset()

使用 iris.dbapi进行 SQL 访问

该包装器暴露了一个兼容常见用法的 DB-API 门面:

  • connect();
  • cursor();
  • execute();
  • fetchone()、fetchmany()、fetchall();
  • commit()、rollback()、close();
  • PEP 249 异常,如 InterfaceError、OperationalError等。

在嵌入式模式下:

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()

在带有显式路径的嵌入式本地(embedded-local)模式下:

import iris

conn = iris.dbapi.connect(path="/opt/iris", namespace="USER")
cur = conn.cursor()
cur.execute("SELECT 1")
print(cur.fetchone())

在远程原生模式下:

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())

在 auto(自动)模式下,包装器会根据提供的参数和 iris.runtime的状态来选择后端。例如,如果你提供了主机名(hostname)、端口(port)、命名空间(namespace)、用户名(username)和密码(password),连接将被路由到原生驱动。

该包装器还对某些嵌入式场景进行了规范化处理,使其行为更接近原生 DB-API:

  • SQL NULL 变为 Python None;
  • 空的 SQL 字符串变为 "";
  • 作为参数传入的 None保持为 SQL NULL;
  • 作为参数传入的 ""保持为空 SQL 字符串。

将虚拟环境绑定到 Embedded Python

该项目提供了两个实用的命令:

bind_iris
unbind_iris

bind_iris会查找当前虚拟环境的 Python 库,更新 IRIS Embedded Python 配置,并创建 iris.cpf文件的备份。

示例:

python3 -m venv .venv
. .venv/bin/activate
pip install iris-embedded-python-wrapper
bind_iris

根据平台和 IRIS 配置的不同,可能需要 IRIS 管理员权限。在 Windows 上,更改配置后可能需要重启 IRIS 实例。

 

何时应该使用此包装器?

如果你希望实现以下目标,该包装器将非常有用:

  • 编写既能在嵌入式模式又能在远程模式下运行的 Python IRIS 代码;
  • 减少 Embedded Python 与原生 API 之间的差异;
  • 通过 iris.runtime使执行模式变得可观测;
  • 在应用代码中使用单一的 DB-API 门面;
  • 简化 Embedded Python 的虚拟环境配置;
  • 更轻松地测试嵌入式和远程路径。

它不是 InterSystems IRIS 或官方原生 SDK 的替代品。它是一个构建在这些组件之上的便利层,旨在提高应用程序代码的可移植性。

资源

结语

iris-embedded-python-wrapper围绕 InterSystems IRIS 的主要 Python 执行模式提供了一个简单的门面。其主要优势在于能够编写如下代码:

 

import iris

然后明确地决定代码是在 Embedded Python、嵌入式本地模式(embedded-local)还是通过远程原生连接中运行。对于需要在不同模式间切换的应用程序,或者希望在本地方便脚本、嵌入式作业和外部 Python 服务之间共享更多代码的团队而言,这种方法消除了大量的技术干扰。