Article
· Sep 12 7m read

Embedded python in InterSystems IRIS

Hello Community,

In this article, I will outline and illustrate the process of implementing ObjectScript within embedded Python. This discussion will also reference other articles related to embedded Python, as well as address questions that have been beneficial to my learning journey.

As you may know, the integration of Python features within IRIS has been possible for quite some time. This article will focus on how to seamlessly incorporate ObjectScript with embedded Python.

Essentially, embedded Python serves as an extension that allows for independent writing and execution. It enables the seamless integration of Python code with ObjectScript and vice versa, allowing both to run within the same context. This functionality significantly enhances the capabilities of your implementation.

To begin, you must specify the language for your Python code within the class definition by using the "language" keyword [language = "python"]. Once this is done, you are prepared to write your Python code.

import iris - The iris package is a vital Python library that facilitates communication with InterSystems' native API classes, routines, globals, and SQL. This package is readily available by default. Anyway It is necessary to import this package at the beginning of your python code if you're going to interact with IRIS.

Few important notes before writing

  • You can use python special variable __name__ to refer the classname inside the class definition.
  • Use _ for %Methods ex: %New  == _New , %OpenId == _OpenId

Let's begin

Class members implementation in Embedded python

1.  Objects and Properties

This section is essential as it will cover the process of initializing a new object, altering the values of existing objects, and configuring properties in both static and dynamic contexts. Create your own class definition and use the simple literal properties

1.1 initialize new Object / Modify existing object

Use _New for initiate new object and _OpenId(id) for edit the existing object

ClassMethod SaveIRISClsObject() [ Language = python ]
{
 #this method calls the %OnNew callback method and get the object
 import iris
 try:
     iris_obj =  iris.cls(__name__)._New()
     if not iris.cls(__name__).IsObj(iris_obj):
      #IsObj is the objectscript wrapper method : which contains $Isobject()
         raise ReferenceError('Object Initlize Error')
 except ReferenceError as e:
     print(e)
     return
 #set the object properties and save the values
 iris_obj.Name = 'Ashok'
 iris_obj.Phone = 9639639635
 status = iris_obj._Save()
 print(status)
 return status
}

1.2 Accessing properties

Prior to commencing the property section, it is important to note that the IRIS data type differs from Python data types, and therefore, IRIS collection data types cannot be utilized directly within Python. To address this, InterSystems has offered a comprehensive solution for converting IRIS data types into Python-compatible formats such as lists, sets, and tuples. This can be achieved by importing the "builtins" module within the IRIS code base, utilizing the class methods ##class(%SYS.Python).Builtins() or by setting builtins = ##class(%SYS.Python).Import("builtins"). I'll cover this in upcoming sections.

So, I use this method to convert the $LB properties into python list for accessing the properties at runtime in python

LEARNING>Set pyList = ##class(%SYS.Python).ToList($LB("Name","Phone","City"))
 
LEARNING>zw pyList
pyList=5@%SYS.Python  ; ['Name', 'Phone', 'City']  ; <OREF>
ClassMethod GetProperties() [Language = objectscript]
{
    set pyList = ##class(%SYS.Python).ToList($LB("Name","Phone","City"))
    do ..pyGetPropertiesAtRunTime(pyList)
}
ClassMethod pyGetPropertiesAtRunTime(properties) [ Language = python ]
{
    import iris
    iris_obj = iris.cls(__name__)._OpenId(1)
    for prop in properties:
        print(getattr(iris_obj,prop))
}

 

1.3 Set properties at runtime.

I utilize this python dictionary to designate my property as a key and, with the corresponding property values serving as the values within that dictionary. You may refer to the code provided below and the community post regarding this property set.

ClassMethod SetProperties()
{
	Set pyDict = ##class(%SYS.Python).Builtins().dict()
	do pyDict.setdefault("Name1", "Ashok kumar")
	do pyDict.setdefault("Phone", "9639639635")
	do pyDict.setdefault("City", "Zanesville")
	Set st = ..pySetPropertiesAtRunTime(pyDict)
}

ClassMethod pySetPropertiesAtRunTime(properties As %SYS.Python) [ Language = python ]
{
	import iris
	iris_obj = iris.cls(__name__)._New()
	for prop in properties:
		setattr(iris_obj, prop,properties[prop])
	
	status = iris_obj._Save()
	return status
}

1.4 Object share context

As previously stated, both Python and ObjectScript operate within the same memory context and share objects. This implies that you can create or open an object in the InCache class and subsequently set or retrieve it in the Python class.

ClassMethod ClassObjectAccess() [Language = objectscript]
{
	Set obj = ..%OpenId(1)
	Write obj.PropAccess(),! ; prints "Ashok kumar"
	Do obj.DefineProperty("test")
	Write obj.PropAccess() ; prints "test"
}

Method PropAccess() [ Language = python ]
{
	
return self.Name
}

Method DefineProperty(name) [ Language = python ]
{
	
self.Name = name
}

2.  Parameters

Get the parameter arbitrary key value pair by using the _GetParameter. Refer the useful community post 

ClassMethod GetParam(parameter = "MYPARAM") [ Language = python ]
{
	import iris
	value = iris.cls(__name__)._GetParameter(parameter)
	print(value)
}

3. Class Method and Methods

3.1 Class Method

The invocation of class methods and functions is highly beneficial for executing the object script code.

Invoke the class method as a static call ex: Do ..Test() 

ClassMethod InvokeStaticClassMethods(clsName = "MyLearn.EmbeddedPython") [ Language = python ]
{
	import iris
	print(iris.cls(clsName).Test())
	# print(iris.cls(__name__).Test()) 
}


Invoke the Class method at runtime Set method="Test" Do $ClassMethod(class, method, args...)

ClassMethod InvokeClassMethodsRunTime(classMethod As %String = "Test") [ Language = python ]
{
 import iris
 clsMethodRef = getattr(iris.cls(__name__), classMethod) # will return the reference of the method
 print(clsMethodRef()) 
}

3.2  Methods

Invoke the instance methods are same as "object script" format. In the below code I've initiate the object first and call the instance method with parameters.

ClassMethod InvokeInstanceMethodWithActualParameters() [ Language = python ]
{
	import iris
	obj = iris.cls(__name__)._New()
	print(obj.TestMethod(1,2,4))
}

3.3  Pass arguments by value and reference b/w python and objectscript

Basically passing the arguments are inevitable between the functions and the same will remain between ObjectScript and python

3.4  Pass by value - It's as usual pass by value

ClassMethod passbyvalfromCOStoPY()
{
    Set name = "test", dob= "12/2/2002", city="chennai"
    Do ..pypassbyvalfromCOStoPY(name, dob, city)
}

ClassMethod pypassbyvalfromCOStoPY(name As %String, dob As %String, city As %String) [ Language = python ]
{
   print(name,'  ',dob,'  ',city)
}

/// pass by value from  python to object script
ClassMethod pypassbyvalfromPY2COS() [ Language = python ]
{
	import iris
	name = 'test'
	dob='12/2/2002'
	city='chennai'
	iris.cls(__name__).passbyvalfromPY2COS(name, dob, city)
}

ClassMethod passbyvalfromPY2COS(name As %String, dob As %String, city As %String)
{
    zwrite name,dob,city
}

3.5 Pass by reference- Unlike the pass by value. Since Python does not support call by reference natively, so, you need to use the iris.ref()  in python code to make it the variable as a reference. refer the reference. To the best of my knowledge, there are no effects on the object script side regarding pass-by-reference variables, even when these variables are modified in Python. Consequently, Python variables will be affected by this pass-by-reference mechanism when the methods of the object script are invoked

ClassMethod pypassbyReffromPY2COS() [ Language = python ]
{
	import iris
	name='python'
	dob=iris.ref('01/01/1991')
	city = iris.ref('chennai')
	print('before COS ',name,'  ',dob.value,'  ',city.value)
	#pass by reference of dob, city
	iris.cls('MyLearn.EmbeddedPythonUtils').passbyReffromPY2COS(name, dob, city)	
	print('after COS ',name,'  ',dob.value,'  ',city.value)
}

ClassMethod passbyReffromPY2COS(name, ByRef dob, ByRef city)
{
  Set name="object script", dob="12/12/2012", city="miami"
}

// output 
LEARNING>do ##class(MyLearn.EmbeddedPythonUtils).pypassbyReffromPY2COS()
before COS  python    01/01/1991    chennai
after COS  python    12/12/2012    miami
 


3.5 **kwargs- There is an additional support for passing the python keyword arguments (**kwargs) from object script. Since InterSystems IRIS does not have the concept of keyword arguments, you have to create a  %DynamicObject to hold the keyword/value pairs and pass the values as syntax Args...

I have created the dynamicObject  "name""ashok""city""chennai"}and set the required key-value pairs in it and eventually pass to the python code.

ClassMethod KWArgs()
{
    set kwargs={ "name": "ashok", "city": "chennai"}
    do ..pyKWArgs(kwargs...)
}

ClassMethod pyKWArgs(name, city, dob = "") [ Language = python ]
{
    print(name, city, dob)
}

// output
LEARNING>do ##class(MyLearn.EmbeddedPythonUtils).KWArgs()
ashok chennai

I will cover the global, routines and SQL in next article

Discussion (1)1
Log in or sign up to continue