Skip to content

grongierisc/iris-global-reference

Repository files navigation

1. IRIS Global Reference Python API

  • Magic in place of Basic
  • Some picture of globals (tree array)

A Pythonic wrapper around InterSystems IRIS globals providing an intuitive interface for working with hierarchical data.

2. Introduction

InterSystems IRIS is a powerful database platform that provides a unique hierarchical data structure called globals. Globals are a key-value store that can be used to store hierarchical data in a tree-like structure. This hierarchical data structure is very powerful but can be difficult to work with using traditional programming languages.

The IRIS Global Reference Python API is a Pythonic wrapper around InterSystems IRIS globals that provides an intuitive interface for working with hierarchical data. The API provides a set of core methods for working with globals, including data operations, node information, navigation, and iteration. The API also provides support for converting globals to dictionaries and JSON and back, making it easy to work with hierarchical data in Python.

Key differences with globals and dictionaries:

  • Globals don't have a notion of arrays
    • This project provides a serialization to store arrays in globals
  • A global node can have both values and child nodes
    • In a dictionary, a node can have either a value or child nodes, but not both
      • To solve this, the API binds the root node to None key
  • Globals are ordered
    • Dictionaries are unordered

3. Installation

pip install iris-global-reference

to have direct access to an iris terminal, you can use the following command:

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

4. Basic Usage

Here's a comprehensive example showing various ways to interact with globals:

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")
team.kill()  # Clear any existing data

# Different ways to set values
team.set((), "Baseball")                    # Set root node
team["name"] = "Boston Red Sox"             # Dictionary-style assignment
team.set(("players", "1"), "Babe Ruth")     # Tuple subscript
team.set(["players", "2"], "Cy Young")      # List subscript
team["players", "3"] = "Ted Williams"       # Multiple subscripts

# Different ways to get values
print(team.get(()))                         # Get root value: "Baseball"
print(team["name"])                         # Dictionary-style access
print(team.get(("players", "1")))          # Using get() method
print(team["players"]["2"])                # Nested dictionary-style

# Check if nodes exist
print(("players", "1") in team)            # True
print("nonexistent" in team)               # False

# Delete nodes
del team["players", "3"]                   # Delete using del
team.kill(("players", "2"))               # Delete using kill()

# Iteration Examples
# 1. Iterate through all nodes and values
for key, value in team.items():
    print(f"Node {key}: {value}")          # Shows all nodes with values

# Output :
# Node (): Baseball
# Node ('name',): Boston Red Sox
# Node ('players', '1'): Babe Ruth

# 2. Iterate through direct children only
for key in team.keys(children_only=True):
    print(f"Direct child: {key}")          # Shows only root level nodes

# Output:
# Direct child: ()
# Direct child: ('name',)

# 3. Custom iteration with subscripts
for sub in team.subscripts(("players",), children_only=True):
    print(f"Player: {team.get(sub)}")      # Shows only players

# Output:
# Player: Babe Ruth

# Count direct children
print(len(team))                           # Number of root level nodes

# Output:
# 3

# Display global structure
print(team.zw())                           # Show ZWRITE format

# Output:
# ^demo="Baseball"
# ^demo("name")="Boston Red Sox"
# ^demo("players","1")="Babe Ruth"

# Display this global in a dictionary format
print(team.to_dict())

# Output:
# {
#   None: 'Baseball',
#  'name': 'Boston Red Sox',
#  'players': {
#       '1': 'Babe Ruth'
#       }
# }

5. Support of remote connection

You can use the GlobalReference class with embedded Python by omitting the connection, or connect to a remote IRIS instance by passing the native iris connection returned by iris.connect(...).

import iris
from iris_global import GlobalReference

# Embedded Python, running inside IRIS.
team = GlobalReference("^demo")

# Remote connection, running outside IRIS.
conn = iris.connect("localhost", 1972, "USER", "SuperUser", "SYS")
team = GlobalReference("^demo", connection=conn)

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")

# Get values
print(team.get(()))  # Baseball
print(team.get(("name",)))  # Boston Red Sox
print(team.get(("players", "1")))  # Babe Ruth

SQLAlchemy engines and iris://... connection strings are no longer supported by GlobalReference. Use iris.connect(host, port, namespace, username, password) for remote access.

6. Core Methods

6.1. Data Operations

  • set(subscript, value): Set a value at the specified node
  • get(subscript): Get the value at the specified node
  • kill(subscript): Delete a node and all its descendants
  • delete(subscript): Same as kill() but more Pythonic

6.1.1. set() Method

The set() method can be used to set values at any node in the global:

team.set((), "Baseball")

can be used to set the root node, while:

team.set(("name",), "Boston Red Sox")

sets a child node with subscript "name".

set can also be used with str or list subscript:

team.set("", "Baseball")
team.set("name", "Boston Red Sox")
team.set(["players", "1"], "Babe Ruth")

You can also use dictionary type subscript:

team["name"] = "Boston Red Sox"
team["players", "1"] = "Babe Ruth"
team["players"]["1"] = "Babe Ruth"

6.1.2. get() Method

The get() method can be used to retrieve values at any node in the global:

team.get(())  # Returns "Baseball"
team.get(("name",))  # Returns "Boston Red Sox"
team.get(("players", "1"))  # Returns "Babe Ruth"

get can also be used with str or list subscript:

team.get("")  # Returns "Baseball"
team.get("name")  # Returns "Boston Red Sox"
team.get(["players", "1"])  # Returns "Babe Ruth"

You can also use dictionary type subscript:

print(team["name"])  # Returns "Boston Red Sox"
print(team["players", "1"])  # Returns "Babe Ruth"
print(team["players"]["1"])  # Returns "Babe Ruth"

6.1.3. kill() Method

The kill() method can be used to delete a node and all its descendants:

team.kill(("players",))

kill can also be used with str or list subscript:

team.kill("players")
team.kill(["players"])

You can also use dictionary type subscript:

del team["players"]

6.1.4. delete() Method

Same as kill() but more Pythonic.

6.2. Node Information

  • data(subscript): Get node status (0=undefined, 1=value only, 10=descendants only, 11=both)
  • has_value(subscript): Check if node has a value
  • has_descendants(subscript): Check if node has child nodes

6.2.1. data() Method

The data() method can be used to get the status of a node:

from iris_global import GlobalReference

data = {
    None: "root",
    "a": {1: "value a"}
}

team = GlobalReference("^data").from_dict(data)

print(team.data(()))  # 11 (node with descendants and value)
print(team.data(("a",)))  # 10 (node with descendants only)
print(team.data(("a", 1)))  # 1 (node with value only)
print(team.data(("b",)))  # 0 (undefined node)

Works with str or list subscript:

print(team.data(""))  # 11
print(team.data("a"))  # 10
print(team.data(["a", 1]))  # 1
print(team.data("b"))  # 0

6.2.2. has_value() Method

The has_value() method can be used to check if a node has a value:

from iris_global import GlobalReference

data = {
    None: "root",
    "a": {1: "value a"}
}

team = GlobalReference("^data").from_dict(data)

print(team.has_value(()))  # True
print(team.has_value(("a",)))  # False
print(team.has_value(("a", 1)))  # True
print(team.has_value(("b",)))  # False

Works with str or list subscript:

print(team.has_value(""))  # True
print(team.has_value("a"))  # False
print(team.has_value(["a", 1]))  # True
print(team.has_value("b"))  # False

6.2.3. has_descendants() Method

The has_descendants() method can be used to check if a node has child nodes:

from iris_global import GlobalReference

data = {
    None: "root",
    "a": {1: "value a"}
}

team = GlobalReference("^data").from_dict(data)

print(team.has_descendants(()))  # True
print(team.has_descendants(("a",)))  # True
print(team.has_descendants(("a", 1)))  # False
print(team.has_descendants(("b",)))  # False

Works with str or list subscript:

print(team.has_descendants(""))  # True
print(team.has_descendants("a"))  # True
print(team.has_descendants(["a", 1]))  # False
print(team.has_descendants("b"))  # False

6.3. Navigation

  • next(subscript, direction): Get next subscript at same level (direction=1) or previous (direction=-1)
  • previous(subscript): Get previous subscript at the same level
  • query(subscript, direction): Navigate through nodes like ObjectScript $QUERY
  • order(subscript, direction): Legacy through nodes like ObjectScript $ORDER

6.3.1. next() Method

The next() method can be used to get the next subscript:

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")

# Query nodes
print(team.next(()))  # Returns ("name",)
print(team.next(("name",)))  # Returns ("players",)
print(team.next(("players",)))  # Returns ("players", "1")
print(team.next(("players", "1")))  # Returns ("players", "2")
print(team.next(("players", "2")))  # Returns None

works with str or list subscript:

print(team.next(""))  # Returns ("name",)
print(team.next("name"))  # Returns ("players",)
print(team.next("players"))  # Returns ("players", "1")
print(team.next(["players", "1"]))  # Returns ("players", "2")
print(team.next(["players", "2"]))  # Returns None

6.3.2. query() Method

Same as next().

The query() method can be used to navigate through nodes like ObjectScript $QUERY:

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")

# Query nodes
print(team.query(()))  # Returns ("name",)
print(team.query(("name",)))  # Returns ("players",)
print(team.query(("players",)))  # Returns ("players", "1")
print(team.query(("players", "1")))  # Returns ("players", "2")
print(team.query(("players", "2")))  # Returns None

works with str or list subscript:

print(team.query(""))  # Returns ("name",)
print(team.query("name"))  # Returns ("players",)
print(team.query("players"))  # Returns ("players", "1")
print(team.query(["players", "1"]))  # Returns ("players", "2")
print(team.query(["players", "2"]))  # Returns None

6.3.3. order() Method

Legacy method to navigate through nodes like ObjectScript $ORDER:

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")

# Get next subscript
print(team.order(()))  # Returns "name"
print(team.order(("name",)))  # Returns "players"
print(team.order(("players",)))  # Returns None
print(team.order(("players", "")))  # Returns "1"
print(team.order(("players", "1")))  # Returns "2"
print(team.order(("players", "2")))  # Returns None

works with str or list subscript:

print(team.order(""))  # Returns "name"
print(team.order("name"))  # Returns "players"
print(team.order("players"))  # Returns None
print(team.order(["players", ""]))  # Returns "1"
print(team.order(["players", "1"]))  # Returns "2"
print(team.order(["players", "2"]))  # Returns None

6.4. Iteration

Note: subscript value are always returned as string tuples

  • subscripts(subscript, direction, with_descendants, with_values, with_root, children_only): Get all subscripts at subscript level
  • keys(subscript, direction, with_descendants, with_values, with_root, children_only): Get all subscripts at subscript level
  • values(subscript, direction, with_descendants, with_values, with_root, children_only): Get all values at subscript level
  • items(subscript, direction, with_descendants, with_values, with_root, children_only): Get all subscripts and values at subscript level

6.4.1. subscripts() Method

The subscripts() method can be used to get all subscripts at a subscript level.

Signature:

  • subscripts(subscript, direction=1, with_descendants=False, with_values=False, with_root=False, children_only=False)
    • subscript: Subscript to start from
    • direction: Direction (1=forward, -1=backward)
    • with_descendants: Include child nodes default: False
    • with_values: Only include nodes with values default: False
    • with_root: Include the start node default: False
    • children_only: Only include direct children default: False

Note: subscripts aim to have the same behavior as ObjectScript $ORDER

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

# Get all subscripts
for subscript in team.subscripts():
    print(subscript) # Returns ('name',), ('players',), ('world_series',)

# Get subscripts starting from "players"
for subscript in team.subscripts(("players",)):
    print(subscript) # Returns ('world_series',)

# Get subscripts starting from "players" that are nested
for subscript in team.subscripts(("players","")):
    print(subscript) # Returns ('players', '1'), ('players', '2')
# or
for subscript in team.subscripts(("players",), children_only=True):
    print(subscript) # Returns ('players', '1'), ('players', '2')

# Get subscripts only with values
for subscript in team.subscripts(with_values=True):
    print(subscript) # Returns ('name',)

# Get subscripts only with descendants
for subscript in team.subscripts(with_descendants=True):
    print(subscript) # Returns ('name',), ('players',), ('players', '1'), ('players', '2'), ('world_series',), ('world_series', '1'), ('world_series', '2')

# Get subscripts with root node
for subscript in team.subscripts(with_root=True):
    print(subscript) # Returns (), ('name',), ('players',), ('world_series',)

# Get subscripts only with descendants and values
for subscript in team.subscripts(with_descendants=True, with_values=True):
    print(subscript) # Returns ('name',), ('players', '1'), ('players', '2'), ('world_series', '1'), ('world_series', '2')

# Get subscripts with root node and descendants and values
for subscript in team.subscripts(with_root=True, with_descendants=True, with_values=True):
    print(subscript) # Returns (), ('name',), ('players', '1'), ('players', '2'), ('world_series', '1'), ('world_series', '2')

6.4.2. keys() Method

The keys() method can be used to get all subscripts at a subscript level.

Signature:

  • keys(subscript, direction=1, with_descendants=True, with_values=True, with_root=True, children_only=False)
    • subscript: Subscript to start from
    • direction: Direction (1=forward, -1=backward)
    • with_descendants: Include child nodes default: True
    • with_values: Only include nodes with values default: True
    • with_root: Include the start node default: True
    • children_only: Only include direct children default: False

Note: keys aim to have the same behavior as Python dictionary keys() It means return all subscripts with values by descending all branches including the root node Basically, same as subscripts(with_descendants=True, with_values=True, with_root=True) Aim to be close as possible to ObjectScript ZWRITE

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

# Get keys
for key in team.keys():
    print(key) # Returns (), ('name',), ('players', '1'), ('players', '2'), ('world_series', '1'), ('world_series', '2')

# Get all keys
for key in team.keys(with_values=False): # Returns all subscripts even without values
    print(key) # Returns ('name',), ('players',), ('players', '1'), ('players', '2'), ('world_series',), ('world_series', '1'), ('world_series', '2')

# Get keys starting from "players"
for key in team.keys(("players",)):
    print(key) # Returns ('players', '1'), ('players', '2'), ('world_series', '1'), ('world_series', '2')

# Get keys starting from "players" that are nested
for key in team.keys(("players",), children_only=True):
    print(key) # Returns ('players', '1'), ('players', '2')
# or
for key in team.keys(("players","")):
    print(key) # Returns ('players', '1'), ('players', '2')

6.4.3. values() Method

The values() method can be used to get all values at a subscript level.

Signature:

  • values(subscript, direction=1, with_descendants=True, with_values=True, with_root=True, children_only=False)
    • subscript: Subscript to start from
    • direction: Direction (1=forward, -1=backward)
    • with_descendants: Include child nodes default: True
    • with_values: Only include nodes with values default: True
    • with_root: Include the start node default: True
    • children_only: Only include direct children default: False

Note: values aim to have the same behavior as Python dictionary values() It means return all values by descending all branches including the root node

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

# Get values
for value in team.values():
    print(value) # Returns "Baseball", "Boston Red Sox", "Babe Ruth", "Cy Young", "1903", "1912"

6.4.4. items() Method

The items() method can be used to get all subscripts and values at a subscript level.

Signature:

  • items(subscript, direction=1, with_descendants=True, with_values=True, with_root=True, children_only=False)
    • subscript: Subscript to start from
    • direction: Direction (1=forward, -1=backward)
    • with_descendants: Include child nodes default: True
    • with_values: Only include nodes with values default: True
    • with_root: Include the start node default: True
    • children_only: Only include direct children default: False

Note: items aim to have the same behavior as Python dictionary items() It means return all subscripts and values by descending all branches including the root node

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

# Get items
for key, value in team.items():
    print(key, value) # Returns (), "Baseball", ('name',), "Boston Red Sox", ('players', '1'), "Babe Ruth", ('players', '2'), "Cy Young", ('world_series', '1'), "1903", ('world_series', '2'), "1912"

6.4.5. zw() Method

The zw() method can be used to display the global in ObjectScript ZWRITE format:

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

print(team.zw())
# ^demo=Baseball
# ^demo("name")="Boston Red Sox"
# ^demo("players","1")="Babe Ruth"
# ^demo("players","2")="Cy Young"
# ^demo("world_series","1")="1903"
# ^demo("world_series","2")="1912"

7. Dictionary & JSON Operations

Convert globals to dictionaries or JSON and back.

  • to_dict(subscript, with_descendants, with_values, with_root, children_only, merge_leafs): Export global to dictionary
  • from_dict(data): Import dictionary to global
  • to_json(subscript, with_descendants, with_values, with_root, children_only, merge_leafs): Export global to JSON
  • from_json(json_str, root_name): Import JSON to global

7.1. to_dict() Method

The to_dict() method can be used to export a global to a dictionary.

Signature:

  • to_dict(subscript=None, with_descendants=True, with_values=True, with_root=True, children_only=False, merge_leafs=True)
    • subscript: Subscript to start from
    • with_descendants: Include child nodes default: True
    • with_values: Only include nodes with values default: True
    • with_root: Include the start node default: True
    • children_only: Only include direct children default: False
    • merge_leafs: Merge leaf nodes into a single value default: True
from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

# Export to dictionary
data_dict = team.to_dict()
print(data_dict)

# returns:
# {
#     None: "Baseball",
#     "name": "Boston Red Sox",
#     "players": {
#         "1": "Babe Ruth",
#         "2": "Cy Young"
#     },
#     "world_series": {
#         "1": "1903",
#         "2": "1912"
#     }
# }
# Note: root node is bound to None key
# Note: leaf nodes are merged by default

# Export to dictionary unmeged
print(team.to_dict(merge_leafs=False))
# returns: 
# {
#     None: "Baseball",
#     "name": {None: "Boston Red Sox"},
#     "players": {
#         "1": {None: "Babe Ruth"},
#         "2": {None: "Cy Young"}
#     },
#     "world_series": {
#         "1": {None: "1903"},
#         "2": {None: "1912"}
#     }
# }

7.2. from_dict() Method

The from_dict() method can be used to import a dictionary to a global.

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Dictionary data
data_dict = {
    None: "Baseball",
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young"
    },
    "world_series": {
        "1": "1903",
        "2": "1912"
    }
}

# Note: root node is bound to None key

# Import dictionary
team.from_dict(data_dict)

# Unmerged dictionary data can be imported as well
data_dict = {
    None: "Baseball",
    "name": {None: "Boston Red Sox"},
    "players": {
        "1": {None: "Babe Ruth"},
        "2": {None: "Cy Young"}
    },
    "world_series": {
        "1": {None: "1903"},
        "2": {None: "1912"}
    }
}

team_unmerged = GlobalReference("^unmerged")
team_unmerged.from_dict(data_dict)

# Check values
team_unmerged.to_dict() == team.to_dict()  # True

7.3. to_json() Method

The to_json() method can be used to export a global to JSON.

Signature:

  • to_json(subscript=None, with_descendants=True, with_values=True, with_root=True, children_only=False, merge_leafs=True, root_name="_")
    • subscript: Subscript to start from
    • with_descendants: Include child nodes default: True
    • with_values: Only include nodes with values default: True
    • with_root: Include the start node default: True
    • children_only: Only include direct children default: False
    • merge_leafs: Merge leaf nodes into a single value default: True
    • root_name: Rename the root node
from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")
team.set(("name",), "Boston Red Sox")
team.set(("players", "1"), "Babe Ruth")
team.set(("players", "2"), "Cy Young")
team.set(("world_series", "1"), "1903")
team.set(("world_series", "2"), "1912")

# Export to JSON
print(team.to_json())
# returns: 
# {
#     "name": "Boston Red Sox",
#     "players": {
#         "1": "Babe Ruth",
#         "2": "Cy Young"
#     },
#     "world_series": {
#         "1": "1903",
#         "2": "1912"
#     },
#     "_": "Baseball"
# }
# Note: root node is bound to "_"
# Note: leaf nodes are merged by default

# Export to JSON unmeged
print(team.to_json(merge_leafs=False))
# returns:
# {
#     "name": {"_": "Boston Red Sox"},
#     "players": {
#         "1": {"_": "Babe Ruth"},
#         "2": {"_": "Cy Young"}
#     },
#     "world_series": {
#         "1": {"_": "1903"},
#         "2": {"_": "1912"}
#     },
#     "_": "Baseball"
# }

# Root node can be renamed
print(team.to_json(root_name="__root__"))
# returns:
# {
#     "name": "Boston Red Sox",
#     "players": {
#         "1": "Babe Ruth",
#         "2": "Cy Young"
#     },
#     "world_series": {
#         "1": "1903",
#         "2": "1912"
#     },
#     "__root__": "Baseball"
# }

7.4. from_json() Method

The from_json() method can be used to import JSON to a global.

import json
from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# JSON data
json_str = """
{
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young"
    },
    "world_series": {
        "1": "1903",
        "2": "1912"
    },
    "_": "Baseball"
}
"""

# Import JSON
team.from_json(json_str)

# Check values
print(json.loads(json_str) == json.loads(team.to_json()))  # True

# Root node can be renamed
json_str = """
{
    "name": "Boston Red Sox",
    "players": {
        "1": "Babe Ruth",
        "2": "Cy Young"
    },
    "world_series": {
        "1": "1903",
        "2": "1912"
    },
    "__root__": "Baseball"
}
"""

team.from_json(json_str, root_name="__root__")

# Check values
print(json.loads(json_str) == json.loads(team.to_json(root_name="__root__")))  # True

8. Python Magic Methods

GlobalReference implements Python magic methods for a more Pythonic experience:

# Dictionary-like access
team["name"] = "Boston Red Sox"
print(team["name"])
del team["players", "3"]

# Check if node exists
if ("players", "1") in team:
    print("Player exists!")

# Get number of direct child nodes
num_nodes = len(team)

# Iterate directly
for key,value in team:
    print(key, value)

Values returned with [] are Key objects. For string, integer, float, and bytes values, the returned key is also a subclass of the matching Python primitive:

name = team["name"]
isinstance(name, str)  # True

Bracket access on a Key is always global child access, not native primitive indexing. Use .value, str(key), or bytes(key) when you need primitive indexing:

team["name"][0]        # Looks up subscript ("name", 0)
team["name"].value[0]  # Returns first character from the string value

Boolean values are returned as plain Key wrappers because Python does not allow subclassing bool.

9. Array support

Note: Array support is experimental

To support array from dict/json, i used a serialization to store the array in a global. The serialization is the following:

  • the root node contains an serialization string eg: __array__
  • the children nodes contains the array values
  • the children nodes are named with the serialization string and the index of the array

example:

from iris_global import GlobalReference

GlobalReference("demo").kill()

gref = GlobalReference("demo")

my_dict = {"a": 1, "b": 2, "array": [1, 2, 3], "array_of_dicts": [{"a": 1}, {"b": 2}]}
gref.from_dict(my_dict)

print(gref.to_dict()) # {'a': 1, 'array': [1, 2, 3], 'array_of_dicts': [{'a': 1}, {'b': 2}], 'b': 2}
print(gref.zw())
# ^demo("a")=1
# ^demo("array")="__array__"
# ^demo("array","__array__0")=1
# ^demo("array","__array__1")=2
# ^demo("array","__array__2")=3
# ^demo("array_of_dicts")="__array__"
# ^demo("array_of_dicts","__array__0","a")=1
# ^demo("array_of_dicts","__array__1","b")=2
# ^demo("b")=2

# can still present global as dict without serialization
print(gref.to_dict(merge_array=False)) 
# returns:
# {
#     'a': 1,
#     'array': {None: '__array__', '__array__0': 1, '__array__1': 2, '__array__2': 3},
#     'array_of_dicts': {None: '__array__', '__array__0': {'a': 1}, '__array__1': {'b': 2}},
#     'b': 2
# }

# the serialization string can be changed
gref.kill()
gref.from_dict(my_dict, array_prefix="__list__")
print(gref.zw())
# ^demo("a")=1
# ^demo("array")="__list__"
# ^demo("array","__list__0")=1
# ^demo("array","__list__1")=2
# ^demo("array","__list__2")=3
# ^demo("array_of_dicts")="__list__"
# ^demo("array_of_dicts","__list__0","a")=1
# ^demo("array_of_dicts","__list__1","b")=2
# ^demo("b")=2

10. Transaction support

Transactions can be supported by using the with statement:

from iris_global import GlobalReference

# Create a reference to a global with a transaction
with GlobalReference("^demo") as team:
    team.set((), "Baseball")
    team.set(("name",), "Boston Red Sox")
    team.set(("players", "1"), "Babe Ruth")
    team.set(("players", "2"), "Cy Young")

or

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")

# Start transaction
with team:
    team.set(("name",), "Boston Red Sox")
    team.set(("players", "1"), "Babe Ruth")
    team.set(("players", "2"), "Cy Young")

Otherwize, you can use the begin(), commit() and rollback() methods:

from iris_global import GlobalReference

# Create a reference to a global
team = GlobalReference("^demo")

# Set values
team.set((), "Baseball")

# Start transaction
team.begin()
team.set(("name",), "Boston Red Sox")

# Commit transaction
team.commit()

11. Benefits

  • Pythonic: Provides a Pythonic interface for working with globals
  • Intuitive: Simplifies working with hierarchical data
  • Efficient: Reduces the need for complex ObjectScript code
  • Flexible: Supports a wide range of data operations

11.1. Merge operation

ObjectScript has a merge operation that can be used to merge two globals. This operation is not supported by the API. However, you can achieve the same result by exporting the globals to dictionaries, merging the dictionaries, and then importing the merged dictionary back to a global.

from iris_global import GlobalReference

# Create a reference to a global
team1 = GlobalReference("^team1")
team11 = GlobalReference("^team11")
team2 = GlobalReference("^team2")

# Set values
team1.set((), "Baseball")
team1.set(("name",), "Boston Red Sox")
team1.set(("players", "1"), "Babe Ruth")

team11.set((), "Baseball")
team11.set(("name",), "Boston Red Sox")
team11.set(("players", "1"), "Babe Ruth")

team2.set((), "Baseball")
team2.set(("name",), "New York Yankees")
team2.set(("players", "2"), "Lou Gehrig")

# Export to dictionaries
data1 = team1.to_dict()
data2 = team2.to_dict()

# Merge dictionaries
data1.update(data2)

# Import merged dictionary
team1.from_dict(data1)
prin(team1.zw())
# ^team1=Baseball
# ^team1("name")=New York Yankees
# ^team1("players","1")=Babe Ruth
# ^team1("players","2")=Lou Gehrig

# or
team11.from_dict(team2)
prin(team11.zw())
# ^team11=Baseball
# ^team11("name")=New York Yankees
# ^team11("players","1")=Babe Ruth
# ^team11("players","2")=Lou Gehrig

The benefit of this approach is that it allows you to use Python's built-in dictionary operations to manipulate globals, making it easier to work with hierarchical data structures. For the more, no need to build a complex python code to merge two globals.

12. Roadmap

  • Add support for global arrays
  • Add support for byte set/get
  • Add support for listbuild, vector, pva, bit
  • Add support of mutli-dimensional variables

13. Compare to other libraries

This table compares the api of the iris_global library with other libraries, only low-level operations are compared:

Feature iris_global irisnative embedded-python comments
initialize global_reference = GlobalReference("^demo", connection=conn) conn = iris.connect(...); global_reference = iris.createIRIS(conn) global_reference = iris.gref("^demo") omit connection for embedded Python
set value global_reference.set(("name",1), "Boston Red Sox") global_reference.set("Boston Red Sox", "demo", "name", 1) global_reference.set(["name",1], "Boston Red Sox") for irisnative, the value is the first argument
get value global_reference.get(("name",1)) global_reference.get("demo", "name", 1) global_reference.get(["name",1])
check if node exists global_reference.data(("name",1)) global_reference.isDefined("demo", "name", 1) global_reference.data(["name",1])
delete node global_reference.kill(("name",1)) global_reference.kill("demo", "name", 1) global_reference.kill(["name",1])
order global_reference.order(("name",1)) global_reference.nextSubscript(False, "demo", "name", 1) global_reference.order(["name",1]) for irisnative, the reverse flag is the first argument

General remarks:

  • iris_global support for get/set can be used with str, list or tuple subscript
  • irisnative use the value as the first argument
  • embedded-python use list subscript
  • irisnative use nextSubscript instead of order
  • irisnative use isDefined instead of data

14. Testing

Run the test suite:

python -m pytest

15. License

MIT License - see LICENSE for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors