0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonのSource参照管理について

Last updated at Posted at 2025-10-11

Pythonプロジェクトにおけるパス処理とモジュール参照

1. プロジェクトフォルダ構成例(MVVMを意識した例)

MVVMパターンを意識してプロジェクトフォルダを作成すると、以下のようになります。

ProjectRoot/
├─ Sources/
│ ├─ Common/
│ │ ├─ Common_A.py
│ │ └─ Common_B.py
│ ├─ Models/
│ │ ├─ Model_A.py
│ │ └─ Model_B.py
│ ├─ ViewModels/
│ │ └─ ViewModel_A.py
│ └─ Views/
│ ├─ View_A.py
│ └─ View_B.py
└─ main.py

Sources/ 以下はプロジェクトのメインソース。
Common や Models は、実行環境に依存しない共通モジュールをまとめたフォルダ。
ViewModels はアプリのロジック層、Views はUI層。
main.py がアプリのエントリポイント。

2. モジュールが見つからない(ModuleNotFoundError)の問題

ProjectRootで main.py を実行すればモジュールは参照可能です。

main.py
from Sources.Common import Common_B  # OK

しかし、サブモジュールや単体ファイルで動作確認をしたい場合は問題が発生します。

Sources/Common/Common_A.py
from Sources.Common import Common_B

実行結果例:

Traceback (most recent call last):
  File "Common_A.py", line 1, in <module>
ModuleNotFoundError: No module named 'Sources.Common'

原因

Pythonはデフォルトで実行ファイルのディレクトリや標準ライブラリしか参照しません。

サブモジュールから実行すると、プロジェクト内の Sources フォルダを見つけられず、ModuleNotFoundError が発生します。

発生環境

  • サブ環境のPython実行(python Sources/Common/Common_A.py など)
  • PyInstallerでビルドした場合
  • Buildozer / Android 実行環境

3. 簡単な解決法 sys.pathによる相対パス追加

Pyinstaller,Buildozerに対応しなくていいなら、下記の書き方で良いです。
この書き方なら、フォルダ部分は全部sys.pathに任せられるので、モジュール名だけで参照してくれます。

import sys

# 必要なフォルダを追加
sys.path.append("./Sources/Common")
sys.path.append("./Sources/Models")
sys.path.append("./Sources/ViewModels")
sys.path.append("./Sources/Views")

sys.path.append("./Common")
sys.path.append("./Models")
sys.path.append("./ViewModels")
sys.path.append("./Views")
import Common_A
import Common_B

4 簡単な解決法2 sys.pathによる絶対パスの追加

PyInstallerでコンパイルが通る程度の対応としては、以下の3行と、ルートからのモジュール指定する対応で十分です。ただし、PyInstallerやBuildozerの実行環境ではPathの問題が出る可能性があるとのこと。

import sys, os
source_directory = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(source_directory, "..", ".."))
sys.path.insert(0, project_root)

from Sources.Common import Common_A
from Sources.Models import Model_B
from Sources.ViewModels import ViewModel_A
from Sources.Views import View_B

これをファイル冒頭に入れるだけで、ProjectRoot 直下を探索対象に追加できます。
これにより、すべてのモジュールを[from ルート基準のPath import モジュール名]の記述でimportできます。

4. PyInstaller対応解決方法 実行環境別ベースパス設定

少し面倒なので、できればもっと簡単な解決法を知りたいところですが、一応私の環境では下記の方法でやっています。
Buildozerについてはこれから調査なので何とも言えませんが・・・
(※ Buildozer用の回路もChatGPTの情報にすぎません。Buildozerの環境が変わったのかビルドできず検証ができない・・・。2025年版作らないとかも。)

Views
from pathlib import Path
import sys

from pathlib import Path
# --- 実行環境によるベースパスの切り替え ---
if getattr(sys, 'frozen', False):  # PyInstaller 実行時
    base_directory = Path(sys._MEIPASS)  # ← str を Path に変換!
elif getattr(sys, 'android_bootstrap', False):  # Buildozer / Android
    from android.storage import app_storage_path
    base_directory = Path(app_storage_path())
else:  # 通常Python実行
    base_directory = Path(__file__).resolve().parent
# --- プロジェクトルートをパスに追加 ---
source_directory = base_directory.parent
project_root = source_directory.parent
if str(project_root) not in sys.path:
    sys.path.insert(0, str(project_root))

from Sources.Views import View_B
from Sources.Common import Common_A
from Sources.Common import Common_B
from Sources.ViewModels import ViewModel_A
from Sources.Models import Model_A

Pythonプロジェクトにおけるパス処理とモジュール参照

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?