PythonスクリプトをPyInstallerで実行モジュール化するときに出てきた問題点

PyInstallerを使って、Pythonスクリプトをバイナリに固める。固めたバイナリを実行すると、ファイルパスの問題でうまく動かないときがある。どのような場合に動かないかを、まずは示す。

ファイル構成は下記の通り。ソースがexample.py、ソースが読み込んでいる画像ファイルがディレクトリpicsの中にある。

$ pwd
/home/hoge/Python/Installer
$ ls
example.py pics/
$ ls pics
flower.png

スクリプトexample.pyは、ウィンドウを表示するだけの簡単なもの。内容は以下の通り。

#!/bin/env python
# program : example.py

import os
import wx

class MyApp(wx.PySimpleApp):
    def OnInit(self):
        self.frm = wx.Frame(None, -1, 'Title', size=(300,200))
        self.p = p = wx.Panel(self.frm, -1)

        dpath = os.path.dirname(__file__)
        ppath = os.path.join(dpath, 'pics', 'flower.png')
        print 'using a file: ',ppath
        bmp = wx.Image(ppath).ConvertToBitmap()     

        self.frm.Show()
        return True

app = MyApp()
app.MainLoop()

実行してみよう。どんなウィンドウが表示されるかは、ここではあまり問題ではない。問題は、画像ファイルのパスppathが正しいかどうか。正しくないと、ConvertToBitmap()に失敗してエラー終了する。

スクリプトファイルがあるディレクトリからスクリプトを実行する場合

$ cd /home/hoge/Python/Installer
$ python example.py
using a file: pics/flower.png

ウィンドウは問題なく表示され、「using a file: ...」で画像ファイルのパスも表示される。つまり、問題なし。

スクリプトファイルがあるディレクトリとは別のディレクトリから、スクリプトを実行する場合

$ cd /home/hoge/
$ python Python/Installer/example.py
using a file: Python/Installer/pics/flower.png

この場合も問題なし。

プログラムをスクリプトのまま実行するときには問題ないことが分かったので、次に、スクリプトを実行モジュールに変換して試してみる。PyInstallerを使って、Linux用のバイナリファイルを作る。

PyInstallerを実行するスクリプトは、下記のようなものでいいだろう。PyInstallerは既にインストールしてあり、実行環境も整えてある。

#!/bin/sh

Makespec.py -a --onefile --name=example example.py
Build.py example.spec

cp -r pics dist

これを実行すると、実行モジュールexampleが作成される。ファイル構成は以下のようになる。

$ pwd
/home/hoge/Python/Installer/dist
$ ls
example pics/

作成されたモジュールを起動してみよう。

実行モジュールがあるディレクトリから起動する場合

$ pwd
/home/hoge/Python/Installer/dist
$ ./example
using a file: pics/flower.png

問題なく起動できた。

実行モジュールがあるディレクトリとは別のディレクトリから、モジュールを起動する場合

$ pwd
/home/hoge
$ Python/Installer/dist/example
using a file: pics/flower.png
Traceback (most recent call last):
  File "<string>", line 19, in <module>
  File "/home/hoge/Python/Installer/build/pyi.linux2/example/outPYZ1.pyz/wx._core", line 8081, in __init__
  File "/home/hoge/Python/Installer/build/pyi.linux2/example/outPYZ1.pyz/wx._core", line 7981, in __init__
  File "/home/hoge/Python/Installer/build/pyi.linux2/example/outPYZ1.pyz/wx._core", line 7555, in _BootstrapApp
  File "<string>", line 13, in OnInit
  File "/home/hoge/Python/Installer/build/pyi.linux2/example/outPYZ1.pyz/wx._core", line 3369, in ConvertToBitmap
wx._core.PyAssertionError: C++ assertion "image.Ok()" failed at ../src/gtk/bitmap.cpp(444) in CreateFromImage(): invalid image

ダメ。flower.pngのファイルパスが正しく認識されていない。現在のディレクトリは、/home/hoge/であり、/home/hoge/pics/flower.pngを探そうとしてエラー終了している。

解決方法

スクリプトの中の

dpath = os.path.dirname(__file__)

dpath = os.path.dirname(sys.argv[0])

とする。