py2exeモジュールについて

Pythonで単体で動くバイナリを作ろう!

tags:python, binary, distribution, module, tips
created:2006-04-07T15:58:21

Pythonで単体で動くバイナリを作ろう! py2exeの利用方法

特徴

py2exeホーム : http://www.py2exe.org/

Python標準のdistutilsのプラグインです。

pythonで作成したスクリプトから、依存ライブラリを検索して、 収集したファイル群だけで実行可能なバイナリを作成する機能を持っています。

最近のバージョンになって、バンドルレベルが選択できるようになり、

  1. 単独のexeファイルに組み上げる
  2. Python25.dllとMSVC71.dllとexeの3つに組み上げる
  3. 依存ライブラリをアーカイブまたはフォルダに分離する

といった混成度合いが指定できるようになりました。

単独のexeにしちゃうと、Pythonランタイムまで内臓してしまうのでシンプルなスクリプトも2メガバイトを超えてしまいます。

注釈

Python25.dllは約2.0メガバイト

2.の3ファイル構成にすると、1メガバイト以下のexeが生成できるので、利用しやすい手ごろなサイズかと。

インストール

下記のリンクを保存して実行するだけ。

py2exe 0.6.6 windows python2.5 インストーラ

コンソールアプリ

setup.py

from distutils.core import setup
import py2exe

py2exe_options = {
  "compressed": 1,
  "optimize": 2,
  "bundle_files": 2}

setup(
  options = {"py2exe": py2exe_options},
  console = [
    {"script" : "example.py", "icon_resources": [(1,"py.ico")]}],
  zipfile = None)

コマンドラインにて:

setup.py py2exe

icon_resources指定でexeのアイコンを指定できます。 bundle_filesオプションを1にすれば大きいサイズの単独exeが作れます。

生成されたexeを実行するとコンソールが開いて動作します。

ウインドウアプリ

setup.py

from distutils.core import setup
import py2exe

py2exe_options = {
  "compressed": 1,
  "optimize": 2,
  "bundle_files": 2}

setup(
  options = {"py2exe": py2exe_options},
  windows = [
    {"script" : "example.pyw", "icon_resources": [(1,"py.ico")]}],
  zipfile = None)

コマンドラインにて:

setup.py py2exe

console引数でなくwindows引数を使うようになるだけでコンソールアプリと同じです。

生成されたexeを実行するとコンソールは開かずに動作します。

細かい指定

py2exe_options = {
  "packages": ['PIL'],
  "excludes": ['tcl'],
  "compressed": 1,
  "optimize": 2,
  "bundle_files": 2}

このように含めたいパッケージや含めたくないパッケージを明示することもできます。 py2exeの自動依存検索は賢い時と賢くない時があります。 動くことが最優先のようで、妙に広い範囲でパッケージを取り込んだりします。 どうやら、小さな出力が欲しいときは 必要なものだけをモジュール単位で指定する方が無難のようです。

警告

setup(
  options = {"py2exe": py2exe_options},
  data_files=[("",["hoge.dll"])],
  console = [
    {"script" :"hogehoge.py", "icon_resources": [(1,"py.ico")]}],
  zipfile = None)

付属のファイルを指定した時、distフォルダにコピーされますが、exe内にバンドルはされません。 これはインストーラを作成する時に有効な方法です。

問題点

py2exeで固めると、site.pyやsite_customize.pyなどの環境依存モジュールの自動インポート機能が機能しないようです。(バンドルされないのかインポートしてくれないだけか良くわかりません。)

また、sys.argv[0]に正しいexeパスが入らないようです。モジュール名だけが入っています。

ですので、exeのフォルダ取得が面倒なことになっています。

以上の問題を回避するのに、メインスクリプト先頭に以下のコードを埋めると良いでしょう。

import sys
import os
import imp

import sys
if hasattr(sys,"setdefaultencoding"):
    sys.setdefaultencoding("utf-8")

def main_is_frozen():
    return (hasattr(sys, "frozen") or # new py2exe
            hasattr(sys, "importers") # old py2exe
            or imp.is_frozen("__main__")) # tools/freeze

def get_main_dir():
    if main_is_frozen():
        return os.path.abspath(os.path.dirname(sys.executable))
    return os.path.abspath(os.path.dirname(sys.argv[0]))

ポイントは以下の2点。

  • site-customize.pyで記述する定石のsys.setdefaultencoding(“utf-8”)をここで記述します。
  • os.path.split(sys.argv[0])[0]の代わりにget_main_dir()をつかってexeのパスを取得できます。

上記の記述ならexeに固める前のスクリプトも正常に動作します。

バンドルしないDLLについて

バンドル指定は、EXEの中にDLLもリソースとして取り込むのですが、 ある種のDLLはこの手法で問題が出るものがあるようです。

バンドルしたくないまたは、問題のあるDLLがあるなら、

py2exe_options = {
  "dll_excludes": ["HOGE.DLL"],
  "compressed": 1,
  "optimize": 2,
  "bundle_files": 2}

というようにdll_excludesキーワードを使ってください。 bundle_files=3では動作して、bundle_files=1,2で動かない場合、

bundle_files=3でdistフォルダにコピーされているDLLのいずれかが問題を起こしている事が予想されます。

wxPythonについて

wxPython2.8以降のアプリをpy2exeでexe化したとき、 実行環境には以下のDLLが必要になるようです。

  • gdiplus.dll
  • msvcp71.dll

これらは、マイクロソフトが配布している

  • vcredist_x86.exe(vc7ランタイムのインストーラ)
  • dotnetfx.exe(.NetFramework1.1以降のランタイムインストーラ)

などをインストールすればよいようです。

結論

単独exeのメリットはPythonのインストールしていない環境に持ち込んでも動くことです。 Win標準のCPythonはMSVCでコンパイルされているらしく、MSVCR71.dllが必要なのは少し残念。 cygwin版やMingw32版ならMSVC71.dllがいらないのかな?

おまけ①

生成exeのインストーラを作るにはもうひとつsetup.pyのようなものを記述すれば大丈夫。

コマンドラインにて:

setup2.py bdist_wininst

でいきなりインストーラが作れます。

書き方の詳細はPython標準distutilsを参照!

おまけ②

pybuilderプロジェクト

ここでTKInterを使ったGUIベースの py2exeフロントエンドが公開されています。