Python の module search path ついて調べてみる
virtualenv
を導入しよう思い立ったのですが、 Python のパス周りについての理解が浅くて何をしているか分からなかったので調査してみました。
目標
(virtualenv
を理解する前提として) Python が module search path をどうやって設定しているか理解する。
※モジュールのインストールについてはまたそのうち。
調査にあたってはこちらのブログを参考にさせて頂きました。 なお以下は自分のマシン(Ubuntu)を前提に書いています。
Python がモジュールをロードする仕組み
import spam
と書いてある場合、 Python は
- まず built-in module の中に
spam
があるかどうか探し - 次に
sys.path
に書いてあるパスの下にspam
がないかを先頭から順番に検索
してくれます。 詳しいことは公式ドキュメント(6.1.2. The Module Search Path)を読んで下さい。短いので。
sys.path
がどうやって作られているか把握できればモジュール切り替えの仕組みが分かりそうな気がしてきました。
何も変更を加えていなければ sys.path
は以下のような感じになるはずです^1。
>>> import sys >>> sys.path ['', '/usr/lib/python2.7', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages']
先頭の ''
はカレントディレクトリを指します。
これで、うっかりテスト用に built-in module と同じ名前のモジュールを作ってしまうと、動かなくなる理由が分かります。
では、残りは一体誰がいつ設定してくれているのでしょうか?
※インポートしたモジュールがどこから来ているかは、
>>> import MySQLdb >>> MySQLdb.__file__ '/usr/lib/python2.7/dist-packages/MySQLdb/__init__.pyc'
で調べることができます。
sys.path
を初めに設定しているのは誰だ?
Python は起動後に lib/pythonx.x
を探して、その下にある site.py
をロードすることで sys.path
を設定しているそうです。
- 起動後に見に行く
lib/pythonx.x
のパス - その後
site.py
によって設定されるパス
を把握しておけば、このあたりのだいたいの挙動がつかめそうですね。
以下はvirtualenv の中の人の解説を見るほうが詳しく分かると思うので、時間のある方はそちらを。
どこの lib/pythonx.x
を見にいくのか?
実行バイナリのパスを起点として上にさかのぼり lib/pythonx.x/os.py
が見つかったらそれを使うようです。
(x.x には python のバージョン番号上二桁が入ります)
言葉で説明してもうまく伝わりませんが、もしディレクトリ構成が以下のようになっている場合、
/usr
/lib
/python2.7/os.py
/local
/bin/python2.7
/usr/local/bin/python2.7
を実行すると/usr/local/lib/python2.7/os.py
を探しに行くが見つからないので- 一つ上の
/usr/lib/python2.7/os.py
を探しにいってめでたく発見
となります。
一番上まで行っても見つからない場合はコンパイル時に --prefix
オプションで設定されたパスが使われるようです^2。
どこの lib/pythonx.x
が使われたかは sys.prefix
で確認することができます^3。
(lib/pythonx.x
を除いたものが入ります)
これでめでたく site.py
をロードすることができます。
site.py
で設定されるパス
site.py
がロードされると、main 関数が呼ばれ、以下の順番で sys.path
にパスが追加されていきます。
(append していくため、最初に追加するものが先頭にきます=優先順位が高いです)
USER_SITE
USER_SITE
は以下のコードで返ってくるパスです。 自分のマシンだと$HOME/.local/lib/pythonx.x/site-packages
になります。```python from sysconfig import get_path import os
USER_SITE = get_path('purelib', '%s_user' % os.name) ```
USER_BASE/local/lib/pythonx.x/dist-packages
USER_BASE
は以下のコードで返ってくるパスです。 自分のマシンだと$HOME/.local
になります。```python from sysconfig import get_config_var
USER_BASE = get_config_var('userbase') ```
USER_BASE/lib/pythonx.x/dist-packages
PREFIX/local/lib/site-packages
orPREFIX/local/lib/dist-packages
PREFIX
にはsys.prefix
またはsys.exec_prefix
が入ります。lib 以下が site-packges になるか dist-packages になるかは OS によって変わってきます^4。
PREFIX/lib/site-packages
orPREFIX/lib/dist-packages
またパスを追加すると同時に、ディレクトリ中の *.pth
をロードして sys.path
に追加します。
site.py
の中では他にも usercustomize
, sitecustomize
というパッケージがロードされます。これらのモジュールを $HOME/.local
下に配置すれば、他のパッケージがインポートされる前に色々な設定を行うことができます。活用法についてはusercustomize による Python カスタマイズが詳しいです。
結論
- バイナリのパスを調べる
- そこから遡っていって
lib/pythonx.x/os.py
のある場所を探す site.py
で何がsys.path
に追加されているかチェック
することで module search path がどうやって設定されているかを理解することができました。