Skip to content

Connecting to InterSystems IRIS from
Client-Side Python Applications


How to use external Python programs to access InterSystems IRIS

Introduction

Much of the functionality of InterSystems IRIS can be accessed from Python programs that run externally to the InterSystems IRIS instance. This is distinct from Embedded Python, which exists with classes and is run within InterSystems IRIS.

This guide refers to connecting to instances of InterSystems IRIS using applications run on the client-side, i.e. not on the same server as InterSystems IRIS. This connection can be made to allow access to InterSystems IRIS databases, or to run scripts or functions on the InterSystems IRIS server.

Installing the Python Software Development Kit

The SDK can be installed using pip:

pip  install intersystems-irispython

Executing SQL Commands

To run SQL commands on a database, we can either use the DB-API or SQLAlchemy. These are common Python frameworks for connecting to SQL databases from Python,. DB-API is a Python standard (PEP 249) which is supported by the InterSystems Python SDK, while SQLAlchemy is a popular toolkit and Object Relational Mapper.

import iris

connection_args = {
    "hostname":"localhost",
    "port":1972,
    "namespace":"USER",
    "username":"_system",
    "password":"SYS"
} 

# Connect
with iris.dbapi.connect(**connection_args) as connection:

    # Create cursor object
    cursor = connection.cursor() 

    # Basic SQL command to create a table
    sql_create ="""CREATE TABLE package.TableName (
                Col1 VARCHAR(50),
                Col2 INTEGER,
                Col3 VARCHAR(200)
                )
                """
    # Execute the Command
    cursor.execute(sql_create)

To add parameter to a query at run-time, leave a ? in place of a parameter. Here is an example of using this to insert values:

    sql = "INSERT INTO package.TableName ( Col1, Col2, Col3) values (?,?,?)"

    cursor.execute(sql, ["Hello", 1, "World!"])

Use similar syntax to execute many queries:

    params = [ 
        ["Hello", 2, "World"],
        ["Foo", 23, "Baz"],
        ["Alpha", 56, "Gamma"],
        ["One", 2, "Three"]
    ]
    cursor.executemany(sql, params)

To retrieve data from the database, we can use SELECT queries. The queries are executed in the same way, but require an additional method call to fetch the data. There are a few options here:

with iris.dbapi.connect(**connection_args) as connection:
    
    # Create Cursor
    cursor = connection.cursor()

    # Define query
    sql_select = "SELECT ID, Col1, Col2, Col3 FROM package.TableName"

    # Execute query
    cursor.execute(sql_select) 

    # Fetch next row of query results
    cursor.execute(sql_select) 
    results_set = cursor.fetchone() 

    # Fetch the next 5 rows
    results_set = cursor.fetchmany(5)

    # Fetch all rows
    cursor.execute(sql_select) # Re-execute query
    results_set = cursor.fetchall() 

    print(results_set)  # ((1, 'Hello', 1, 'World!'), (2, 'Hello', 2, 'World'), (3, 'Foo', 23, 'Baz'), (4, 'Alpha', 56, 'Gamma'), (5, 'One', 2, 'Three'))

The results set is a tuple of tuples, and can be easily converted to a pandas DataFrame as follows:

import pandas as pd

df = pd.DataFrame(results_set, columns=["ID", "Col1", "Col2", "Col3"])

SQL Alchemy

To use SQLAlchemy with IRIS, install the official InterSystems SQLAlchemy dialect from PyPI. This installs both the base SQLAlchemy, as well as the InterSystems-specific extensions.

pip install sqlalchemy-intersystems-iris

SQLAlchemy can be used as follows:

from sqlalchemy import create_engine 

# Credentials: 
username = "_system" 
password = "SYS" 
namespace = "USER" 
hostname = "localhost" 
port = 1972 

DATABASE_URL = f"iris://{username}:{password}@{hostname}:{port}/{namespace}" 

engine = create_engine(DATABASE_URL, echo=False)

You can use SQLAlchemy to create a table:

from sqlalchemy import Column, MetaData, Table, select
from sqlalchemy.sql.sqltypes import Integer, VARCHAR
from sqlalchemy_intersystems_iris import IRISVector

## Create a table, adding column objects to create the schema

demo_table = Table("DemoTableName",  # Give the table a name
                    
                    ## Create metadata for the table
                    MetaData(), 
                    
                    ## Create an ID as the index and autoincrement it
                    Column("ID", Integer, primary_key=True, autoincrement = True),
                    
                    ## Create a column for the item name
                    Column("Name", VARCHAR(1024)),
                    
                    ## Use the special IRIS Vector datatype to embed vectors 
                    Column("EmbeddingVectors", IRISVector(item_type=float, max_items=3)) 
                  )

# Drop existing table with same name
demo_table.drop(engine, checkfirst=True) # Uses the engine object created in the block above

# Create table
demo_table.create(engine, checkfirst=True)

and insert data:

with engine.connect() as conn: 
    conn.execute( demo_table.insert(), ## the action being executed
                [
                    {"Name": "Cat","EmbeddingVectors":[4,4,2]},
                    {"Name": "Dog","EmbeddingVectors":[3.5,3.5,2]},
                    {"Name": "Espresso", "EmbeddingVectors":[-2, -2, 1]},
                    {"Name": "Tea", "EmbeddingVectors":[-1, -5, 0]}
                ]
    )
    conn.commit()

The SQLAlchemy engine can be combined with the data manipulation tool, pandas, to read data directly into a DataFrame:

import pandas as pd 

query = "SELECT ID, Name, EmbeddingVectors FROM DemoTableName"

# Read SQL output Directly to Pandas Dataframe
df = pd.read_sql(query, engine)

print(df.head())

# Outputs: 
#    ID      Name EmbeddingVectors
# 0   1       Cat            4,4,2
# 1   2       Dog        3.5,3.5,2
# 2   3  Espresso          -2,-2,1
# 3   4       Tea          -1,-5,0

Native Connection (Globals and Methods)

Globals and InterSystems IRIS classes can be accessed using an  iris.createIRIS connection:

import iris

connection_args = {
    "hostname":"localhost",
    "port":1972,
    "namespace":"USER",
    "username":"_system",
    "password":"SYS"
} 
## Create connection

with iris.connect(**connection_args) as connection:
    
    # Create an iris object
    irispy = iris.createIRIS(connection)

    # Create a global array in the USER namespace on the server
    irispy.set("Value", "myGlobal", "subscript") 

    # Read the value from the database and print it
    # usage: irispy.get(Global, subscripts1, subscript2, subscript...)

    print(irispy.get("myGlobal", "subscript")) # prints "Value"

    # Delete the global array and terminate
    irispy.kill("myGlobal") 

You can also use the irispy object with server-side classes and methods. You call class method using  irispy.classMethodValue or  irispy.classMethodVoid, where value and void specify whether anything is returned from the class method. If you need specific return types, you also used  irispy.classMethodString, or replace  String with another datatype.

So we could use the following InterSystems IRIS class:

Class sample.DemoClass 
{
ClassMethod AddNumbers(a As %Integer, b As %Integer) As %Integer
{
	return a+b	
}
ClassMethod IncrementGlobal(value As %String) As %Status
{
	set ^DemoGlobal($INCREMENT(^DemoGlobal)) = value
}
}

With:

with iris.connect(**connection_args) as connection:
    
    # Create irispy object
    irispy = iris.createIRIS(connection)

    # Call the AddNumbers class method with return value
    number_sum = irispy.classMethodValue("sample.DemoClass", "AddNumbers", 1, 2) 
    print(number_sum) # prints 3

    # Calls the IncrementGlobal class method without return value
    irispy.classMethodVoid("sample.DemoClass", "IncrementGlobal", "Hello World")

    print(irispy.get("DemoGlobal")) # prints 1 (or the number of times IncrementGlobal has been run)

    print(irispy.get("DemoGlobal", 1)) # Prints "Hello World"

You can also create a class object, for example, say you had the IRIS class:

Class sample.Person Extends %Persistent
{

	Property Name As %String;
	
	Property Age As %Integer;
	
	Method setName(name As %String) As %Status{
		set ..Name = name
		quit $$$OK
	}
	
	Method addYears(num as %Integer) As %Integer {
		return ..Age + num
	}
}

You could use this in an external Python application with:

with iris.connect(**connection_args) as connection:
        
    # Create new class object 
    person = irispy.classMethodObject("sample.Person", "%New")

    # Set a property
    person.set("Age", 25)

    # Get a property
    age = person.get("Age")

    print(age) # 25

    # Call a method without a return with .invokeVoid(methodName, parameter)
    person.invokeVoid("setName", "George")
    print(person.get("Name")) # prints "George"

    # Call a method with a return
    new_age = person.invoke("addYears", 5)
    print(new_age) # prints 30

    # Call the inherited method to save the object to a database
    person.invokeVoid("%Save")

Further Reading