Escrito por

Sales Engineer at InterSystems Corporation
Artículo Alberto Fuentes · 22 mayo 7m read

Manipulando los globals de InterSystems IRIS al estilo Python con iris-global-reference

Los globals de InterSystems IRIS son uno de los puntos fuertes principales de la plataforma: almacenan datos jerárquicos en una estructura directa, ordenada y eficiente. Pero cuando trabajáis desde Python, manipular globals a veces puede parecer más una API de bajo nivel que algo alineado con las formas naturales de trabajar del lenguaje.

El proyecto iris-global-reference proporciona una capa de Python sobre los globals de InterSystems IRIS. Su objetivo es sencillo: hacer que el acceso a los globals sea más legible, más idiomático y más fácil de integrar en código Python moderno, sin ocultar el modelo jerárquico subyacente.

¿Por qué este proyecto?

En ObjectScript, los globals son algo natural

set ^demo("players",1)="Babe Ruth"
set ^demo("players",2)="Cy Young"

En Python, a menudo queremos escribir algo más cercano a un diccionario:

team["players", "1"] = "Babe Ruth"
print(team["players"]["1"])

Esto es exactamente lo que proporciona iris-global-reference con la clase GlobalReference.

El proyecto está orientado a varios casos de uso:

  • Manipular los globals de InterSystems IRIS con una sintaxis más “Pythonica”;
  • Recorrer fácilmente un árbol de globals;
  • Convertir globals a diccionarios de Python o JSON;
  • Importar diccionarios o JSON a InterSystems IRIS;
  • Usar la misma API tanto en Embedded Python como desde una conexión remota;
  • Envolver operaciones comunes como set, get, kill, $ORDER, $QUERY, y transacciones.

Instalación

El paquete está disponible a través de pip

pip install iris-global-reference

Para usarlo directamente desde un terminal de Python en InterSystems IRIS, también podéis instalarlo en el directorio de Python de la instancia:

pip install iris-global-reference --target=<mgr_dir>/python

Primer ejemplo

Aquí tenéis un ejemplo mínimo usando un globa ^demo

from iris_global import GlobalReference

team = GlobalReference("^demo")
team.kill()

team.set((), "Baseball")
team["name"] = "Boston Red Sox"
team.set(("players", "1"), "Babe Ruth")
team.set(["players", "2"], "Cy Young")
team["players", "3"] = "Ted Williams"

print(team.get(()))
print(team["name"])
print(team["players"]["1"])

La misma referencia acepta múltiples formas de subscripción: cadena, lista o tupla. Esto os permite usar la API en el estilo que mejor se adapte al código que estáis llamando.

Una API cercana a los diccionarios de Python

GlobalReference expone métodos explícitos:

team.set(("players", "1"), "Babe Ruth")
print(team.get(("players", "1")))
team.kill(("players", "1"))

Pero también admite operaciones habituales de Python:

team["players", "1"] = "Babe Ruth"

if ("players", "1") in team:
    print(team["players"]["1"])

del team["players", "1"]

Esta sintaxis es útil para escribir código de aplicación más natural, manteniéndose alineado con el modelo de datos de globals.

Recorriendo un Global

El proyecto proporciona varios métodos de iteración:

for key in team.keys():
    print(key)

for value in team.values():
    print(value)

for key, value in team.items():
    print(key, value)

También podéis controlar el recorrido:

for subscript in team.subscripts(("players",), children_only=True):
    print(subscript, team.get(subscript))

Para quienes desarrolláis en ObjectScript, order() y query() proporcionan un comportamiento similar a $ORDER y $QUERY:

print(team.order(("players", "")))
print(team.query(("players",)))

Exportar un Global a un diccionario o JSON

Uno de los beneficios del proyecto es facilitar los viajes de ida y vuelta entre los globals de InterSystems IRIS y las estructuras de Python.

data = team.to_dict()
print(data)

Resultado de ejemplo:

{
    None: "Baseball",
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young",
        "3": "Ted Williams"
    }
}

La clave None representa el valor del nodo actual. Esto es necesario porque un nodo de un global de InterSystems IRIS puede tener tanto un valor como descendientes, mientras que un diccionario de Python normalmente representa o bien un valor o bien un diccionario anidado.

El mismo contenido puede exportarse a JSON:

json_data = team.to_json()
print(json_data)

En JSON, el valor del nodo actual se representa por defecto con la clave _:

{
    "_": "Baseball",
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young",
        "3": "Ted Williams"
    }
}

La importación funciona en sentido inverso:

team.from_dict({
    None: "Baseball",
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young"
    }
})

Y para JSON:

team.from_json("""
{
    "_": "Baseball",
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young"
    }
}
""")

Embedded Python o conexión remota

La API puede usarse desde Embedded Python sin necesidad de proporcionar una conexión:

from iris_global import GlobalReference

team = GlobalReference("^demo")
team["name"] = "Boston Red Sox"

También puede usarse desde un programa externo de Python con una conexión nativa a InterSystems IRIS:

import iris
from iris_global import GlobalReference

conn = iris.connect("localhost", 1972, "USER", "SuperUser", "SYS")
team = GlobalReference("^demo", connection=conn)

team["name"] = "Boston Red Sox"
print(team["name"])

El proyecto está orientado a la conexión nativa proporcionada por iris.connect(...).

Transacciones

Las transacciones se exponen mediante un gestor de contexto de Python:

from iris_global import GlobalReference

team = GlobalReference("^demo")

with team.transaction():
    team["name"] = "Boston Red Sox"
    team["players", "1"] = "Babe Ruth"

Si el bloque se completa correctamente, la transacción se confirma. Si se produce una excepción, se revierte.

La clase también puede usarse directamente con with:

with GlobalReference("^demo") as team:
    team["name"] = "Boston Red Sox"

Soporte experimental de arrays

Los globals de InterSystems IRIS no tienen un concepto nativo de array en el sentido de JSON o Python. Para permitir la importación y exportación de listas, el proyecto utiliza una convención de serialización.

Por ejemplo:

gref = GlobalReference("^demo")
gref.from_dict({
    "name": "example",
    "numbers": [1, 2, 3]
})

print(gref.to_dict())

El array se almacena en el global con un prefijo interno, __array__ por defecto, y luego se reconstruye como una lista de Python durante la exportación. Esta parte sigue siendo experimental, pero ya hace más cómodos los intercambios con estructuras tipo JSON.

Like Mostrando contenido como ZWRITE 

Para depuración o para comprobar rápidamente la estructura almacenada, el método zw() devuelve una salida similar a ZWRITE:

print(team.zw())

Ejemplo:

^demo="Baseball"
^demo("name")="Boston Red Sox"
^demo("players","1")="Babe Ruth"
^demo("players","2")="Cy Young"

Esto es útil cuando queréis comparar el resultado en Python con lo que escribiríais o inspeccionaríais desde ObjectScript.

Comparación rápida con APIs nativas

El objetivo del proyecto no es reemplazar las APIs nativas de InterSystems IRIS, sino añadir una capa de conveniencia para los casos en los que estáis escribiendo principalmente en Python.

Por ejemplo, con iris-global-reference:

global_reference.set(("name", 1), "Boston Red Sox")
value = global_reference.get(("name", 1))

Con una API nativa, el orden de los argumentos y el manejo de subscripts puede ser diferente. Esta biblioteca estandariza el uso en torno a una convención de Python: primero la ruta del nodo y luego el valor.

¿Cuándo deberíais usar este proyecto?

iris-global-reference es especialmente útil si:

  • desarrolláis con Embedded Python y InterSystems IRIS;
  • escribís scripts de Python que necesitan leer o poblar globals;
  • queréis exponer datos de globals como JSON;
  • queréis manipular estructuras jerárquicas de InterSystems IRIS con diccionarios de Python;
  • queréis hacer prototipos rápidamente sin escribir mucho ObjectScript;
  • necesitáis una API más legible para operaciones comunes con globals.

Hoja de ruta

La hoja de ruta actual incluye:

  • soporte más avanzado para arrays;
  • soporte más completo para datos binarios;
  • tipos de InterSystems IRIS como listbuild, vector, PVA o bit;
  • soporte para variables multidimensionales.

Probando el proyecto

El repositorio contiene una suite de pruebas. Para ejecutarla:

python -m pytest

Conclusión

iris-global-reference es una biblioteca pequeña, pero resuelve un problema concreto: hacer que los globals de InterSystems IRIS sean más agradables de manipular desde Python.

Preserva las operaciones fundamentales del modelo de InterSystems IRIS al mismo tiempo que añade una experiencia cercana a los diccionarios de Python, conversiones prácticas a JSON, iteradores y uso tanto desde Embedded Python como desde conexiones remotas.

Para quienes desarrolláis en la intersección de InterSystems IRIS y Python, es una herramienta sencilla de probar y fácil de integrar en scripts, prototipos o aplicaciones más estructuradas.

Links: