pip 非対応のモジュールを virtualenv で使うには(Windows 編)

プロジェクトごとに独立した Python インストール環境を作れる virtualenv は便利なのだけど、pip でのインストールに対応していないモジュールを使いたいとなるととたんに難しいことになる。Windows では標準で拡張モジュールをビルドできないこともあってインストーラーの配布形式(bdist_wininst)が長い間使われてきたので、古いパッケージだと pip でインストールできる wheel 形式での配布がなされていないものが多い。wxPython とか py2exe とかである。

しかし、一旦インストーラーでシステムの Python にインストールしたうえで、それらを virtualenv 環境のライブラリのディレクトリにリダイレクトしてやれば、virtualenv 環境からでもインポートできるようになる。リダイレクトといっても、ようは site-packages にハードリンクやジャンクションを張るだけだけど。

たとえば wxPython の場合、通常どおりインストーラーで導入してシステムの Python から使ってみると、

C:\Users\mshibata>python
Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win 32
Type "help", "copyright", "credits" or "license" for more information.
>>> import wx
>>> wx.__file__
'C:\\Python27\\lib\\site-packages\\wx-3.0-msw\\wx\\__init__.pyc'
>>>

パッケージが C:\Python27\lib\site-packages\wx-3.0-msw\wx にあるということがわかる。そこで、virtualenv 環境があるディレクトリで、

C:\Users\mshibata\work>virtualenv venv
New python executable in venv\Scripts\python.exe
Installing setuptools, pip...done.
C:\Users\mshibata\work>mklink /j venv\Lib\site-packages\wx C:\Python27\lib\site-packages\wx-3.0-msw\wx
venv\Lib\site-packages\wx <<===>> C:\Python27\lib\site-packages\wx-3.0-msw\wx のジャンクションが作成されました

とすれば、

C:\Users\mshibata\work>venv\Scripts\activate.bat
(venv) C:\Users\mshibata\work>python
Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win 32
Type "help", "copyright", "credits" or "license" for more information.
>>> import wx
>>>

とこのようにインポートできる。パッケージではなく単体のモジュールの場合は、ジャンクションではなくモジュールの .py ファイルへのハードリンクを張ればよい(けど、そういうのはふつうに virtualenv 環境にインストールできるか)。

py2exe もこの方法で virtualenv 環境で使えるようにすることができる。しかしこれだと py2exe がさらに読みこんでいる別のモジュールまではリダイレクトされていないので、ImportError が発生することがある。その場合は適宜足りてないモジュールへのリンクを張ればよい(いいかげんだ)。

最初だけなので手作業でやってもいいのだけれど、いちいちインポートされてるモジュールのパスを調べてコピペするのは面倒なので、上記の作業をやってくれるスクリプトを作ってみた。

C:\Users\mshibata\work>redirect-package.py venv wx py2exe
venv\Lib\site-packages\wx <<===>> C:\Python27\lib\site-packages\wx-3.0-msw\wx のジャンクションが作成されました
venv\Lib\site-packages\py2exe <<===>> C:\Python27\lib\site-packages\py2exe のジャンクションが作成されました

と、こんなふうに使う。