風柳メモ

ソフトウェア・プログラミング関連の覚書が中心。

さくらのレンタルサーバでユーザディレクトリ上にPythonをおいて外部モジュールを自由に導入する試み

前置き

Google App Engineの料金体系改定によって、いろいろとモチベーションが下がったりもしましたが…。
せっかくPythonをかじりかけたことでもあるので、とりあえず、さくらのレンタルサーバ上でPythonを動かして、いろいろ試せるようにしてみようと思い立ちました。
自宅のPCには一応環境あるのだけれど、公開するには(マシン的に)不安定だし。

さくらのレンタルサーバでは、標準でPython 2.7.2が入っています(2012/3/10現在)。
なので、わざわざ自分のディレクトリに入れなくても……とも思いましたが、lxmlなどの外部モジュールを使おうとすると面倒が多そうな気がしたので、丸ごと入れてしまえという発想だったわけなんですが。
いろいろとはまったわけです、予感どおり。

手順

(username)となっているところは、各自のユーザ名で読み替えてください。

1. Python 2.7.2 をインストール
$ mkdir -p ~/temp
$ cd ~/temp
$ wget http://www.python.org/ftp/python/2.7.2/Python-2.7.2.tgz
$ tar xvfz ./Python-2.7.2.tgz
$ cd ./Python-2.7.2
$ ./configure --prefix=$HOME  --with-threads --enable-shared
$ make
$ make install
$ ln -s ~/lib/python2.7/ ~/lib/python

自分は"--prefix=$HOME"をつけて、~/ 直下にインストールされるようにしました。なお、作業用ディレクトリとして ~/temp を用意しています。


これで、

  • /home/(username)/bin 下にPythonの実行ファイル
  • /home/(username)/lib/python2.7 下にライブラリ関係(上記手順では便宜上、~/lib/python というバージョン名無しのシンボリックリンクを作成)
  • /home/(username)/share/man/man1 下にマニュアル

が、それぞれインストールされます。

2. 環境変数の変更(.bash_profile編集)

ちなみに自分はcsh→bashに変更して使用しています。cshの人は~/.cshrcの編集になると思いますが、よくわからないです…。

$ vim ~/.bash_profile

で、環境変数を設定している辺りに、

export PYTHONHOME=$HOME
export PYTHONPATH=$PYTHONHOME/lib/python
export LD_LIBRARY_PATH=$PYTHONHOME/lib:$LD_LIBRARY_PATH
export LIBRARY_PATH=$PYTHONHOME/lib:$LIBRARY_PATH
export PATH=$HOME/bin:$HOME/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:$PATH

のようにPYTHONHOME、PYTHONPATH、LD_LIBRARY_PATH、LIBRARY_PATHを設定し、PATHの先頭に$HOME/binを付け加え、

$ source ~/.bash_profile

で反映。

3. easy_installのインストール
$ cd ~/temp
$ wget http://peak.telecommunity.com/dist/ez_setup.py
$ python ez_setup.py
4. 外部モジュールのインストール
$ easy_install -UZ lxml
$ easy_install -UZ jinja2
$ easy_install -UZ MySQL-python

などなど、後はお好みで必要な外部モジュールをインストール。

なお、easy_install で導入した外部モジュールは、

  • /home/(username)/lib/python2.7/site-packages/

に入るようになります。

CGIスクリプトを書くときの注意

  1. 拡張子は .cgi に。
  2. パーミッションは 705(755,700)等に設定(suEXECによる制限と思われる)。
  3. 1行目(シバン・shebang)には LD_LIBRARY_PATH 環境変数の設定をし、インストールしたPython実行ファイルへのPATHを書くこと。

3. ですが、具体的には、1行目を

#! /usr/bin/env -S LD_LIBRARY_PATH=/home/(username)/lib /home/(username)/bin/python

のようにする、ということです。
気になる人は、他の環境変数(PYTHONHOME、PYTHONPATH、LIBRARY_PATH)も追加してもよいですが、少なくとも LD_LIBRARY_PATH だけは無いと、当方の環境では動きませんでした。

具体例

実際にレンタルサーバ上においてあるPythonのCGIファイルです(動作確認可)
外部モジュールとしてeasy_installした、lxmlおよびjinja(Jinja2)を使用しています。

#! /usr/bin/env -S LD_LIBRARY_PATH=/home/furyu-tei/lib PYTHONHOME=/home/furyu-tei PYTHONPATH=/home/furyu-tei/lib/python LIBRARY_PATH=/home/furyu-tei/lib /home/furyu-tei/bin/python
# -*- coding: utf-8 -*-

# ■ さくらレンタルサーバのユーザディレクトリ上に Python をインストールして外部モジュールを動かす例

import os
import urllib2
import lxml.html
import platform
from jinja2 import Environment, FileSystemLoader


TPL_FILE = 'test-py27_userdir.tpl'
TGT_URL = 'http://www.sakura.ne.jp/'


def guess_autoescape(template_name):
  if template_name is None or '.' not in template_name: return False
  ext = template_name.rsplit('.', 1)[1]
  return ext in ('html', 'htm', 'xml', 'tpl')


if __name__ == '__main__':
  rsp = urllib2.urlopen(TGT_URL)
  content = rsp.read()
  document = lxml.html.fromstring(content)
  document.make_links_absolute(TGT_URL)
  try:
    page_title = document.xpath('//title')[0].text
  except:
    page_title = u'(タイトル不明)'
  links = document.xpath('//a')
  
  template = Environment(loader=FileSystemLoader(os.getcwd()),extensions=['jinja2.ext.autoescape'],autoescape=guess_autoescape).get_template(TPL_FILE)
  tvalue = dict(
    title = u'『さくらインターネット』レンタルサーバのユーザディレクトリ上に入れたPython%(ver)sで動作' % dict(ver=platform.python_version()),
    sub_title1 = u'環境変数',
    env_list = sorted([(name,os.environ[name]) for name in os.environ]),
    sub_title2 = u'lxmlの試行(『%(title)s』(%(url)s)上リンクのURL)' % dict(title=page_title, url=TGT_URL),
    url_list = sorted(list(set([l.get('href','') for l in links]))),
  )
  
  print 'Status: 200 OK'
  print 'Content-Type:text/html; charset=utf-8'
  print ''
  print template.render(tvalue).encode('utf-8','ignore')

#■ end of file

はまったところ

モジュールによっては、うまくインストールできない場合有り

たとえば、MySQL-python(MySQLdb) を入れようとすると、コンパイル(リンク)エラー発生。

$ easy_install -UZ MySQL-python
:
(中略)
:
/usr/bin/ld: cannot find -lpython2.7

ちなみに、LD_LIBRARY_PATHはちゃんと設定はされていました。

なので、リンカ(ld)にPATHを教えてあげるため、gccが参照する環境変数である LIBRARY_PATH を

export LIBRARY_PATH=$PYTHONHOME/lib:$LIBRARY_PATH

のように、/home/(username)/lib を参照するよう設定することで、回避できるようになりました。

CGI が動かない…

ローカルでは動作していたものが、ブラウザからアクセスするとエラーに。
すべて 500: Internal Server Errorになりましたが、ログを見ると、シバンの書き方により動作が異なります。

■パターン(1)

#! /usr/bin/env python

の場合は、

Traceback (most recent call last):
  File "test.cgi", line 8, in <module>
    import lxml.html
ImportError: No module named lxml.html

となり、さくらの標準のPythonが動き、かつ、インストールされていないlxmlが見つからないと言ってます。
ユーザディレクトリ上のpython実行ファイルのフルパスを記載する必要あり。


■パターン(2)

#! /usr/bin/env /home/furyu-tei/bin/python

の場合は、

Traceback (most recent call last):
  File "test.cgi", line 7, in <module>
    import urllib2
  File "/home/furyu-tei/lib/python2.7/urllib2.py", line 94, in <module>
    import httplib
  File "/home/furyu-tei/lib/python2.7/httplib.py", line 69, in <module>
    from array import array
ImportError: /home/furyu-tei/lib/python2.7/lib-dynload/array.so: Undefined symbol "PyUnicodeUCS2_FromUnicode"

となり、ユーザディレクトリに入れたPythonを実行しようとしているが、モジュール読み込み時に不明なシンボルがあると言っています。
ライブラリに対するパスが通っていないと出る→LD_LIBRARY_PATHの設定が必要。


ということで、


■パターン(3)

#! /usr/bin/env -S LD_LIBRARY_PATH=/home/furyu-tei/lib /home/furyu-tei/bin/python

とすることにより、正常に動作するようになりました。