昨日からずっと悩んでいたこれ
が解決した.結局のところプログラムの書き方が間違っていたということではなくて,どこのPIL(Python Image Library; Pillow)を呼び出すかという一点で詰まっていたらしい.まずは結論を書く.
- Google App Engine でも普通にPILは使える.
- Product環境(GAEサーバ上)では,GAE側で用意されているPILをロードする必要がある.
- ローカル開発環境で使うためのPILをGAEプロジェクト内に含めてはいけない.
これだけ守り,app.yaml
や appengine_config.py
の書き方を間違えなければ上手くいく.一つずつ振り返りメモを書いておく.
PIL のインストール
Product環境には PIL は既に用意されているので,GAEプロジェクト内にPILをインストールする必要はない.とは言っても,ローカルサーバを起動して開発するときは各自で端末にインストールする必要がある.このときは,以下のように-t lib指定した pip install をしてはいけない.
# これは gcloud などを入れるときのやり方 $ pip install -t lib Pillow
これだと,プロジェクトルートのライブラリフォルダlib
にインストールされてしまう.lib
フォルダは諸々のソースコードとともにGAEにアップロードされるのでここにPILを入れてしまうと,サーバ上で実行されるアプリケーションはこちらのPILを参照してしまう.正しくは,こうやる.
$ pip install Pillow
なぜ自前で用意したこちらのPIL*1を参照させてはいけないのか?
これはおそらく PIL/Image.py 内でGAEが禁止している関数が呼び出されているからだと思われる.実際のところ,GAEプラグインが入ったPyCharmなどのIDEでこのファイルを開くと,幾つかの行において「System calls are not allowed by Google App Engine sandbox」のような警告が出る. GAE側で提供されているPILはこれらのシステムコールの部分を上手いこと解決してくれているのだろう.
PILに限らず,このページに挙げられているような,既にGAEに組み込まれている Third-party Libraries を手元の環境にインストールするときは気を付けたい.
import文
正解な書き方はこちら(もちろん lib/ には PIL を入れない!)
from PIL import Image
これを書いておくだけで,
- Product環境では GAEが用意した PIL/Image.pyc
- ローカル環境では .pyenv/versions/2.7.10/lib/python2.7/site-packages/PIL/Image.pyc
が読み込まれる.
以下は,少々厄介な話になるが,一応書いておく.ローカルでは動くけどデプロイしたら動かないという現象の表. ここまでで書いたようにしていれば何の問題もないので,全部僕の勘違いが悪いのけれど,折角なのでまとめてみた.
「O」は正常に動く,「X」はエラーで失敗することを表す.マス内の上段はProduct環境での結果,下段はローカル開発環境での結果を記している.
lib/PIL あり | lib/PIL なし(正解) | |
---|---|---|
from PIL import Image (正解) |
X O |
O O |
from lib.PIL import Image |
X X |
- - |
ちなみにサーバでのエラーはすべて ImportError: dynamic module does not define init function (init_imaging) となる.
作業中のメモ
こういうふうに詰まると辛いけれど,ある瞬間を越えて,徐々に全貌が垣間見えてくると楽しくなってくる.
参考ドキュメント
*1:/base/data/home/s~[project-id]/[version].[数字列]/lib/PIL/Image.py