はじめに
myproject/のmain.py
で
from myproject.core import some_function
と
from core import some_function
のデバッグ時とexe化後の状態でインポートの挙動の違いとその対処法についてのメモ
概要
Pythonのコーディングで、デバッグ時には問題なく動作していたインポートが、exe化した後に失敗することがありました。
今回は、自分が遭遇した ModuleNotFoundError
について、その原因と解決方法を共有します。
プロジェクト構成
その時のフォルダ構成です。
main.py
が、同じディレクトリの「core」フォルダ内のutil.py
をインポートして使おうとしていました。
MyProject/
│
├── myproject/
│ ├── main.py
│ └── core/
│ ├── __init__.py
│ └─── util.py
│
└── README.md
main.py
のコード例
import os
import sys
from myproject.core.util import some_function
def main():
some_function()
if __name__ == "__main__":
main()
このコードはexe化したときには正しくインポート出来ていましたが、デバッグ時には次のようなエラーが発生しました。
ModuleNotFoundError: No module named 'myproject'
※逆に、以下のコードだとデバッグ時ではインポート出来ましたが、exe化するとインポートに失敗しました。
from util import some_function
原因
以下のことが関係しているのかと思いました。
- exe化のプロセス(
PyInstaller
などを使う場合)では、Pythonのファイルシステムのパスに関する設定が異なるため、手動でパスを調整する必要がある - exe化すると、プロジェクトがバンドルされ、ディレクトリ構造が変わるため、相対パスでのインポートは注意が必要
解決方法
この問題に対処法として、Pythonがモジュールを探すパスを明示的に設定する必要があり、sys.path
を使って、モジュールを含むディレクトリを手動で追加しました。
修正後のmain.py
のコード
import os
import sys
# フォルダの親ディレクトリをパスに追加する
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from myproject.core.util import some_function
def main():
some_function()
if __name__ == "__main__":
main()
まとめ
- デバッグ時とexe化後のモジュールインポートエラーは、
sys.path
の違いによって引き起こされることがあるので、問題が起こるようならばsys.path.append()
で明示的にパスを追加することで解決できた- デバッグ時とexe化後で同じ挙動をするのを確認できた
- exeとして動いているのか、.pyとして動いているのか、で「自分の位置」の情報が変わるらしいということを知れた