Initializing InterSystems IRIS Docker Image with Python
Tags: docker, python, embedded, tutorial
Published: 2023-10-16 16:34:12

While starting the development with IRIS we have a distribution kit or in case of Docker we are pulling the docker image and then often we need to initialize it and setup the development environment. We might need to create databases, namespaces, turn on/off some services, create resources. We often need to import code and data into IRIS instance and run some custom code to init the solution.

<img alt="Lajos Simicska declares war on Viktor Orban: "It's either him or me!" - The Budapest Beacon" src="https://budapestbeacon.com/wp-content/uploads/2015/02/there-can-be-only-one.jpg">

And there plenty of templates on Open Exchange where we suggest how to init REST, Interoperability, Analytics, Fullstack and [many other templates](https://openexchange.intersystems.com/?categories=Template) with ObjectScript. What if we want to use only Python to setup the development environment for Embedded Python project with IRIS?

So, the recent release of [Embedded Python template](https://openexchange.intersystems.com/package/iris-embedded-python-template) is the pure python boilerplate that could be a starting point for developers that build python projects with no need to use and learn ObjectScript. This article expresses how this template could be used to initialize IRIS. Here we go!

Ok!

**Creating Database and Namespace**

Often we need to create database and namespace. This could be done with merge cpf functionality of IRIS, and here is the merge file that does the thing:

<pre>[Actions]

CreateResource:Name=%DB_IRISAPP_DATA,Description="IRISAPP_DATA database"

CreateDatabase:Name=IRISAPP_DATA,Directory=/usr/irissys/mgr/IRISAPP_DATA

CreateResource:Name=%DB_IRISAPP_CODE,Description="IRISAPP_CODE database"

CreateDatabase:Name=IRISAPP_CODE,Directory=/usr/irissys/mgr/IRISAPP_CODE

CreateNamespace:Name=IRISAPP,Globals=IRISAPP_DATA,Routines=IRISAPP_CODE,Interop=1

ModifyService:Name=%Service_CallIn,Enabled=1,AutheEnabled=48

ModifyUser:Name=SuperUser,PasswordHash=a31d24aecc0bfe560a7e45bd913ad27c667dc25a75cbfd358c451bb595b6bd52bd25c82cafaa23ca1dd30b3b4947d12d3bb0ffb2a717df29912b743a281f97c1,0a4c463a2fa1e7542b61aa48800091ab688eb0a14bebf536638f411f5454c9343b9aa6402b4694f0a89b624407a5f43f0a38fc35216bb18aab7dc41ef9f056b1,10000,SHA512</pre>

Merge cpf contains Actions which in this case create dabasases IRISAPP\_DATA and IRISAPP\_CODE for data and code respectively, the related IRISAPP namespace to access it and the related resources %IRISAPP\_DATA and %IRISAPP\_CODE" to manage the access.

This Merge cpf also enables Callin service to make Embedded python work via ModifyService action:

<pre>ModifyService:Name=%Service_CallIn,Enabled=1,AutheEnabled=48</pre>

The last action is an example how you can securely set user's password, using ModifyUser action:

<pre>ModifyUser:Name=SuperUser,PasswordHash=a31d24aecc0bfe560a7e45bd913ad27c667dc25a75cbfd358c451bb595b6bd52bd25c82cafaa23ca1dd30b3b4947d12d3bb0ffb2a717df29912b743a281f97c1,0a4c463a2fa1e7542b61aa48800091ab688eb0a14bebf536638f411f5454c9343b9aa6402b4694f0a89b624407a5f43f0a38fc35216bb18aab7dc41ef9f056b1,10000,SHA512</pre>

The password hash here can be generated via another docker image from InterSystems, e.g. with the following line called in a terminal ([learn more about passwordhash](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_PasswordHash#:~:text=b%20is%20salt.-,Description,a%20new%20tab%20(SYS).)):

<pre>docker run --rm -it containers.intersystems.com/intersystems/passwordhash:1.1 -algorithm SHA512 -workfactor 10000</pre>

merge command is could be called in [Dockerfile like this](https://github.com/intersystems-community/iris-embedded-python-template/blob/master/Dockerfile#L24):

<pre>iris merge IRIS merge.cpf && \</pre>

Other types of merge actions and different settings available for IRIS [could be found in documentation.](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_cpf#RACS_cpf_edit_merge)

**Running an Arbitrary Init Code**

Another popular use case is to run an arbitrary custom code to load data, init variables and load code from the repository e.g. in a form of IPM package. Usually in IRIS templates there is a special file iris.script that contains such a code in form of ObjectScript. I kept the [iris.script file](https://github.com/intersystems-community/iris-embedded-python-template/blob/4c12d4b02770c7422c7553ee818a18c1871c3759/iris.script) in the repository just to demo how it have been used with ObjectScript. 

In this python template you can find the example of such init code in a [iris_script.py](https://github.com/intersystems-community/iris-embedded-python-template/blob/4c12d4b02770c7422c7553ee818a18c1871c3759/iris_script.py) file. Let's see what's there:

<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs"><span class="hljs-keyword">import</span> glob

<span class="hljs-keyword">import</span> os

<span class="hljs-keyword">import</span> iris

<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine

<span class="hljs-keyword">from</span> iris <span class="hljs-keyword">import</span> ipm

<span class="hljs-comment"># switch namespace to the %SYS namespace</span>
iris.system.Process.SetNamespace(<span class="hljs-string">"%SYS"</span>)

<span class="hljs-comment"># set credentials to not expire</span>
iris.cls(<span class="hljs-string">'Security.Users'</span>).UnExpireUserPasswords(<span class="hljs-string">"*"</span>)

<span class="hljs-comment"># switch namespace to IRISAPP built by merge.cpf</span>
iris.system.Process.SetNamespace(<span class="hljs-string">"IRISAPP"</span>)

<span class="hljs-comment"># load ipm package listed in module.xml</span>
<span class="hljs-comment">#iris.cls('%ZPM.PackageManager').Shell("load /home/irisowner/dev -v")</span>
<span class="hljs-keyword">assert</span> ipm(<span class="hljs-string">'load /home/irisowner/dev -v'</span>)

<span class="hljs-comment"># load demo data</span>
engine = create_engine(<span class="hljs-string">'iris+emb:///'</span>)

<span class="hljs-comment"># list all csv files in the demo data folder</span>
<span class="hljs-keyword">for</span> files <span class="hljs-keyword">in</span> glob.glob(<span class="hljs-string">'/home/irisowner/dev/data/*.csv'</span>):

<span class="hljs-comment"># get the file name without the extension</span>
table_name = os.path.splitext(os.path.basename(files))[<span class="hljs-number">0</span>]

<span class="hljs-comment"># load the csv file into a pandas dataframe</span>
df = pd.read_csv(files)

<span class="hljs-comment"># write the dataframe to IRIS</span>
df.to_sql(table_name, engine, if_exists=<span class="hljs-string">'replace'</span>, index=<span class="hljs-keyword">False</span>, schema=<span class="hljs-string">'dc_demo'&</span>)</code></pre>

The code is rather self-explanatory and it has the demo of changing namespaces, loading repo code in form of IPM module, importing csv files that contained in /data folder of the repo using sqlalchemy lib.

**What Does the Template do?**

Basically this template provides examples how can  Embedded Python by used with IRIS. 

**1. Calling Python From ObjectScript**

To demo it we have [several ObjectScript classes in the repo](https://github.com/intersystems-community/iris-embedded-python-template/blob/54a2d09d3d3dc7b7aa73b18fe66c91ff48a8bf39/src/dc/python).

To test the approach open IRIS terminal and run:

<pre>IRISAPP>d ##class(dc.python.test).Hello()

World</pre>

this test demoes how you can use functions in [sample.py](https://github.com/intersystems-community/iris-embedded-python-template/blob/54a2d09d3d3dc7b7aa73b18fe66c91ff48a8bf39/python/sample.py) code in IRIS using Embedded Python.

**2. Refer to IRIS from python**

To test this you can run [irisapp.py](https://github.com/intersystems-community/iris-embedded-python-template/blob/0e71f56278cb9930ea48fb04a8b28d63aff87ca0/python/irisapp.py) that performs different operations with IRIS database. To run all the test start terminal session in docker:

<pre># attach to the running IRIS container

docker-compose exec iris bash

# run the script

$ irispython ./python/irisapp.py</pre>

**3. Building Python Flask REST API that works with IRIS**

There is also an example of REST api in python made with Flask that you could use in your applications that you can find in [/python/flask/app.py](https://github.com/intersystems-community/iris-embedded-python-template/blob/4c12d4b02770c7422c7553ee818a18c1871c3759/python/flask/app.py). 

To start Flask REST api do:

<pre>$ docker-compose exec iris bash

# irispython /python/flask/app.py</pre>

Then you can test the server at http://localhost:55030/.

And here are two examples on how to add a new record in IRIS and read a record:

<pre>POST http://localhost:55030/persistentclass

Content-Type: application/json

Accept: application/json

{

"test": "toto"

}

Result:

json

{

"id": 1,

"test": "toto"

}


GET http://localhost:55030/persistentclass/1

Accept: application/json

Result:

json

{

"id": 1,

"test": "toto"

}</pre>

**4. Deployment**

The deployment of the solution to IRIS systems can be performed via IPM. For that purpose we have an IPM [module.xml](https://github.com/intersystems-community/iris-embedded-python-template/blob/c8b16dbe81d2e9b26199cb92dda95263e6993f00/module.xml) that will deliver ObjectScript and Python files to a target IRIS system from an IPM Registry you will publish module at.

Thanks a lot to @Guillaume.Rongier7183<span> and @Dmitry.Maslennikov<span> who helped a lot to create the template and make "Pure Python" approach possible.</span></span>

There are more "radical" approaches in organizing such templates - here is the "no dockerfile" and "no objedctscript" sample by @Dmitry.Maslennikov<span> ,</span>

<span>here is "embedded python" template by @Guillaume.Rongier7183.</span>