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?

PyCharmとVS Codeで「相対パス」が挙動違う問題を整理してみた

0
Posted at

Pythonで開発していると、PyCharmだと動く相対パス/相対インポートが、VS Codeやターミナルだと動かないという現象にかなり高い確率で出会います。 pythontutorials
この記事では、「書き方」の違いではなく、IDEの設定の違いが原因であることを整理しつつ、どの環境でも安定して動く書き方のパターンをまとめます。 pythontutorials


前提:Pythonの相対パス・相対インポートはIDEに依存しない

まず押さえておきたいポイントは、

  • open("data/input.csv") のようなファイルパスの相対指定
    → 「カレントディレクトリ(Working Directory)からの相対」で解決される。 stackoverflow
  • from . import x, from ..pkg import y のようなパッケージ相対インポートの文法も
    → Pythonの仕様で決まっており、IDEによって変わるものではない。 stackoverflow

にもかかわらず挙動が変わるのは、IDEが「どこをカレントディレクトリにしてスクリプトを起動しているか」「どこをimportの基準にしているか」が違うからです。 stackoverflow


PyCharm側の特徴:Working Directoryをかなり面倒見てくれる

PyCharmで右クリック → Runしたとき、デフォルトのWorking Directoryは以下のようになります。 stackoverflow

  • スクリプトがプロジェクト直下にある場合
    → プロジェクトルートがWorking Directoryになる($PROJECT_DIR$)。 stackoverflow
  • サブフォルダにある場合
    → そのサブフォルダ、またはモジュールディレクトリ($MODULE_DIR$)がWorking Directoryになる。 pythontutorials

そのため、例えば以下のような構成で:

my_project/
├── data/
│   └── input.csv
└── src/
    └── main.py

main.py をPyCharmから実行して open("data/input.csv") と書いても、PyCharmが上手くWorking Directoryをプロジェクトルートにしてくれる設定になっていると普通に動きますstackoverflow

逆に、PyCharmで相対パスがコケる場合は、Run Configurationで設定されているWorking Directoryがズレていることがほとんどです。 stackoverflow

  • 対処:Run Configurationの「Working directory」を
    • プロジェクトルート($PROJECT_DIR$)に揃える
    • もしくはスクリプトと同じフォルダ($MODULE_DIR$)に揃える

VS Code側の特徴:ターミナルとほぼ同じ、あまり手は出してくれない

VS Code + Python拡張の場合、基本的な挙動はターミナルで python your_script.py を叩いたときに近いです。 discuss.python

  • Working Directory
    • デバッガ実行時は launch.json"cwd" 設定に依存する。 stackoverflow
    • 何も設定しなければ、通常はワークスペースルートやターミナルのカレントディレクトリが使われる。 apxml
  • インポートパス
    • PyCharmのように「プロジェクトルートを自動でimportパスに含める」という優しさが弱く、
      PYTHONPATHsettings.json"python.analysis.extraPaths" などを自分で調整することが多い。 stefanocivelli

その結果、PyCharmでは通っていた次のようなコードがVS Codeでコケがちです。

from my_project.utils import helper  # PyCharmではOKだった

VS Code側ではプロジェクトルートがimportパスに入っていないため、ModuleNotFoundError になる、というパターンです。 stefanocivelli

  • 対処の一例(VS CodeをPyCharmっぽくする)
    • .vscode/settings.json に以下のように書いて、プロジェクトルートをimportパスに追加する。 stackoverflow

      {
        "python.analysis.extraPaths": [
          "./"  // ワークスペースルート
        ]
      }
      
    • あるいは、PYTHONPATH を設定してプロジェクトルートを追加する。 youtube


IDEに依存しない相対パスの書き方パターン

IDEごとの挙動に振り回されたくない場合、「カレントディレクトリ」ではなく「スクリプト自身の場所」を基準にパスを組み立てるのが定番パターンです。 qiita

基本パターン:__file__ + pathlib

from pathlib import Path

# このファイル(モジュール)自身があるディレクトリ
BASE_DIR = Path(__file__).resolve().parent

# data/input.csv へのパス
DATA_PATH = BASE_DIR / "data" / "input.csv"

with DATA_PATH.open("r", encoding="utf-8") as f:
    content = f.read()

ポイント:

  • __file__ で「このモジュールのパス」を取得する。 chayarokurokuro.hatenablog
  • Path(__file__).resolve().parent で「モジュールが存在するディレクトリ」を絶対パスで取得する。 pythontutorials
  • あとは BASE_DIR / "data" / "input.csv" のように相対パスを組み立てる。 qiita

こうしておけば、

  • PyCharmから実行しても
  • VS Codeのデバッガから実行しても
  • ターミナルで python -m package.module と実行しても

常に「このファイルの場所」からの相対パスになるため、Working Directoryの違いに依存しません。 pythontutorials

階層を跨ぐ場合:parent を使って上に上がる

例えば以下の構成で、src/my_module/data_processor.py から data/global_input.txt を読みたい場合: pythontutorials

my_project/
├── data/
│   └── global_input.txt
└── src/
    └── my_module/
        └── data_processor.py
from pathlib import Path

MODULE_DIR = Path(__file__).resolve().parent          # .../src/my_module
PROJECT_ROOT = MODULE_DIR.parent.parent               # .../my_project

DATA_PATH = PROJECT_ROOT / "data" / "global_input.txt"

parent を何回辿るかで、好きな階層を基準にできます。 pythontutorials


まとめ:何が違って、どう書くのが安全か

整理すると:

  • Pythonの文法としての「相対パス」「相対インポート」はPyCharmでもVS Codeでも同じstackoverflow
  • 違って見えるのは
    • PyCharmがRun Configurationで設定するWorking Directoryとimport pathの扱いがかなり親切で、 pythontutorials
    • VS Codeはターミナル実行に近い素直な挙動で、設定しない限りプロジェクトルートを特別扱いしないため。 stefanocivelli
  • IDEに依存しないための実践的なパターンは
    • ファイルアクセス:__file__ + pathlib.Path で「モジュールの場所基準」でパスを組む。 chayarokurokuro.hatenablog
    • import:プロジェクト構造をパッケージとして成立させた上で、必要なら PYTHONPATH や VS Code設定でルートを明示的に通す。 discuss.python

おまけ:ハマりやすい「よくある落とし穴」

  • 「PyCharmで動いたからOKだと思っていたら、サーバにデプロイしたら全部FileNotFoundError/ModuleNotFoundError」
    → PyCharmのWorking Directoryマジックに依存していたパターン。 reddit
  • Jupyter Notebookで os.getcwd() を基準にパスを書いていて、他のスクリプトから呼ぶと壊れる
    → Notebook基準のCWDと、スクリプト実行時のCWDが違う問題。 chayarokurokuro.hatenablog

このあたりを一度整理しておくと、「IDE変えたら動かなくなった…」というストレスをかなり減らせます。

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?