hmPythonとPythonの「アウトプロセス連携」

インプロセスでは駄目な事態に対応する

  • hmPython内で実行することが出来ないライブラリ(numpy等)を使用したい場合
  • すでに別のPython等で動作しているPythonを最小限の労力にて利用したい場合
  • 異なるビット数のPythonと連携したい、といった場合

などで威力を発揮します。

アウトプロセス連携概論

アウトプロセス連携は単純です

下図のように「インプロセスのhmPython3」から「アウトプロセスとして別のPython」を起動し、
情報のやりとりは「shelve」ライブラリで行うというものです。

PICTURE

サンプルのダウンロード

更新日 2017/10/26
inprocess_and_outprocess.zip

サンプル解説

  • マクロファイル

    inprocess.mac
    #DLL = loaddll( hidemarudir + @"\hmPython3.dll" );
    #r = dllfuncw( #DLL, "DoString", "import inprocess");
    freedll( #DLL );
    
  • hmPython用(インプロセス)のpythonファイル

    インプロセスとなるhmPythonの役割は、

    • 連携対象となる「別のpythonプロセスを呼び出すこと」
    • 値をデータファイルへと保持しておくことです。
      (ここではPythonの「shelve」ライブラリを利用)
    inprocess.py
    from hmPython import hm
    import shelve
    import subprocess
    
    # 秀丸のディレクトリ
    hidemarudir = hm.Macro.Var["hidemarudir"]
    
    # 現在実行しているマクロのディレクトリ
    currentmacrodirectory = hm.Macro.Var["currentmacrodirectory"]
    
    # アウトプロセスへと処理を(一部)委託する。そのPythonスクリプトのファイル名
    outprocess_script = currentmacrodirectory + r'/outprocess.py'
    
    # アウトプロセスに処理を委託。
    # アウトプロセスからインプロセスへと値を入れたいものは、shelveでやりとりする
    def call_subprocess(script_name, shelve_path):
    
        hmpython_exe = hidemarudir + r'/HmPython3/python.exe'
    
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE
    
        result = subprocess.run((hmpython_exe, outprocess_script, shelve_path), startupinfo=startupinfo)
        return result
    
    
    # デバッグモニターや秀丸のアウトプット枠に対象データを表示
    def dump_data(data):
    
        # デバッグモニタに表示
        hm.debuginfo(type(data))
        hm.debuginfo(data)
            
        # 秀丸のアウトプロセスに表示
        output_result(type(data))
        output_result(data)
    
    
    # 秀丸のアウトプット枠に出力する
    def output_result(msg):
        hm.Macro.Var["$_message"] = msg
        ret = hm.Macro.Eval(r'''
            // アウトプット枠へ出力
            #OP = loaddll("HmOutputPane.dll");
            #ret = dllfunc(#OP, "Output",hidemaruhandle(0), $_message + "\r\n");
            freedll(#OP);
         ''')
        hm.Macro.Var["$_message"] = ""
        return ret
    
    
    # メイン
    def main_script():
    
        # データやりとり用のshelveのパス。パッケージ名事態をシェルブの名前にしておく
        shelve_fullpath = currentmacrodirectory + '/' + __name__
    
        # hmPythonだからこそ得やすいデータ
        param1 = len(hm.Edit.TotalText)
    
    
        # インプロセス側で持ったデータ
        multiarr = [
                        [1, 2, 3],
                        [4, 5, 6],
                        [7, 8, param1]
                   ]
    
        #外部プロセスに渡すデータをシェルブにセーブ
        savedata = shelve.open(shelve_fullpath)
        # 自分から情報を格納する側がshelveをクリアする
        savedata.clear()
        # インプロセスからの情報をshelveに書き込んでいく
        
        savedata['from_inprocess_list'] = multiarr
        savedata.close()
        #-----------------------------------------------------
    
        # アウトプロセスとして別途pythonを呼び出す
        call_subprocess(outprocess_script, shelve_path=shelve_fullpath)
    
        #外部プロセスから渡されたデータをシェルブからロード
        load_data = shelve.open(shelve_fullpath)
        result_list = load_data["from_outprocess_list"]
        result_dict = load_data["from_outprocess_dict"]
        load_data.close()
        #-----------------------------------------------------
    
        dump_data(result_list)
        dump_data(result_dict)
    
    
    main_script()
    
  • 外部Python用のpythonファイル

    アウトプロセスのPythonの役割は、

    • hmPythonでは困難な処理を実行し、
    • hmPythonで利用したい値をファイルへと保存しておくことです。

    shelveライブラリを利用することで、
    hmPythonと外部プロセスPythonの間で「データをパースする」
    といった不毛な手間から解放されます。

    outprocess.py
    import numpy as np
    import shelve
    import sys
    
    # 最初の引数には、shelveのフルパスが入っている
    shelve_fullpath = sys.argv[1]
    
    # インプロセス(hmPython側のpython)からshelveへと格納された情報を拾う。
    data = shelve.open(shelve_fullpath)
    arg_list = data['from_inprocess_list']
    data.close()
    
    result = np.array(arg_list)
    result = result.T # 転置行列
    data = shelve.open(shelve_fullpath)
    
    # numpyのオブジェクトを直接代入すると、インプロセスの方でnumpyが勝手に読み込まれてしまう
    # それを防止するため、numpyの配列ではなく、あくまでも普通のpythonの多次元リストになおしておく
    data['from_outprocess_list'] = result.tolist()
    
    data['from_outprocess_dict'] = {"aaaa":33}
    
    data.close()