LoginSignup
11
9

More than 5 years have passed since last update.

Ruby の PyCall を使って pyenv な Python を使えるようにする方法

Posted at

想定している環境

  • Mac
  • PyCall はインストール済み
  • pyenv で Python をインストールしていて、PyCall からはこの Python を利用したい

遭遇した問題

こういう Ruby スクリプト1を書いて、

test.rb
require 'pycall/import'

puts "ok"

実行したら、

$ ruby test.rb
ImportError: No module named site

というエラーが出る。

解決方法

pyenv で Python をインストールするときに、共有ライブラリを作るようにオプションを指定する。

$ CONFIGURE_OPTS="--enable-shared" pyenv install 3.6.1

こうすると、~/.pyenv/versions/3.6.1 配下に libpython3.6m.dylib が生成される。2

追跡

site モジュールを探す

$ cd $(pyenv prefix)
$ find . -name site.py
./lib/python3.6/site-packages/jedi/evaluate/site.py
./lib/python3.6/site.py

site.py はある。
ということは、単にパスが通っていないのではないかと推測。

PYTHONPATH を通してみる

PYTHONPATH=$HOME/.pyenv/versions/3.6.1/lib/python3.6 ruby test.rb
  File "/Users/cesare/.pyenv/versions/3.6.1/lib/python3.6/site.py", line 177
    file=sys.stderr)
        ^
SyntaxError: invalid syntax

モジュールは無事に見つかるようになったが、今度はシンタックスエラーが出た。
どうやら、このモジュールをロードした処理系が Python3.x 系のものではないのでは疑惑。

PyCall のソースを軽く眺めた感じ、FFI を使っているようなので、たぶん処理系は共有ライブラリではないかと予想した。

処理系の共有ライブラリを探す

Mac だと共有ライブラリの拡張子は .dylib であることが多いので、ひとまずこんな感じで探してみる。

$ find . -name '*.dylib'
./lib/python3.6/site-packages/matplotlib/.dylibs/libpng16.16.dylib
./lib/python3.6/site-packages/matplotlib/.dylibs/libz.1.2.10.dylib
./lib/python3.6/site-packages/scipy/.dylibs/libgcc_s.1.dylib
./lib/python3.6/site-packages/scipy/.dylibs/libgfortran.3.dylib
./lib/python3.6/site-packages/scipy/.dylibs/libquadmath.0.dylib

幾つか出てきたが、これらは後からインストールしたパッケージのものなので違う。
.pyenv/versions/3.6.1/lib 配下を覗いてみると libpython3.6m.a というファイルなら転がっているが、 .a は共有ライブラリではないし、探し物はこれではない。おそらく libpython3.6m.dylib というファイルが存在していないといけないのでは、と推測。

pyenv で処理系の共有ライブラリが作られるようにする

Python の処理系のソースを取ってきて configure のオプションを調べると --enable-shared というものが指定できるらしいことが分かった。pyenv のインストール時に configure のオプションを指定するには、環境変数 CONFIGURE_OPTS を使えば良いらしい。

ということで、

$ CONFIGURE_OPTS="--enable-shared" pyenv install 3.6.1

こうすれば libpython3.6m.dylib が生成された。
テスト用の Ruby スクリプトを再実行してみると、

$ ruby test.rb
ok

無事動いた。

補足

libpython の検索順序

PyCall が libpython を探す様子は、環境変数 DEBUG_FIND_LIBPYTHON を使うと観測できる。

$ DEBUG_FIND_LIBPYTHON=1 ruby -e 'require "pycall"'

というようにすれば、標準出力に検索したパスが列挙されるようになる。



  1. エラーの原因を探るために余計なものを削って最小限にしたらこのコードになった。標準出力に "ok" と出てこないので、require 'pycall/import' でコケていることが伺える。 

  2. --enable-shared を指定していなかったときは .dylib は生成されておらず、同名の .a ファイルのみが置かれていた。 

11
9
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
11
9