Pythonのライブラリを動的に追加する

  • 1月 14, 2009 08:44

ちょっと site.addsitedirについて分らないことがあったのでメモ。

  • Python起動時にsiteモジュールは自動的にインポートされる。(-S をつけるとインポートしない)
  • site-packages自体とその直下の.pthファイルに書いてあるモジュールをsys.pathに追加する。

を行っています。

後から、「/hoge/my-lib/複数のモジュール」にPATHを通す必要があるとき

sys.path.insert(0, '/hoge/my-lib')

だと上手くいかない。なぜならモジュールの多くは

my-lib/
hoge-2.1.1/
/hoge /__init__.py /hogemodule1.py

のような構成になっているので、my-lib直下に__init__.pyが無いためにPythonがその下のモジュールを見つけられないためだ。

どうしても、sys.pathを使う場合は

import sys
import os

sys.path.insert(0, os.path.join('/hoge/my-lib/hoge-2.1.1'))
sys.path.insert(0, os.path.join('/hoge/my-lib/fuga-2.2.1'))
sys.path.insert(0, os.path.join('/hoge/my-lib/hum-1.1.3'))

のように一つ一つのモジュールを指定してあげる必要がある。

しかし、それだと非常に面倒なので/hoge/my-lib/mypackage.pthのような.pthファイルに

hoge-2.1.1
huga-2.2.1
hum-1.1.3

とテキストファイルを用意し

from site import addsitedir

addsitedir('/hoge/my-lib')

と、してあげると.pthファイルに書いてあるモジュールを一つ一つにPATHを通してくれる。

pinaxのサンプルプロジェクトについているmanage.pyは以下のように拡張されているのだが、addsitedir()とsys.pathの共存はこのようにするらしい。

path = addsitedir(join(settings.PINAX_ROOT, "libs/external_libs"), set())
if path:
    sys.path = list(path) + sys.path
sys.path.insert(0, join(settings.PINAX_ROOT, "apps/external_apps"))
sys.path.insert(0, join(settings.PINAX_ROOT, "apps/local_apps"))
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))

addsitedirの第2引数にset()をつけている。

set()が無いとNoneで返されてしまうのだが、set()を渡すことで追加したモジュールのpathのsetを返してくれる。

それをsys.pathに追加して、さらにsys.path.insertで別のものを追加してるようだ。

ちなみにpthファイルを手で書くのが面倒だと思ってたらpinaxのapps/external_libsにupdate.shというシェルスクリプトがあった

#!/bin/sh
ls -1 `dirname $0` | grep -v packages.pth | grep -v update.sh > `dirname $0`/packages.pth
cat `dirname $0`/packages.pth

これは便利だ。

以下、site.pyのaddsitedir関数の部分。

def addsitedir(sitedir, known_paths=None):
    """Add 'sitedir' argument to sys.path if missing and handle .pth files in
    'sitedir'"""
    if known_paths is None:
        known_paths = _init_pathinfo()
        reset = 1
    else:
        reset = 0
    sitedir, sitedircase = makepath(sitedir)
    if not sitedircase in known_paths:
        sys.path.append(sitedir)        # Add path component
    try:
        names = os.listdir(sitedir)
    except os.error:
        return
    names.sort()
    for name in names:
        if name.endswith(os.extsep + "pth"):
            addpackage(sitedir, name, known_paths)
    if reset:
        known_paths = None
    return known_paths