Using Embedded Python
Embedded Python allows you to use Python as a native option for programming InterSystems IRIS applications. If you are new to Embedded Python, read Demo: Using Embedded Python, and then read this document for a deeper dive into Embedded Python.
While this document will be helpful to anyone who is learning Embedded Python, some level of ObjectScript familiarity will be beneficial to the reader. If you are a Python developer who is new to InterSystems IRIS and ObjectScript, also see the Orientation Guide for Server-Side Programming.
Prerequisites
The version of Python required to use Embedded Python depends on the platform you are running. In most cases, this is the default version of Python for your operating system. See Other Supported Features for a complete list of operating systems and the corresponding supported version of Python. Using a different version of Python will result in errors when using Embedded Python.
On Microsoft Windows, the InterSystems IRIS installation kit installs the correct version of Python (currently 3.9.5) for use with Embedded Python only. If you are on a development machine and want to use Python for general purposes, InterSystems recommends downloading and installing this same version from https://www.python.org/downloads/Opens in a new tab.
Many flavors of UNIX-based operating systems come with Python installed. If you need to install it, use the version recommended for your operating system by your package manager, for example:
-
macOS: Install Python 3.9 using Homebrew (https://formulae.brew.sh/formula/python@3.9Opens in a new tab)
-
Ubuntu: apt-get install python3
-
Red Hat Enterprise Linux or Oracle Linux: yum install python3
-
SUSE: zypper install python3
If you get an error that says “Failed to load python,” it means that you either don’t have Python installed or an unexpected version of Python is detected on your system. Check Other Supported Features and make sure you have the required version of Python installed, and if necessary, install it or reinstall it using one of the above methods.
If your computer has multiple versions of Python installed and you try to run Embedded Python from the command line, irispython will run the first python3 executable that it detects, as determined by your path environment variable. Make sure that the folders in your path are set appropriately so that the correct version of the executable is the first one found. For more information on using the irispython command, see Start the Python Shell from the Command Line.
On a UNIX-based system, you may want to install Python packages with the pip3 command. If you do not have pip3 installed already, install the package python3-pip with your system’s package manager.
To prevent IRIS_ACCESSDENIED errors while running Embedded Python, enable %Service_Callin. In the Management Portal, go to System Administration > Security > Services, select %Service_CallIn, and check the Service Enabled box.
Run Embedded Python
The section details several ways to run Embedded Python:
All of these ways allow you to call InterSystems IRIS APIs by importing the iris Python module and using its methods.
From the Python Shell
You can start the Python shell from an InterSystems Terminal session or from the command line.
Start the Python Shell from Terminal
Start the Python shell from an InterSystems Terminal session by calling the Shell() method of the %SYS.PythonOpens in a new tab class. This launches the Python interpreter in interactive mode. The user and namespace from the Terminal session are passed to the Python shell.
Exit the Python shell by typing the command quit().
The following example launches the Python shell from the USER namespace in a Terminal session. It prints the first few numbers in the Fibonacci sequence and then uses the InterSystems IRIS %SYSTEM.OBJ.ShowClasses() method to print a list of classes in the current namespace.
USER>do ##class(%SYS.Python).Shell()
Python 3.9.5 (default, Jul 6 2021, 13:03:56) [MSC v.1927 64 bit (AMD64)] on win32
Type quit() or Ctrl-D to exit this shell.
>>> a, b = 0, 1
>>> while a < 10:
... print(a, end=' ')
... a, b = b, a+b
...
0 1 1 2 3 5 8 >>>
>>> status = iris.cls('%SYSTEM.OBJ').ShowClasses()
User.Company
User.Person
>>> print(status)
1
>>> quit()
USER>
The method %SYSTEM.OBJ.ShowClasses() returns an InterSystems IRIS %StatusOpens in a new tab value. In this case, a 1 means that no errors were detected.
You do not need to import the iris module explicitly when running the Python shell using the Shell() method of the %SYS.PythonOpens in a new tab class. Just go ahead and use the module.
Start the Python Shell from the Command Line
Start the Python shell from the command line by using the irispython command. This works much the same as starting the shell from Terminal, but you must pass in the InterSystems IRIS username, password, and namespace.
The following example launches the Python shell from the Windows command line:
C:\InterSystems\IRIS\bin>set IRISUSERNAME = <username>
C:\InterSystems\IRIS\bin>set IRISPASSWORD = <password>
C:\InterSystems\IRIS\bin>set IRISNAMESPACE = USER
C:\InterSystems\IRIS\bin>irispython
Python 3.9.5 (default, Jul 6 2021, 13:03:56) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
On UNIX-based systems, use export instead of set.
/InterSystems/IRIS/bin$ export IRISUSERNAME=<username>
/InterSystems/IRIS/bin$ export IRISPASSWORD=<password>
/InterSystems/IRIS/bin$ export IRISNAMESPACE=USER
/InterSystems/IRIS/bin$ ./irispython
Python 3.9.5 (default, Jul 22 2021, 23:12:58)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
If you try to run import iris and see a message saying IRIS_ACCESSDENIED, enable %Service_Callin. In the Management Portal, go to System Administration > Security > Services, select %Service_CallIn, and check the Service Enabled box.
In a Python Script File (.py)
You can also use the irispython command to execute a Python script.
Consider a file C:\python\test.py, on a Windows system, containing the following code:
# print the members of the Fibonacci series that are less than 10
print('Fibonacci series:')
a, b = 0, 1
while a < 10:
print(a, end=' ')
a, b = b, a + b
# import the iris module and show the classes in this namespace
import iris
print('\nInterSystems IRIS classes in this namespace:')
status = iris.cls('%SYSTEM.OBJ').ShowClasses()
print(status)
You could run test.py from the command line, as follows:
C:\InterSystems\IRIS\bin>set IRISUSERNAME = <username>
C:\InterSystems\IRIS\bin>set IRISPASSWORD = <password>
C:\InterSystems\IRIS\bin>set IRISNAMESPACE = USER
C:\InterSystems\IRIS\bin>irispython \python\test.py
Fibonacci series:
0 1 1 2 3 5 8
InterSystems IRIS classes in this namespace:
User.Company
User.Person
1
On UNIX-based systems, use export instead of set.
/InterSystems/IRIS/bin$ export IRISUSERNAME=<username>
/InterSystems/IRIS/bin$ export IRISPASSWORD=<password>
/InterSystems/IRIS/bin$ export IRISNAMESPACE=USER
/InterSystems/IRIS/bin$ ./irispython /python/test.py
Fibonacci series:
0 1 1 2 3 5 8
InterSystems IRIS classes in this namespace:
User.Company
User.Person
1
If you try to run import iris and see a message saying IRIS_ACCESSDENIED, enable %Service_Callin. In the Management Portal, go to System Administration > Security > Services, select %Service_CallIn, and check the Service Enabled box.
In a Method in an InterSystems IRIS Class
You can write Python methods in an InterSystems IRIS class by using the Language keyword. You can then call the method as you would call a method written in ObjectScript.
For example, take the following class with a class method written in Python:
Class User.EmbeddedPython
{
/// Description
ClassMethod Test() As %Status [ Language = python ]
{
# print the members of the Fibonacci series that are less than 10
print('Fibonacci series:')
a, b = 0, 1
while a < 10:
print(a, end=' ')
a, b = b, a + b
# import the iris module and show the classes in this namespace
import iris
print('\nInterSystems IRIS classes in this namespace:')
status = iris.cls('%SYSTEM.OBJ').ShowClasses()
return status
}
}
You can call this method from ObjectScript:
USER>set status = ##class(User.EmbeddedPython).Test()
Fibonacci series:
0 1 1 2 3 5 8
InterSystems IRIS classes in this namespace:
User.Company
User.EmbeddedPython
User.Person
USER>write status
1
Or from Python:
>>> import iris
>>> status = iris.cls('User.EmbeddedPython').Test()
Fibonacci series:
0 1 1 2 3 5 8
InterSystems IRIS classes in this namespace:
User.Company
User.EmbeddedPython
User.Person
>>> print(status)
1
In SQL Functions and Stored Procedures
You can also write a SQL function or stored procedure using Embedded Python by specifying the argument LANGUAGE PYTHON in the CREATE statement, as is shown below:
CREATE FUNCTION tzconvert(dt TIMESTAMP, tzfrom VARCHAR, tzto VARCHAR)
RETURNS TIMESTAMP
LANGUAGE PYTHON
{
from datetime import datetime
from dateutil import parser, tz
d = parser.parse(dt)
if (tzfrom is not None):
tzf = tz.gettz(tzfrom)
d = d.replace(tzinfo = tzf)
return d.astimezone(tz.gettz(tzto)).strftime("%Y-%m-%d %H:%M:%S")
}
The code uses functions from the Python datetime and dateutil modules.
On some platforms, the datetime and dateutil modules may not be installed by default. If you run this example and get a ModuleNotFoundError, install the missing modules as described in Use a Python Library.
The following SELECT statement calls the SQL function, converting the current date/time from Eastern time to Coordinated Universal Time (UTC).
SELECT tzconvert(now(), 'US/Eastern', 'UTC')
The function returns something like:
2021-10-19 15:10:05
Call Embedded Python Code from ObjectScript
The section details several ways to call Embedded Python code from ObjectScript:
In some cases, you can call the Python code much the same way as you would call ObjectScript code, while sometimes you need to use the %SYS.PythonOpens in a new tab class to bridge the gap between the two languages. For more information, see Bridging the Gap Between ObjectScript and Embedded Python.
Use a Python Library
Embedded Python gives you easy access to thousands of useful libraries. Commonly called “packages,” these need to be installed from the Python Package Index (PyPIOpens in a new tab) into the <installdir>/mgr/python directory before they can be used.
For example, the ReportLab Toolkit is an open source library for generating PDFs and graphics. The following command uses the package installer irispip to install ReportLab on a Windows system:
C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python reportlab
On a UNIX-based system, use:
$ pip3 install --target /InterSystems/IRIS/mgr/python reportlab
After installing a package, you can use the Import() method of the %SYS.PythonOpens in a new tab class to use it in your ObjectScript code.
Given a file location, the following ObjectScript method, CreateSamplePDF(), creates a sample PDF file and saves it to that location.
Class Demo.PDF
{
ClassMethod CreateSamplePDF(fileloc As %String) As %Status
{
set canvaslib = ##class(%SYS.Python).Import("reportlab.pdfgen.canvas")
set canvas = canvaslib.Canvas(fileloc)
do canvas.drawImage("C:\Sample\isc.png", 150, 600)
do canvas.drawImage("C:\Sample\python.png", 150, 200)
do canvas.setFont("Helvetica-Bold", 24)
do canvas.drawString(25, 450, "InterSystems IRIS & Python. Perfect Together.")
do canvas.save()
}
}
The first line of the method imports the canvas.py file from the pdfgen subpackage of ReportLab. The second line of code instantiates a Canvas object and then proceeds to call its methods, much the way it would call the methods of any InterSystems IRIS object.
You can then call the method in the usual way:
do ##class(Demo.PDF).CreateSamplePDF("C:\Sample\hello.pdf")
The following PDF is generated and saved at the specified location:

Call a Method of an InterSystems IRIS Class Written in Python
You can write a method in an InterSystems IRIS class using Embedded Python and then call it from ObjectScript in the same way you would call a method written in ObjectScript.
The next example uses the usaddress-scourgify library, which can be installed from the command line on Windows as follows:
C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python usaddress-scourgify
On a UNIX-based system, use:
$ pip3 install --target /InterSystems/IRIS/mgr/python usaddress-scourgify
The demo class below contains properties for the parts of a U.S. address and a method, written in Python, that uses usaddress-scourgify to normalize an address according to the U.S. Postal Service standard.
Class Demo.Address Extends %Library.Persistent
{
Property AddressLine1 As %String;
Property AddressLine2 As %String;
Property City As %String;
Property State As %String;
Property PostalCode As %String;
Method Normalize(addr As %String) [ Language = python ]
{
from scourgify import normalize_address_record
normalized = normalize_address_record(addr)
self.AddressLine1 = normalized['address_line_1']
self.AddressLine2 = normalized['address_line_2']
self.City = normalized['city']
self.State = normalized['state']
self.PostalCode = normalized['postal_code']
}
}
Given a address string as input, the Normalize() instance method of the class normalizes the address and stores each part in the various properties of a Demo.Address object.
You can call the method as follows:
USER>set a = ##class(Demo.Address).%New()
USER>do a.Normalize("One Memorial Drive, 8th Floor, Cambridge, Massachusetts 02142")
USER>zwrite a
a=3@Demo.Address <OREF>
+----------------- general information ---------------
| oref value: 3
| class name: Demo.Address
| reference count: 2
+----------------- attribute values ------------------
| %Concurrency = 1 <Set>
| AddressLine1 = "ONE MEMORIAL DR"
| AddressLine2 = "FL 8TH"
| City = "CAMBRIDGE"
| PostalCode = "02142"
| State = "MA"
+-----------------------------------------------------
Run an SQL Function or Stored Procedure Written in Python
When you create a SQL function or stored procedure using Embedded Python, InterSystems IRIS projects a class with a method that can be called from ObjectScript as you would any other method.
For example, the SQL function from the example earlier in this document generates a class User.functzconvert, which has a tzconvert() method. Call it from ObjectScript as follows:
USER>zwrite ##class(User.functzconvert).tzconvert($zdatetime($h,3),"US/Eastern","UTC")
"2021-10-20 15:09:26"
Here, $zdatetime($h,3) is used to convert the current date and time from $HOROLOG format to ODBC date format.
Run an Arbitrary Python Command
Sometimes, when you are developing or testing Embedded Python code, it can be helpful to run an arbitrary Python command from ObjectScript. You can do this with the Run() method of the %SYS.PythonOpens in a new tab class.
Perhaps you want to test the normalize_address_record() function from the usaddress_scourgify package used earlier in this document, and you don’t remember how it works. You can use the %SYS.Python.Run() method to output the help for the function from the Terminal as follows:
USER>set rslt = ##class(%SYS.Python).Run("from scourgify import normalize_address_record")
USER>set rslt = ##class(%SYS.Python).Run("help(normalize_address_record)")
Help on function normalize_address_record in module scourgify.normalize:
normalize_address_record(address, addr_map=None, addtl_funcs=None, strict=True)
Normalize an address according to USPS pub. 28 standards.
Takes an address string, or a dict-like with standard address fields
(address_line_1, address_line_2, city, state, postal_code), removes
unacceptable special characters, extra spaces, predictable abnormal
character sub-strings and phrases, abbreviates directional indicators
and street types. If applicable, line 2 address elements (ie: Apt, Unit)
are separated from line 1 inputs.
.
.
.
The %SYS.Python.Run() method returns 0 on success or -1 on failure.
Using Embedded Python in Interoperability Productions
If you are writing custom business host classes or adapter classes for interoperability productions in InterSystems IRIS, any callback methods must be written in ObjectScript. A callback method is an inherited method that does nothing by default, but is designed to be implemented by the user. The ObjectScript code in a callback method can, however, make use of Python libraries or call other methods implemented in Python.
The following example shows a business operation that takes the string value from an incoming message and uses the Amazon Web Services (AWS) boto3 Python library to send that string to a phone in a text message via the Amazon Simple Notification Service (SNS). The scope of this AWS library is out of scope for this discussion, but you can see in the example that the OnInit() and OnMessage() callback methods are written in ObjectScript, while the methods PyInit() and SendSMS() are written in Python.
/// Send SMS via AWS SNS
Class dc.opcua.SMS Extends Ens.BusinessOperation
{
Parameter INVOCATION = "Queue";
/// AWS boto3 client
Property client As %SYS.Python;
/// json.dumps reference
Property tojson As %SYS.Python;
/// Phone number to send SMS to
Property phone As %String [ Required ];
Parameter SETTINGS = "phone:SMS";
Method OnMessage(request As Ens.StringContainer, Output response As Ens.StringContainer) As %Status
{
#dim sc As %Status = $$$OK
try {
set response = ##class(Ens.StringContainer).%New(..SendSMS(request.StringValue))
set code = +{}.%FromJSON(response.StringValue).ResponseMetadata.HTTPStatusCode
set:(code'=200) sc = $$$ERROR($$$GeneralError, $$$FormatText("Error sending SMS,
code: %1 (expected 200), text: %2", code, response.StringValue))
} catch ex {
set sc = ex.AsStatus()
}
return sc
}
Method SendSMS(msg As %String) [ Language = python ]
{
response = self.client.publish(PhoneNumber=self.phone, Message=msg)
return self.tojson(response)
}
Method OnInit() As %Status
{
#dim sc As %Status = $$$OK
try {
do ..PyInit()
} catch ex {
set sc = ex.AsStatus()
}
quit sc
}
/// Connect to AWS
Method PyInit() [ Language = python ]
{
import boto3
from json import dumps
self.client = boto3.client("sns")
self.tojson = dumps
}
}
The code in the OnMessage() method, above, contains an extra line break for better formatting when printing this document.
One exception to this rule is that you can implement a callback method in Python if it does not use the input from the adapter.
The following business service example is known as a poller. In this case, the business service can be set to run at intervals and generates a request (in this case containing a random string value) that is sent to a business process for handling. In this example the OnProcessInput() callback method can be implemented in Python because it does not make use of the pInput argument in the method’s signature.
Class Debug.Service.Poller Extends Ens.BusinessService
{
Property Target As Ens.DataType.ConfigName;
Parameter SETTINGS = "Target:Basic";
Parameter ADAPTER = "Ens.InboundAdapter";
Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject,
ByRef pHint As %String) As %Status [ Language = python ]
{
import iris
import random
fruits = ["apple", "banana", "cherry"]
fruit = random.choice(fruits)
request = iris.cls('Ens.StringRequest')._New()
request.StringValue = fruit + ' ' + iris.cls('Debug.Service.Poller').GetSomeText()
return self.SendRequestAsync(self.Target,request)
}
ClassMethod GetSomeText() As %String
{
Quit "is something to eat"
}
}
For more information on programming for interoperability productions, see Programming Business Services, Processes and Operations.