New post

Find

Article
· Jan 29 6m read

pyprod: Pure Python IRIS Interoperability

Intersystems IRIS Productions provide a powerful framework for connecting disparate systems across various protocols and message formats in a reliable, observable, and scalable manner. intersystems_pyprod, short for InterSystems Python Productions, is a Python library that enables developers to build these interoperability components entirely in Python. Designed for flexibility, it supports a hybrid approach: you can seamlessly mix new Python-based components with existing ObjectScript-based ones, leveraging your established IRIS infrastructure. Once defined, these Python components are managed just like any other; they can be added, configured, and connected using the IRIS Production Configuration page. 


A Quick Primer on InterSystems IRIS Productions

Key Elements of a Production

Image from Learning Services training material

An IRIS Production generally receives data from external interfaces, processes it through coordinated steps, and routes it to its destination. As messages move through the system, they are automatically persisted, making the entire flow fully traceable through IRIS’s visual trace and logging tools. The architecture relies on certain key elements:

  1. Business Hosts: These are the core building blocks—Services, Processes, and Operations—that pass persistable messages between one another.
  2. Adapters: Inbound and outbound adapters manage the interaction with the external world, handling the specific protocols needed to receive and send data.
  3. Callbacks: The engine uses specific callback methods to pass messages between hosts, either synchronously or asynchronously. These callbacks follow strict signatures and return a Status object to ensure execution integrity.
  4. Configuration Helpers: Objects such as Properties and Parameters expose settings to the Production Configuration UI, allowing users to easily instantiate, configure, and save the state of these components.

Workflow using pyprod

This is essentially a 3 step process.

  1. Write your production components in a regular Python script. In that script, you import the required base classes from intersystems_pyprod and define your own components by subclassing them, just as you would with any other Python library.
  2. Load them into InterSystems IRIS by running the intersystems_pyprod (same name as the library) command from the terminal and passing it the path to your Python script. This step links the Python classes with IRIS so that they appear as production components and can be configured and wired together using the standard Production Configuration UI. 
  3. Create the production using the Production Configuration page and start the Production

NOTE: If you create all your components with all their Properties hardcoded within the python script, you only need to add them to the production and start the Production. 

You can connect pyprod to your IRIS instance by doing a one time setup


Simple Example

In this example, we demonstrate a synchronous message flow where a request originates from a Service, moves through a Process, and is forwarded to an Operation. The resulting response then travels the same path in reverse, passing from the Operation back through the Process to the Service. Additionally, we showcase how to utilize the IRISLog utility to write custom log entries.

Step 1

Create your Production components using pyprod in the file HelloWorld.py

Here are some key parts of the code

  • Package Naming: We define iris_package_name, which prefixes all classes as they appear on the Production Configuration page (If omitted, the script name is used as the default prefix).
  • Persistable Messages: We define MyRequest and MyResponse. These are the essential data structures for communication, as only persistable objects can be passed between Services, Processes, and Operations.
  • The Inbound Adapter: Our adapter passes a string to the Service using the business_host_process_input method.
  • The Business Service: Implemented with the help of OnProcessInput callback.
    • MyService receives data from the adapter and converts it into a MyRequest message
    • We use the ADAPTER IRISParameter to link the Inbound Adapter to the Service. Note that this attribute must be named ADAPTER in all caps to align with IRIS conventions.
    • We define a target IRISProperty, which allows users to select the destination component directly via the Configuration UI.
  • The Business Process: Implemented with the help of OnRequest callback.
  • The Business Operation: Implemented with the help of OnMessage callback. (You can also define a MessageMap)
  • Logic & Callbacks: Finally, the hosts implement their core logic within standard callbacks like OnProcessInput and OnRequest, routing messages using the SendRequestSync method.

You can read more about each of these parts on the pyprod API Reference page and also using the Quick Start Guide.

import time

from intersystems_pyprod import (
    InboundAdapter,BusinessService, BusinessProcess, 
    BusinessOperation, OutboundAdapter, JsonSerialize, 
    IRISProperty, IRISParameter, IRISLog, Status)

iris_package_name = "helloworld"

class MyRequest(JsonSerialize):
    content: str

class MyResponse(JsonSerialize):
    content: str

class MyInAdapter(InboundAdapter):
    def OnTask(self):
        time.sleep(0.5)
        self.business_host_process_input("request message")
        return Status.OK()

class MyService(BusinessService):
    ADAPTER = IRISParameter("helloworld.MyInAdapter")
    target = IRISProperty(settings="Target")
    def OnProcessInput(self, input):
        persistent_message = MyRequest(input)
        status, response = self.SendRequestSync(self.target, persistent_message)
        IRISLog.Info(response.content)
        return status

class MyProcess(BusinessProcess):
    target = IRISProperty(settings="Target")
    def on_request(self, input):
        status, response = self.SendRequestSync(self.target,input)
        return status, response


class MyOperation(BusinessOperation):
    ADAPTER = IRISParameter("helloworld.MyOutAdapter")
    def OnMessage(self, input):
        status = self.ADAPTER.custom_method(input)
        response = MyResponse("response message")
        return status, response


class MyOutAdapter(OutboundAdapter):
    def custom_method(self, input):
        IRISLog.Info(input.content)
        return Status.OK()

 

Step 2

Once your code is ready, load the components to IRIS.

$ intersystems_pyprod /full/path/to/HelloWorld.py

    Loading MyRequest to IRIS...
    ...
    Load finished successfully.
    
    Loading MyResponse to IRIS...
    ...
    Load finished successfully.
    ...
    

Step 3

Add each host to the Production using the Production Configuration page.

The image below shows MyService and its target property being configured through the UI. Follow the same process to add MyProcess and MyOperation. Once the setup is complete, simply start the production to see your messages in motion.


Final Thoughts

By combining the flexibility of the Python ecosystem with the industrial-grade reliability of InterSystems IRIS, pyprod offers a modern path for building interoperability solutions. Whether you are developing entirely new "Pure Python" productions or enhancing existing ObjectScript infrastructures with specialized Python libraries, pyprod ensures your components remain fully integrated, observable, and easy to configure. We look forward to seeing what you build!


Quick Links

GitHub repository  

PyPi Package

Support the Project: If you find this library useful, please consider giving us a ⭐ on GitHub and suggesting enhancements. It helps the project grow and makes it easier for other developers in the InterSystems community to discover it!
Discussion (0)1
Log in or sign up to continue
Article
· Jan 29 2m read

临时文件与单例:自我清理

我曾多次遇到一种模式,即我需要使用临时文件/文件夹,并在稍后的某个时候将其清理掉。

在这种情况下,自然是遵循"Robust Error Handling and Cleanup in ObjectScript "中的模式,使用 try/catch/pseudo-finally 或注册对象来管理析构函数中的清理工作。%Stream.File*也有一个 "RemoveOnClose "属性,您可以对其进行设置,但要小心使用,因为您可能会不小心删除一个重要文件,而且这个标志会在调用%Save()时被重置,因此您需要在重置后将其设回 1。

不过,有一个棘手的情况——假设你需要临时文件在外部堆栈级别中继续存在。例如:

ClassMethod MethodA()
{
    Do ..MethodB(.filename)
    // Do something else with the filename
}

ClassMethod MethodB(Output filename)
{
    // Create a temp file and set filename to the file's name
    Set filename = ##class(%Library.File).TempFilename()
    
    //... and probably do some other stuff
}

你总是可以传递 RemoveOnClose 设置为 1 的 %Stream.File* 对象,但我们在这里讨论的其实只是临时文件。

这就是 "单例(Singleton)"概念的由来。我们在IPM %IPM.General.Singleton 中有一个基本实现,你可以扩展它以满足不同的使用情况。一般行为和使用模式如下

  • 在较高的堆栈层中,调用该类的 %Get(),可以获得一个实例,在较低的堆栈层中调用 %Get() 也可以获得该实例。
  • 当对象在使用它的最高堆栈层中退出作用域时,将运行清理代码。

这比 % 变量更好一些,因为你不需要去检查它是否被定义,而且它还能通过一些深层对象技巧,在较低的堆栈层级上存活下来。

关于临时文件,IPM 也有一个临时文件管理器单例。解决这个问题的方法是:

ClassMethod MethodA()
{
    Set tempFileManager = ##class(%IPM.Utils.TempFileManager).%Get()
    Do ..MethodB(.filename)
    // Do something else with the filename
    // The temp file is cleaned up automatically when tempFileManager goes out of scope
}

ClassMethod MethodB(Output filename)
{
    Set tempFileManager = ##class(%IPM.Utils.TempFileManager).%Get()
    // Create a temp file and set filename to the file's name
    Set filename = tempFileManager.GetTempFileName(".md")
    
    //... and probably do some other stuff
}
Discussion (0)1
Log in or sign up to continue
Question
· Jan 29

Plumbing Services in Dubai – Professional & Reliable Plumbing Solutions

Our Plumbing Services in Dubai provide expert, fast, and affordable solutions for residential and commercial plumbing needs. Whether you are dealing with a leaking pipe, blocked drain, faulty water heater, or require complete plumbing installation, our skilled plumbers deliver efficient and long-lasting results.

We use advanced tools, high-quality materials, and industry-approved techniques to diagnose and resolve plumbing issues safely and effectively. Each service begins with a detailed inspection to identify the root cause and prevent future problems.

Our plumbing services in Dubai include leak detection and repair, blocked drain cleaning, pipe repair and replacement, tap and mixer installation, toilet repair, water heater installation and maintenance, bathroom and kitchen plumbing, and emergency plumbing services.

Discussion (0)1
Log in or sign up to continue
Article
· Jan 28 4m read

SQLウィンドウ関数を理解する(パート1)

InterSystems IRISのウィンドウ関数を使用すると、累積合計、ランキング、移動平均など、強力な分析を直接SQLで実行できます。
ウィンドウ関数は、「GROUP BY」のように結果をまとめることなく、関連行の「ウィンドウ」(グループ上)で動作します。
つまり、ループも結合も一時テーブルも使わずに、より簡潔で高速、しかも保守しやすいクエリを書くことができます。

この記事では、よくあるデータ分析タスクに取り組むことで、ウィンドウ関数の仕組みを理解していきましょう。


InterSystems IRISでのSQLウィンドウ関数入門

SQLウィンドウ関数は、データ分析のための強力なツールです。
各行をそのまま表示したまま、複数行の集計とランキングを計算することができます。
ダッシュボード、レポート、または複雑な分析を構築しているかどうかに関係なく、ウィンドウ関数はロジックを簡素化し、パフォーマンスを大幅に向上させます。

注意:私はウィンドウ関数の専門家ではありませんが、私がウィンドウ関数を理解するうえで役立ったインサイトやリソースを共有したいと思います。 ご提案や訂正があれば、ぜひお知らせください!

Discussion (0)0
Log in or sign up to continue
Article
· Jan 28 7m read

VSCode でリモートアタッチを利用して Embedded Python をデバッグする方法

開発者の皆さん、こんにちは!

この記事では、InterSystems IRIS サーバサイドで実行できるPython(Embedded Python)のデバッグ方法をご紹介します。

前提:VSCode の Python デバッガ用モジュールの debugpy を利用するため、Python スクリプトファイルに記載した Embedded Python のコードが対象です。クラス定義に[Language = python]を指定して記載しているコードは対象外となります。ご注意ください!

(1)なぜdebugpyを使うのか?

VSCode の Python デバッガは内部的に、debugpy を使用していますが、これは「VSCode が起動した Python プロセス」に自動適用される仕組みです。

そのため、irispython のような VSCode 管理外の Python プロセスや、すでに起動しているリモート/コンテナ上の Python に対しては、明示的に debugpy を起動する必要があります。

 

(2)debugpyのインストール

ということで、debugpy をインストールします。

pip install debugpy

必要であれば --break-system-packages を付けてインストールします。

pip3 install debugpy --break-system-packages

 

(3)VSCodeの準備

VSCode デバッグメニューを開き、create a launch.json file をクリックし、launch.json に Python リモートアタッチ用デバッガを設定します。

 

リモートアタッチで接続するサーバー名(IPアドレス)、リモートのデバッグサーバーがリッスンするポート番号を指定します。

 

 

上記指定で、launch.json がワークスペースに追加されるので、利用環境に合わせて設定を変更します。

※ ポート番号は空いている任意の番号を利用できます。

pathMappings は、「VSCode 上のファイル」と「デバッグ対象プロセスが実行しているファイル」を1対1で対応付けるための設定です。

この対応が取れていない場合、ブレークポイントを置いてもヒットしない、または別の行で止まることがあります。

pathMappings 以下の localRoot は、VSCodeのワークスペースフォルダのルートがデフォルト設定されていますので、デバッグ用のPythonスクリプトファイルがあるディレクトリを指定します。

例では、ワークスペース/src/DebugTest.py をデバッグ用コードに使用したいので、${workspaceFolder}/src としています。

pathMappings 以下の remoteRoot は、アタッチ先デバッグサーバーで実行する Python スクリプトファイルの存在するのディレクトリを指定します。

指定例は以下の通りです。

 

✅例1:Embedded Pythonの実行がローカルの場合

Windows での記述例です。

remoteRoot には、Embedded Python で実行する Python スクリプトファイルが存在するディレクトリ:C:\\WorkSpace\\EmbeddedPythonDebug\\src を指定しています。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

        {
            "name": "Python Debugger: Remote Attach",
            "type": "debugpy",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/src",
                    "remoteRoot": "C:\\WorkSpace\\EmbeddedPythonDebug\\src"
                }
            ]
        }
    ]
}

 

✅例2:Embedded Python の実行がIRISコンテナの場合

※ VSCode Dev Containersを利用している場合は、ドキュメント:Running a Session through Docker Dev Containers をご参照ください。

コンテナの場合は、デバッグサーバーがリッスンするポート番号をコンテナ開始時にホストから利用できるように、ポートフォーワードを指定する必要があります。

このリポジトリでは、docker-compose.yml を利用しているため、以下の設定をしています。

ports: ["9872:1972","5678:5678","52773:52773"]

services:
  iris:
    build:
      context: .
      dockerfile: Dockerfile
    ports: ["9872:1972","5678:5678","52773:52773"]
    container_name: trysql
    volumes: ["./src:/src"]
    environment:
      - TZ=JST-9

また、VSCode のデバッグで参照するホスト上のディレクトリをコンテナから参照できるようにバインドマウントしておく必要があります。

volumes: ["./src:/src"]

ホストの src をコンテナの /src にバインドマウントしています。

上記コンテナに対する launch.json は以下の通りです。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        
        
        {
            "name": "Python Debugger: Remote Attach",
            "type": "debugpy",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/src",
                    "remoteRoot": "/src"
                }
            ]
        }
    ]
}

 

(4)デバッグ

サンプル一式👉https://github.com/Intersystems-jp/debug-embedded-python

デバッグを開始するため、debugpy をインストールしたデバッグサーバーで Python スクリプトファイルを実行します。実行時 irispython を使用します。

実行時の記述は以下の通りです。

<IRISインストールディレクトリ>/bin/irispython -m debugpy --listen localhost:5678 --wait-for-client <Pythonスクリプトファイル>

--listen localhost:5678 は、デバッグサーバーに localhost からポート 5678 での接続待機を指示しています。

コンテナの場合は、ホストから接続する必要があるので --listen 0.0.0.0:5678 と指定します。

--wait-for-client の指定で、VSCodeのデバッガーからの接続があるまで Python スクリプトが実行されないようにします。

※ --wait-for-client を指定しない場合、VSCode から接続する前に Python スクリプトが実行されるため、設定したブレークポイントには到達しません。

具体的な実行例は以下の通りです。

 

ローカルでの実行

Windowsでの実行例で記載します。IRISのインストールディレクトリを c:\intersystems\irishealth1 として記載していますので、実行環境に合わせてディレクトリを変更してください。

デバッグ前に初期データなどの準備をします。irispythonを利用して以下実行します。

c:\intersystems\irishealth1\bin\irispython .\src\DebugTest.py --mode prepare

DebugTest.py の prepare()関数を実行しています。

初期実行が終了したら、irispython と debugpy を利用して Python スクリプトを実行します。

 

 

コンテナでの実行

/usr/irissys/bin/irispython -m debugpy --listen 0.0.0.0:5678 --wait-for-client /src/DebugTest.py

DebugTest.py の run()関数を実行します。

 

コンテナ開始からデバッグ実行までの一連の流れ

コンテナ開始

docker compose up -d

コンテナへログイン

docker exec -it debugtest bash

デバッグ実行

/usr/irissys/bin/irispython -m debugpy --listen 0.0.0.0:5678 --wait-for-client /src/DebugTest.py

 

ここで、VSCodeのデバッグメニューから launch.json で指定した設定を利用して、デバッグを開始します。

launch.json の name に設定した名称:Python Debugger: Remote Attach が表示されているので、クリックするとデバッグが開始します。

 

 後は、VSCodeのメニューに従ってデバッグを進めます。

 

よくあるトラブル

実際に遭遇したトラブルをご紹介します。

✅ブレークポイントが止まらない → pathMappings の対応が合っていない

✅コンテナで localhost 指定して接続できない。 →  0.0.0.0 を指定して解消

✅import iris でエラーになる → irispythonで起動していない

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