×

警告メッセージ

  • Cisco Support Forums is in Read Only mode while the site is being migrated.
  • Cisco Support Forums is in Read Only mode while the site is being migrated.

アプリケーションの動的更新方法

ブログ

2014/08/10 - 20:48
8月 10th, 2014
User Badges:

プログラミングに関するブログを書いてくれという依頼があったので投稿してみます。

今日は Python で作成したアプリケーションをバイナリ化して配布し、なおかつそれを動的にアップデートする手法について扱います。

現在、私は Cisco Log Viewer というアプリケーションを開発しており、そのプログラムではログの解析処理を行います。
そのアプリで「アプリケーションを新しくダウンロードしなくても解析処理の精度をあげたいよね」という要望があったので、既存のアプリを動的に更新する方法について調査しました。
簡単ながら動くことが確認できたので、その作り方を記事にします。

まず作りたいアプリのコンポーネントは以下の構成になっています。
解析のフレームワークのアプリケーションがある。そのアプリケーションの内部の一部処理のみを動的に更新(サーバーからダウンロード)する。
これを図に記すと以下のようになります。


これの簡易版を作ることを目標として記事を書きます。
おおまかな構成として
 1. 必要となる技術の紹介
 2. サンプルコードの提示

 3. 実行結果

となります。

 

== 技術の紹介 ==

 

キーとなるポイントは2つあります。
まず一つめは python のアプリケーションをどのようにしてアプリにするかということです。アプリにしなければ python をインストールしている環境でしかプログラムを実行できないため、windows環境を含めた万人向けに配るためにはアプリ化は必須です。

2つめのポイントは、先に作ったバイナリの内部をどのようにアップデートするかということです。ここを動的に処理できないと、「アップデート = 新しいアプリのダウンロード」となってしまうため、ユーザには面倒くさいですね。

まず第一のバイナリ化について扱います。
これを行うためには、python をアプリ化するための「スクリプト」を利用します。
色々な種類のものがあるのですが、私は
 windows: cx_freeze
 mac: py2app
を利用しています。windows ですと py2exe という cx_freeze というソフトがあるのですが、これはサポートするpythonのバージョンが限定的なので使っていないです。
mac だと py2app がスタンダードのようですね。
注意すべきは windows で 64bit の python, cx_freeze を使うと 32 bit 環境で利用できないため、両者とも 32bit のものを利用するということぐらいだと思います。

これを使うと、以下のように配布イメージが作成されます。

 

windows


 

mac


 

次に第二の「動的ロード」について扱います。
手法はいくらかあるかもしれませんが、私は __load__ 関数と getattr 関数を使いました。
また、モジュールをロードするためにはそのモジュールのパスが登録されている必要があります。
それには sys.path.append 関数を使います。
これらの関数の利用例を記載します。

# add desktop to system path
SYSTEM_PATH = os.path.expanduser('~/Desktop')
sys.path.append(SYSTEM_PATH)

# load module "product" as "module"
module = __import__(product, {}, {}, [])

# load module's check_file function as self.check_file
self.check_file = getattr(module, 'check_file')

# load module's parse function as self.parse
self.parse = getattr(module, 'parse')


== サンプルコードの紹介 ==

 

main.py は動的ロードを行うクラス Handler とそれをテストする関数 test_handler があります。
Handler クラスの set_product クラスにモジュール名をいれるとそのモジュールをロードし、Handler クラスの check_file 関数と parse 関数を上書きします。
例えば、次に記載する nexus.py を "nexus" としてロードすると、Handler の check_file 関数は nexus.py の
check_file 関数として動きます。
この main.py はバイナリとして配布されます。

main.py


 

SYSTEM_PATH には今回は Desktop にしており、動的にロードするファイル群もそこに置いています。本番用のコードは配布するアプリケーションを配置したディレクトリ、もしくはそのサブディレクトリあたりにすべきでしょう。

次に main.py が動的ロードする nexus.py と catalyst.py のコードを提示します。

catalyst.py


 

nexus.py


 

特に複雑なことはやっていませんが、parser_util.py を呼び出してログをファイルに書き出しています。これは最初に示したデータベースへの書き込み処理を簡易化したもの、かつ動作を確認する方法として使っています。

最後に parser_util.py のコードを提示します。指定されたファイルに追記で指定された文字列 + 改行を書き込みます。

parser_util.py



== 実行 ==

main.py から作られた main.py(Mac)とmain.exe(Windows)を実行してみます。
なお、動的に読み込まれる nexus.py と catalyst.py は python のパスを通した Desktop に設置してある必要があります。
parser_util.py の関数 log で作成された log.txt を見ると以下のようになっていました。

catalyst checkfile
catalyst parse
nexus checkfile
nexus parse

動いてくれていますね。
次に動的に読み込んでいる catalyst.py と nexus.py をアップデートしてみます。
ここではログに書き込むメッセージの後に 2 とつけます。
実行すると以下のようになりました。

catalyst checkfile
catalyst parse
nexus checkfile
nexus parse
catalyst checkfile2
catalyst parse2
nexus checkfile2
nexus parse2

後半に 2 が追加されていますね。
これでバイナリをアップデートせずにアプリケーションの挙動が変わっていることが確認できました。
おわりー!!

 

Loading.