Skip to main content

Calling Database Methods and Functions from Python

This section describes methods of class iris.IRIS that allow you to call ObjectScript class methods and functions directly from your Python application. See the following sections for details and examples:

Calling Class Methods from Python

The classMethodValue() and classMethodVoid() methods will work for most purposes, but if a specific return type is needed, the following IRIS.classMethodValue() typecast methods are also available: classMethodBoolean(), classMethodBytes(), classMethodDecimal(), classMethodFloat(), classMethodIRISList(), classMethodInteger(), classMethodObject(), and classMethodString().

These methods all take string arguments for class_name and method_name, plus 0 or more method arguments.

The code in the following example calls class methods of several datatypes from an ObjectScript test class named User.NativeTest. (see listing “ObjectScript Class User.NativeTest” at the end of this section).

Python calls to ObjectScript class methods

The code in this example calls class methods of each supported datatype from ObjectScript test class User.NativeTest (listed immediately after this example). Assume that variable irispy is a previously defined instance of class iris.IRIS and is currently connected to the server (see “Creating a Connection in Python”).

  className = 'User.NativeTest'

  comment = ".cmBoolean() tests whether arguments 2 and 3 are equal: "
  boolVal = irispy.classMethodBoolean(className,'cmBoolean',2,3)
  print(className + comment + str(boolVal))

  comment = ".cmBytes returns integer arguments 72,105,33 as a byte array (string value 'Hi!'): "
  byteVal = irispy.classMethodBytes(className,'cmBytes',72,105,33) #ASCI 'Hi!'
  print(className + comment + str(byteVal))

  comment = ".cmString() concatenates 'Hello' with argument string 'World': "
  stringVal = irispy.classMethodString(className,'cmString','World')
  print(className + comment + stringVal)

  comment = ".cmLong() returns the sum of arguments 7+8: "
  longVal = irispy.classMethodInteger(className,'cmLong',7,8)
  print(className + comment + str(longVal))

  comment = ".cmDouble() multiplies argument 4.5 by 1.5: "
  doubleVal = irispy.classMethodFloat(className,'cmDouble',4.5)
  print(className + comment + str(doubleVal))

  comment = ".cmList() returns a $LIST containing arguments 'The answer is ' and 42: "
  listVal = irispy.classMethodIRISList(className,"cmList","The answer is ",42);
  print(className + comment+listVal.get(1)+str(listVal.get(2)))

  comment = ".cmVoid assigns argument value 75 to global node ^cmGlobal: "
  try:
    irispy.kill('cmGlobal') # delete ^cmGlobal if it exists
    irispy.classMethodVoid(className,'cmVoid',75)
    nodeVal = irispy.get('cmGlobal');  #get current value of ^cmGlobal
  except:
    nodeVal = 'FAIL'
  print(className + comment + str(nodeVal))

This example omits classMethodValue() (which returns an untyped value), classMethodDecimal() (which differs from classMethodFloat() primarily in support for higher precision), and classMethodObject() (which is demonstrated in “Controlling Database Objects with Python”).

ObjectScript Class User.NativeTest

To run the previous example, this ObjectScript class must be compiled and available on the server:


Class User.NativeTest Extends %Persistent
  {

  ClassMethod cmBoolean(cm1 As %Integer, cm2 As %Integer) As %Boolean
  {
     Quit (cm1=cm2)
  }

  ClassMethod cmBytes(cm1 As %Integer, cm2 As %Integer, cm3 As %Integer) As %Binary
  {
     Quit $CHAR(cm1,cm2,cm3)
  }

  ClassMethod cmString(cm1 As %String) As %String
  {
     Quit "Hello "_cm1
  }

  ClassMethod cmLong(cm1 As %Integer, cm2 As %Integer) As %Integer
  {
     Quit cm1+cm2
  }

  ClassMethod cmDouble(cm1 As %Double) As %Double
  {
     Quit cm1 * 1.5
  }

  ClassMethod cmVoid(cm1 As %Integer)
  {
     Set ^cmGlobal=cm1
     Quit
  }

  ClassMethod cmList(cm1 As %String, cm2 As %Integer)
  {
     Set list = $LISTBUILD(cm1,cm2)
     Quit list
  }
}

You can test these methods by calling them from the Terminal. For example:

USER>write ##class(User.NativeTest).cmString("World")
Hello World

Calling Functions and Procedures from Python

Note:
Procedural Code Support

On older InterSystems database platforms, code consisted of modules containing functions and procedures, rather than object-oriented classes and methods (see “Callable User-defined Code Modules” in Using ObjectScript). Functions are frequently necessary for older code bases, but new code should use object oriented method calls if possible.

The function() and procedure() methods will work for most purposes, but if a specific return type is needed, the following IRIS.function() typecast methods are also available: functionBoolean(), functionBytes(), functionDecimal(), functionFloat(), functionIRISList(), functionObject(), functionInteger(), and functionString().

These methods take string arguments for functionLabel and routineName, plus 0 or more function arguments, which may be bool, bytes, bytearray, Decimal, float, int, str or IRISList.

The code in the following example calls functions from an ObjectScript test routine named NativeRoutine (listed immediately after the example).

Note:
Built-in ObjectScript $ system functions are not supported

These methods are designed to call functions in user-defined routines. ObjectScript system functions (which start with a $ character. See “ObjectScript Functions” in the ObjectScript Reference) cannot be called directly from your Python code. However, you can call a system function indirectly by writing an ObjectScript wrapper function that calls the system function and returns the result. For example, the fnList() function (at the end of this section in ObjectScript Routine NativeRoutine.mac) calls $LISTBUILD.

Python calls to ObjectScript routines

The code in this example calls functions of each supported datatype from the ObjectScript routine NativeRoutine (File NativeRoutine.mac, listed immediately after this example). Assume that irispy is an existing instance of class iris.IRIS, and is currently connected to the server (see “Creating a Connection in Python”).

  routineName = 'NativeRoutine'

  comment = ".fnBoolean() tests whether arguments 2 and 3 are equal: "
  boolVal = irispy.functionBoolean('fnBoolean',routineName,2,3)
  print(routineName + comment + str(boolVal))

  comment = ".fnBytes returns integer arguments 72,105,33 as a byte array (string value 'Hi!'): "
  byteVal = irispy.functionBytes('fnBytes',routineName,72,105,33)  #ASCI 'Hi!'
  print(routineName + comment + str(byteVal))

  comment = ".fnString() concatenates 'Hello' with argument string 'World': "
  stringVal = irispy.functionString("fnString",routineName,"World")
  print(routineName + comment + stringVal)

  comment = ".fnLong() returns the sum of arguments 7+8: "
  longVal = irispy.functionInteger('fnLong',routineName,7,8)
  print(routineName + comment + str(longVal))

  comment = ".fnDouble() multiplies argument 4.5 by 1.5: "
  doubleVal = irispy.functionFloat('fnDouble',routineName,4.5)
  print(routineName + comment + str(doubleVal))

  comment = ".fnList() returns a $LIST containing arguments 'The answer is ' and 42: "
  listVal = irispy.functionIRISList("fnList",routineName,"The answer is ",42);
  print(routineName + comment + listVal.get(1)+str(listVal.get(2)));

  comment = ".fnProcedure() assigns argument value 66 to global node ^fnGlobal: "
  try:
    irispy.kill('fnGlobal') # delete ^fnGlobal if it exists
    irispy.procedure('fnProcedure',routineName,66)
    nodeVal = irispy.get('fnGlobal') # get current value of node ^fnGlobal
  except:
    nodeVal = 'FAIL'
  print(routineName + comment + str(nodeVal))

This example omits function() (which returns an untyped value), functionDecimal() (which differs from functionFloat() primarily in support for higher precision), and functionObject() (functionally identical to the classMethodObject() method demonstrated in “Controlling Database Objects with Python”).

ObjectScript Routine NativeRoutine.mac

To run the previous example, this ObjectScript routine must be compiled and available on the server:

  fnBoolean(fn1,fn2) public {
     quit (fn1=fn2)
  }
  fnBytes(fn1,fn2,fn3) public {
      quit $CHAR(fn1,fn2,fn3)
  }
  fnString(fn1) public {
      quit "Hello "_fn1
  }
  fnLong(fn1,fn2) public {
      quit fn1+fn2
  }
  fnDouble(fn1) public {
      quit fn1 * 1.5
  }
  fnProcedure(fn1) public {
      set ^fnGlobal=fn1
      quit
  }
  fnList(fn1,fn2) public {
      set list = $LISTBUILD(fn1,fn2)
      quit list
  }

You can test these functions by calling them from the Terminal. For example:

USER>write $$fnString^NativeRoutine("World")
Hello World

Passing Arguments by Reference

Most of the classes in the InterSystems Class Library use a calling convention where methods only return a %Status value. The actual results are returned in arguments passed by reference. The Native SDK supports pass by reference for both methods and functions by assigning the argument value to an instance of class IRISReference and passing that instance as the argument:

  ref_object = iris.IRISReference(None); // set inital value to None
  irispy.classMethodObject("%SomeClass","SomeMethod",ref_object);
  returned_value = ref_object.getValue;  // get the method result

The following example calls a standard Class Library method:

Using pass-by-reference arguments

This example calls %SYS.DatabaseQuery.GetDatabaseFreeSpace() to get the amount of free space (in MB) available in the iristemp database.

  dir = "C:/InterSystems/IRIS/mgr/iristemp" # directory to be tested
  value = "error"
  status = 0

  freeMB = iris.IRISReference(None) # set inital value to 0
  print("Variable freeMB is type"+str(type(freeMB)) + ", value=" + str(freeMB.getValue()))
  try:
    print("Calling %SYS.DatabaseQuery.GetDatabaseFreeSpace()... ")
    status = irispy.classMethodObject("%SYS.DatabaseQuery","GetDatabaseFreeSpace",dir,freeMB)
    value = freeMB.getValue()
  except:
    print("Call to class method GetDatabaseFreeSpace() returned error:")
  print("(status=" + str(status) + ") Free space in " + dir + " = " + str(value) + "MB\n")

prints:

Variable freeMB is type<class 'iris.IRISReference'>, value=None
Calling %SYS.DatabaseQuery.GetDatabaseFreeSpace()...
(status=1) Free space in C:/InterSystems/IRIS/mgr/iristemp = 10MB
Feedback