Embedded python in InterSystems IRIS Part-2
In the previous article. Practices of class members and their execution within embedded Python. We will now turn our attention to the process of switching namespaces, accessing global variables , traversing and routine executions within embedded Python.
Before proceeding to the other functions. let us briefly review the execute function within the iris package. This function is exceptionally beneficial for executing the arbitrary ObjectScript functions and class invocation.
>>> b
'aaaa'
>>> b = iris.execute('return $Extract("123456",2,5)')
>>> b
'2345'
>>> b = iris.execute('return $Length(123456)')
>>> iris.execute('write ##Class(%SYSTEM.SYS).NameSpace()')
LEARNING>>>
>>> b = iris.execute('return ##Class(%SYSTEM.SYS).NameSpace()')
>>> b
'LEARNING'
Let us begin!
4. Switch Namespaces
Switching namespaces during execution is often necessary. However, unlike in IRIS, direct switching of namespaces within embedded Python is not feasible. Therefore, it is essential to utilize existing class definitions or to create a wrapper method to facilitate the switching of namespaces.
ClassMethod SwitchNM() [ Language = python ]
{
import iris
print(iris.cls('%SYSTEM.SYS').NameSpace())
print(iris.system.Process.SetNamespace("USER"))
try:
iris.cls('User.EmbeddedPython').pyGetTemplateString()
except RuntimeError as e:
print("Wrong NameSpace",e)
}
5. Global
To utilize the capabilities of global for data traversal or to retrieve information from legacy global systems directly, rather than through SQL or objects within embedded Python, one can access it directly by employing the gref function from the iris package. To set or get global values, the gref function can be utilized to establish a reference to the global variable and directly assign values within Python.
iris.gref
| InterSystems IRIS global reference object.
| Use the iris.gref() method to obtain a reference to a global
|
| Methods defined here:
|
| __delitem__(self, key, /)
| Delete self[key].
|
| __getitem__(self, key, /)
| Return self[key].
|
| __len__(self, /)
| Return len(self).
|
| __repr__(self, /)
| Return repr(self).
|
| __setitem__(self, key, value, /)
| Set self[key] to value.
|
| __str__(self, /)
| Return str(self).
|
| data(self, key)
| Given the keys of a global as a list, returns the state of that.
| Example: x = g.data([i,j]) sets x to 0,1,10,11 0-if undefined, 1-defined, 10-undefined but has descendants, 11-has value and descendants
|
| get(self, key)
| Given the keys of a global as a list, returns the value stored at that node of the global.
| Example: x = g.get([i,j]) sets x to the value stored at key i,j of global g.
|
| getAsBytes(self, key)
| Given the keys of a global as a list, returns a string stored at that node of the global, as bytes.
| Example: x = g.getAsBytes([i,j]) sets x to the value stored at key i,j of global g, as bytes.
|
| keys(self, key)
| Traverses a global starting at the specified key, returning each key in the global.
| Example: for key in g.keys([i, j]) traverses g from key i,j, returning each key in turn. Optional second argumenent 1 or -1, if -1 reverses the returned order
|
| kill(self, key)
| Given the keys of a global as a list, kills that node of the global and its subtree.
| Example: g.kill([i,j]) kills the node stored at key i,j of global g and any descendants.
|
| order(self, key)
| Given the keys of a global as a list, returns the next key of the global, optional second argument 1 or -1, if -1 returns the previous key.
| Example: j = g.order([i,j]) sets j to the next second-level key of global g.
|
| orderiter(self, key)
| Traverses a global starting at the specified key, returning the next key and value as a tuple.
| Example: for (key, value) in g.orderiter([i,j]) traverses g from key i,j, returning the next key and value. Optional second argumenent 1 or -1, if -1 reverses the returned order.
|
| query(self, key)
| Traverses a global starting at the specified key, returning each key and value as a tuple.
| Example: for (key, value) in g.query([i,j]) traverses g from key i,j, returning each key and value in turn. Optional second argumenent 1 or -1, if -1 reverses the returned order.
|
| set(self, key, value)
| Given the keys of a global as a list, sets the value stored at that key of the global.
| Example: g.set([i,j], 10) sets the value of the node at key i,j of global g to 10
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
5.1 Set global values
ClassMethod SetGlobal() [ Language = python ]
{
import iris
#create a global reference
g = iris.gref('^mygbl')
g[1],g[2]='Mon','Tue'
g["95752455",1]=iris.execute('return $LFS("Ashok,55720,9639639639,test@gmail.com",",")')
g["85752400",1]=iris.execute('return $LB("Test","9517539635","t@gmail.com")')
g["test","c1"]=iris.execute('return ##Class(MyLearn.EmbeddedPython).executeAndGetResult()') # method wil return some listbuild values# declare values by using set function
g.set([3],'Wed')
g.set([3,1,1],'Test multilevel')
}
5.2 get global values
Fetch the global values from python directly by using the subscripts or get method.
ClassMethod GetGlobal() [ Language = python ]
{
import iris
#gets a global reference
g = iris.gref('^mybgl')
# get values
print(g[3,1,1])
print(g.get([2,1]))
print(g["95752455",1])
}
5.3 Traversal
order - Traversing the global is essential for collecting multiple level of data's from global. This embedded Python order functions similarly to the $Order command, utilizing the order function from the iris.gref. Initially, it is necessary to establish a reference to the global entity that requires traversal.
Single subscript level traversal
ClassMethod DollarOrder() [ Language = python ]
{
import iris
g = iris.gref('^MyLearn.EmbeddedPythonD') # I use my persistent class global
key = ''
while True:
key = g.order([key])
if key == None:
breakprint(f'{key} {g.get([key])}')
}Multi subscript level traversal
global
^mygbl(1)="Mon"
^mygbl(2)="Tue"
^mygbl(3)="Wed"
^mygbl(3,1,1)="Test multilevel"
^mygbl(85752400,1)=$lb("Test","9517539635","t@gmail.com")
^mygbl(95752455,1)=$lb("Ashok","55720","9639639639","test@gmail.com")
^mygbl("test","c1")=$lb("Test","8527538521","pylearn@gmail.com")
ClassMethod DollarOrderMultiLevel() [ Language = python ]
{
import iris
g = iris.gref('^mygbl')
key1= ''whileTrue:
key1 = g.order([key1])
if key1== None:
break
key2 = ''whileTrue:
key2 = g.order([key1,key2])
if key2 == None:
break
value = g.get([key1,key2])
print(key1,key2,value)
}
query - query function from the iris.gref similarly $query. This function is collects all the global values in to tuples. the tuple result contains the id(s) in list and values is the next tuple. You can refer the below tuple sample
tuple
zw ^mybgl
^mybgl(1)="Mon"
^mybgl(2)="Tue"
^mybgl(3)="Wed"
^mybgl(3,1,1)="Test multilevel"
^mybgl(95752455,1)=$lb("Ashok","55720","9639639639","test@gmail.com")
Python tuple : ( [ids], data)
(['1'], 'Mon')
(['2'], 'Tue')
(['3'], 'Wed')
(['3', '1', '1'], 'Test multilevel')
(['95752455', '1'], '\x07\x01Ashok\x07\x0155720\x0c\x019639639639\x10\x01test@gmail.com')
ClassMethod DollarQuery() [ Language = python ]
{
import iris
g = iris.gref('^mygbl')
key = g.query()#this will return tuples of all the subscriptsfor x in key:
print(x) # output (['3', '1', '1'], 'Test multilevel')
}data - this data function Check whether the given subscript is exist in the global and return the $data values by using the data function
ClassMethod DollarData() [ Language = python ]
{
import iris
g = iris.gref('^mygbl')
key1= ''
print(g.data([1]))
}
6. Routines
Furthermore, it is essential to implement the class members. We must execute the routines as part of the implementation for legacy codebase systems and other related situations. Consequently, there exists a dedicated function within the iris library package that allows for the invocation of routines from embedded python through the use of the routine function.
myirispython.mac
w 123
q
ir1
w "running ir1"
q
add(p1,p2) public{
return p1+p2
}
sub(p1,p2)
s c= p1-p2
ClassMethod RunRoutines() [ Language = python ]
{
import iris
iris.routine('^myirispython')
iris.routine('add^myirispython',1,2) # same aswrite$$add^myirispython(1,2)
}
Additionally, you can execute the routine by using execute function as well. iris.execute('do ^myirispython')
>>> iris.routine('^myirispythonT')
Traceback (most recent call last):
File "<input>", line 1, in <module>
RuntimeError: Routine not found
Will continue the other topics in the next article.
Comments
Great articles. Thanks for sharing.
Why are you using Language Tag and not "pure" python script ? It's so much convient to work with native python script than python code in cls classes.
Hello @Guillaume Rongier
Thanks for the feedback! I go over your pretty useful article. I just write python code inside the ObjectScript itself by using language mode because of it's small code snippets. I actually facing some issues while writing IRIS in native python script.
From my pervious community question. First I install this intersystems_irispython-3.2.0-py3-none-any.whl in python and there is no cls, execute, routine, gref, ref or other IRIS script functions available.
As you recommend from the post. I install the official driver https://github.com/intersystems-community/intersystems-irispython/releases/download/3.8.0/intersystems_iris-3.8.0-py3-none-any.whl file and I could use the IRIS functions for embedded python cls, execute, routine, gref, ref etc...
However, I got this ImportError: DLL load failed while importing pythonint: The specified module could not be found." error while executing the .py scripterror while executing my script
script is nothing but simple class method invocation.
import iris
defExecute_Classmethod():
print(iris.cls('MyLearn.EmbeddedPython').test1())
Execute_Classmethod()
anyway when I keep my native python scripts under "IRISinstalldirectory\mgr\python" and import my code as module and it's working because it's running inside the IRIS not using the python "driver"
ClassMethod CallPyscripts()
{
set ap = "mypyap"set pyImport = ##class(%SYS.Python).Import(ap)
set builtin = $SYSTEM.Python.Builtins()
do builtin.help(pyImport)
write pyImport.irisversion,!
write pyImport."Execute_Classmethod1"()
}#__init__.pyimport iris
from .irisembdpyth2024 import *
irisversion = iris.execute('return $zv')
# irisembdpyth2024.py fileimport iris
defExecute_Classmethod1():
print(iris.cls('MyLearn.EmbeddedPython').test1())Hello Ashok, this is a great article, thanks for sharing it!
However, I would like to ask how to create an oref using a property index. For example, considering the class:
Class User.Test Extends%Persistent
{
Property Code As%String(MAXLEN = 50) [ Required ];
Index CodeIdx On CodeIdx [ Unique ];
}In ObjectScript I can use the following syntax to open the oref using its ID:
set obj = ##class(User.Test).CodeIdxOpen(ID)In the same way I can use the CodeIdxDelete or CodeIdxExists as well.
I was wondering how to obtain the same result in Embedded Python.
Hello @Pietro Di Leo
AFAIK, There is no direct execution of the IndexKeyOpen and other generated methods in python. We can write a wrapper method to run those methods.
Class pylearn.NewClass2 Extends%Persistent
{
Property Code As%String(MAXLEN = 50) [ Required ];
Index CodeIdx On Code [ Unique ];ClassMethod RunCode() [ Language = python ]
{
import iris
iris_obj = iris.cls(__name__).GeObjByIdx("CodeIdx","A212")
print(iris_obj.Code)
}
ClassMethod GeObjByIdx(IndexName, ID)
{
Return$ClassMethod(,IndexName_"Open",ID)
}
}Thanks for the clarification Ashok! I solved in a similar way. It would be good in a future version to have the possibility of executing generated methods too.