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

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

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

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

  • アウトプロセス連携概論

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

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

  • サンプルのダウンロード

    inprocess_and_outprocess.zipファイル。
    └更新日 2017/10/26
  • サンプル解説

    • マクロファイル

      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()