以前、Java から Python 実行した時の結果をPython標準出力で Java が受信する方法を投稿したが、
Javaからプロセス起動で実行するPython と文字列の受け渡し - Oboe吹きプログラマの黙示録
Python 標準出力→Java受け取り - Oboe吹きプログラマの黙示録
このような、Base64 コードに変換して仲介するようなことをしなくても良いことに気がついた。
Python から、Java へ正常時の結果出力(標準出力)は、JSONで出力して
Java側がJSONパースするのが綺麗であろう。
(Python 実行中でエラー発生、エラー出力:標準エラー出力の方は、特殊なので後で記載)
Pythonスクリプトが以下のような断片コードを実行しているとする。
Pythonの断片コード
import json # class の __init__で記述するもの self.mydict = dict() # メソッドで記述するもの self.mydict['A'] = 'A123' self.mydict['B'] = 24 self.mydict['C'] = '漢字:氏名' # result = json.dumps(self.mydict) print(result)
以下を標準出力する。
{"A": "A123", "B": 24, "C": "\u6f22\u5b57\uff1a\u6c0f\u540d"}
受信するJava側は、Jackson または、Google gson で読み取れば、
{"A": "A123", "B": 24, "C": "漢字:氏名"}
として読み込める。
自分が作った ScriptExecutor 、
https://github.com/yipuran/yipuran-core/wiki/Script_exec#orgyipuranutilprocessscriptexecutor
で、以下のように、コードを書いて確認できる。
Jackson 使用の場合、先日公開した以下を使って、、
https://github.com/yipuran/yipuran-jack/wiki
StringBuilder sb = new StringBuilder(); int sts = ScriptExecutor.run(()->"python c:/work/forJava/resmain.py" , t->{ sb.append(t); }, (t, e)->{ // エラー捕捉 pythonErrorTrace(t).forEach(s->{ System.out.println(s); }); }); String jsonstr = sb.toString(); // Jackson JsonNode を ObjectMapper readTree でJsonNode を求めて解析する処理 JsonNodeParse jp = new JsonNodeParse(); jp.stream(jsonstr).forEach(e->{ System.out.println(e.getKey() + " --> " + e.getValue() ); });
Python用 エラー捕捉→ Stream<String>
public Stream<String> pythonErrorTrace(String error) { String estr = error.replaceAll("\r", "").replaceAll("\n", ""); estr = estr.substring(2, estr.length()-2); String[] ary = estr.split("', '"); return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>(){ int x = -1; @Override public boolean hasNext(){ return x < ary.length-1; } @Override public String next(){ x++; return ary[x].replaceFirst("\\\\n$", ""); } }, Spliterator.ORDERED), false); }
Google gson であれば、以前作って公開した
https://github.com/yipuran/yipuran-gsonhelper/blob/master/src/main/java/org/yipuran/gsonhelper/util/JsonEntryParse.java
を使用すれば、JSONの解析は、
String jsonstr = sb.toString(); JsonEntryParse jp = new JsonEntryParse(); jp.read(jsonstr, (k, v)->{ System.out.println(k + " --> "+ v); });
このように確認できる。
問題は、Python処理内でエラー発生した時に、
・エラーメッセージをJavaで受信した時に文字化けしないこと。
・Python エラースタックトレースを中途半端ではなく最後まで取得すること
であった。
Python スクリプト
raise RuntimeWarning("警告エラー")
を発生するように任意にコーディングします。
スタックトレースをエラー発生まで採取するように、Python標準の traceback モジュールを import して
format_exception でスタックトレース採取して、print オプション file=sys.stderr で
スタックトレースを標準エラー出力します。
if __name__ == '__main__': try: main = Main() main.exec() except Exception as e: (etype, evalue, etb) = sys.exc_info() print(traceback.format_exception(etype, evalue, etb), file=sys.stderr)
このままでは、Java 側、ScriptExecutor#run() のエラー捕捉の BiConsumer でダンプすると、
Traceback (most recent call last): File "c:\work\forJava\resmain.py", line 21, in <module>\n main.exec() File "c:\work\forJava\resmain.py", line 13, in exec\n self.stool.func() File "c:\work\forJava\tools\jpress.py", line 14, in func\n raise RuntimeWarning("�x���G���[") RuntimeWarning: �x���G���[
と、文字化けしてしまいます。
標準エラー出力をUTF-8で出力するように、Python側で最初に宣言します。
import sys sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
すると、Java 側、ScriptExecutor#run() のエラー捕捉の BiConsumer でのダンプも
結果は以下のとおりになる
Traceback (most recent call last): File "c:\work\forJava\resmain.py", line 21, in <module>\n main.exec() File "c:\work\forJava\resmain.py", line 13, in exec\n self.stool.func() File "c:\work\forJava\tools\jpress.py", line 14, in func\n raise RuntimeWarning("警告エラー") RuntimeWarning: 警告エラー
標準出力は、同様に sys.stdout を以下のように設定していても
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
Python が出力する全角文字は、 \uXXXX の書式で出力されるので、
{"A": "A123", "B": 24, "C": "\u6f22\u5b57\uff1a\u6c0f\u540d"}