Escrito por

Software Engineer at Zarmik
Artigo Heloisa Paiva · 1 h atrás 7m read

Manipulando Globais do InterSystems IRIS ao Estilo Python com iris-global-reference

Os globals do InterSystems IRIS são um dos principais pontos fortes da plataforma: eles armazenam dados hierárquicos em uma estrutura direta, ordenada e eficiente. Mas ao trabalhar com Python, manipular globals às vezes pode parecer mais próximo de uma API de baixo nível do que dos hábitos naturais da linguagem.

O projeto iris-global-reference fornece uma camada Python sobre os globals do IRIS. Seu objetivo é simples: tornar o acesso aos globals mais legível, mais idiomático e mais fácil de integrar em código Python moderno, sem esconder o modelo hierárquico subjacente.

Tags sugeridas: InterSystems IRIS, Python, Embedded Python, Globals, JSON

Por que este projeto?

Em ObjectScript, globals são naturais:

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

Em Python, frequentemente queremos escrever algo mais próximo de um dicionário:

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

É exatamente isso que o iris-global-reference fornece com a classe GlobalReference.

O projeto atende a diversos casos de uso:

  • manipular globals do IRIS com uma sintaxe mais Pythonica;
  • percorrer facilmente uma árvore de globals;
  • converter globals em dicionários Python ou JSON;
  • importar dicionários ou JSON para o IRIS;
  • usar a mesma API em Embedded Python ou a partir de uma conexão remota;
  • encapsular operações comuns como set, get, kill, $ORDER, $QUERY e transações.

Instalação

O pacote está disponível via pip:

pip install iris-global-reference

Para usá-lo diretamente em um terminal Python do IRIS, você também pode instalá-lo no diretório Python da instância:

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

Primeiro exemplo

Aqui está um exemplo mínimo usando um global ^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"])

A mesma referência aceita múltiplos formatos de subscritos: string, lista ou tupla. Isso permite usar a API no estilo que melhor se adapta ao código chamador.

Uma API próxima de dicionários Python

GlobalReference expõe métodos explícitos:

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

Mas também suporta operações familiares do Python:

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

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

del team["players", "1"]

Essa sintaxe é útil para escrever código de aplicação mais natural enquanto permanece alinhada ao modelo de dados dos globals.

Percorrendo um global

O projeto fornece diversos métodos de iteração:

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

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

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

Você também pode controlar o percurso:

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

Para desenvolvedores familiarizados com ObjectScript, order() e query() fornecem um comportamento próximo de $ORDER e $QUERY:

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

Exportando um global para um dicionário ou JSON

Um dos benefícios do projeto é facilitar a conversão entre globals do IRIS e estruturas Python.

data = team.to_dict()
print(data)

Resultado de exemplo:

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

A chave None representa o valor do nó atual. Isso é necessário porque um nó de global do IRIS pode possuir tanto um valor quanto descendentes, enquanto um dicionário Python geralmente representa ou um valor ou um dicionário aninhado.

O mesmo conteúdo pode ser exportado para JSON:

json_data = team.to_json()
print(json_data)

No JSON, o valor do nó atual é representado por padrão pela chave _:

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

A importação funciona no sentido inverso:

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

E para JSON:

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

Embedded Python ou conexão remota

A API pode ser usada a partir de Embedded Python sem fornecer uma conexão:

from iris_global import GlobalReference

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

Ela também pode ser usada a partir de um programa Python externo com uma conexão nativa 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"])

O projeto utiliza a conexão nativa fornecida por iris.connect(...).

Transações

As transações são expostas por meio de um context manager Python:

from iris_global import GlobalReference

team = GlobalReference("^demo")

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

Se o bloco for concluído com sucesso, a transação é confirmada. Se uma exceção for lançada, ela será revertida.

A classe também pode ser usada diretamente com with:

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

Suporte experimental a arrays

Globals do IRIS não possuem um conceito nativo de array no sentido de JSON ou Python. Para suportar importação e exportação de listas, o projeto utiliza uma convenção de serialização.

Por exemplo:

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

print(gref.to_dict())

O array é armazenado no global com um prefixo interno, __array__ por padrão, e então reconstruído como uma lista Python durante a exportação. Essa parte ainda é experimental, mas já torna trocas com estruturas similares a JSON mais convenientes.

Exibindo conteúdo como ZWRITE

Para depuração ou para verificar rapidamente a estrutura armazenada, o método zw() retorna uma saída próxima de ZWRITE:

print(team.zw())

Exemplo:

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

Isso é útil quando você deseja comparar o resultado em Python com o que escreveria ou inspecionaria a partir do ObjectScript.

Comparação rápida com APIs nativas

O objetivo do projeto não é substituir as APIs nativas do IRIS, mas adicionar uma camada de conveniência para casos em que você está principalmente escrevendo Python.

Por exemplo, com iris-global-reference:

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

Com uma API nativa, a ordem dos argumentos e o tratamento de subscritos podem ser diferentes. Esta biblioteca padroniza o uso em torno de uma convenção Python: primeiro o caminho do nó, depois o valor.

Quando você deve usar este projeto?

iris-global-reference é especialmente útil se você:

  • desenvolve com Embedded Python e InterSystems IRIS;
  • escreve scripts Python que precisam ler ou popular globals;
  • deseja expor dados de globals como JSON;
  • deseja manipular estruturas hierárquicas do IRIS com dicionários Python;
  • deseja prototipar rapidamente sem escrever muito ObjectScript;
  • precisa de uma API mais legível para operações comuns em globals.

Roadmap

O roadmap atual inclui:

  • suporte mais avançado a arrays;
  • suporte mais completo a dados binários;
  • tipos IRIS como listbuild, vector, PVA ou bit;
  • suporte a variáveis multidimensionais.

Testando o projeto

O repositório contém uma suíte de testes. Para executá-la:

python -m pytest

Conclusão

iris-global-reference é uma biblioteca pequena, mas resolve um problema concreto: tornar globals do IRIS mais agradáveis de manipular a partir do Python.

Ela preserva as operações fundamentais do modelo IRIS enquanto adiciona uma experiência próxima de dicionários Python, conversões práticas para JSON, iteradores e uso tanto em Embedded Python quanto em conexões remotas.

Para desenvolvedores que trabalham na interseção entre InterSystems IRIS e Python, é uma ferramenta simples de experimentar e fácil de integrar em scripts, protótipos ou aplicações mais estruturadas.

Links: