PythonでスタンドアロンWebアプリを作ろうとしてJavaScriptに移植してた話


PythonでスタンドアロンWebアプリを動かす選択肢

 スタンドアロンアプリでGUIを書くのが面倒なのでHTMLで動かそうと言う考えで、気楽な気持ちで始めたら暗礁に乗り上げました。

 Python上の選択肢はpywebviewcefpythonぐらい。
 flexxとFirefoxベースのありますが、まだβの様なので取りあえず様子見

pywebviewの問題

 pywebviewは単体のウェブラッパーとして完結していますが、OSによって内部で動作しているWebブラウザが異なり、トラブルシューティングが大変な上、ブラウザによって見た目が微妙に変わり、バージョンやWebエンジンでJavaScriptやセキュリティポリシーが大きく異なると言う問題があります。特にMAC(Safari)とWindows(IE or Chromium)とLinux(未検証)で動作が大きく異なります。
 特にChromiumの場合、セキュリティポリシーが厳格化されている為、アプリ内でWebサーバーを立ててアクセスさせると言う本末転倒な事をしないと行けません。回避する方法がいくつ有りますがそいつがいつまで使えるかはあやしいです(過去の例を試したところ全部失敗した……)

 アプリ内でWebサーバーを立てるアプリはAndroidのアプリに割とありますが、先日もセキュリティホールが発見されて外から中のファイルがまるみえなる言うアプリがありましたが、

あれは bind="0.0.0.0" とか書いてあるサンプルが悪い。

 Googleはセキュリティ対策にローカルリソースアクセスを禁止してセキュリティホールを作り出すと言う邪悪をやっているわけです。

内部Webサーバーを起ち上げる場合、ホストの指定 bind="0.0.0.0" は禁止です。あれはワイルドカードでノーガードと言う意味になります。ローカル以外でつなげない様にするにはbind="127.0.0.1"と書かないといけません。

 safari(webkit)はローカルリソースのアクセスが可能です。つまりスタンドアローンアプリでWeb UIを使いたいのに複数のブラウザを想定しないといけない上にWindows上での動作があやしいので却下になりました。

cefpythonの問題

 cefpython組み込み用のChronium(CEF)とのブリッジライブラリ、cefpythonの問題は単独で動かす事は一応可能なのですがそれを使うことを想定してない点です。何らかの形でラップしないと行けないわけです(pywebviewにはエンジンの機能があり、cefの選択肢があるのですがOSによっては上手くインストール出来ないケースがあります)
 つまりなんらかのGUIライブラリでラップする必要があるのですがこれが全部鬼門で……。

  • Tkinker ワークアラウンドをかなり使わないといけない
  • kivy ラッパーライブラリ(garden.cefpython)に問題があり、OSをえり好みする上、メンテナンスもまともにされてない。
  • wxpython 環境にとってはPythonをコンパイルしなおす必要がある

 UNIX用のライブラリを無理矢理動かしている関係でWindowsやMACで不具合起こしすぎるるのでOSごとに実装を変えないと行けない感じ。

 ここまでやるならJavaの方が楽ですし以前かいたソリューションで十分です。

Node.js + Electronで動かす

 GUIの部分はHTML + JavaScriptで書いているのでいっその事、全部JavaScriptにしてしまえと言うソリューションを発動してみます。

 アプリ本体はNode.jsを利用、今回はドキュメントにアクセスしやすいWebエンジンににElectronを使用することにします。

 ElctronはChroniumベースなのですがローカルリソースのアクセスに関しては事実上ノーガードです。この特徴は外部のコンテンツを表示させる用途を排除して、ローカルだけに完結するスタンドアローンGUIの方に向いていると言えます。

 そう言うわけでPythonで書いたコードをJavaScriptに移植してみます。
 実際のところPythonとJavaScriptは構文以外の言語仕様は意外と似ていないようで似ており、一番異なるのがライブラリと構文だったりします。もう一つ、スレッドとイベントドリブンと言う非同期の部分やクラスの実装方法に大きなモデルの違いがありますが、ここはGUIを実装するときに問題になる部分なので今回のケースでは問題になりません。

 NumpyやSciPyみたいなライブラリを使っていない素のPythonコードであれば機械的にコード変換かますだけで終わりました。(そもそも元のコードがほとんど正規表現叩いているだけ……)

 ほぼ逐語訳になったので、自動化できる気もする

 一番面倒だったのが str.formatedの扱いで、JavaScriptの場合コールバック関数を実装する必要がありました(タグ関数のサンプルをそのまま使った)

 ElectronのWebブラウザとメインプロセスのデータやりとりはWokerに近いので、慣れるまでが大変ですが慣れれば逆に簡単かもしれない。

 そのうえビルドまでしてくれるので楽。