【解決】GAEでCloudSQLに接続しようとしたら、dev_appserver の時だけ ImportError が出る

研究で Google App Engine(Python) を利用していてハマっている。
Flask を使った Web サービスを GAE + CloudSQL で開発しており、 Flask から SQLAlchemy を使って CloudSQL に接続しようとしたところでエラーを吐く。
どうも `MySQLdb` を import しようとして `ImportError: No module name _mysql` のエラーが出ているらしい。
しかも、このエラーは `dev_appserver.py` でローカルで挙動を確認しようとしたときのみ起きる。

エラーログは以下の通り。 
（ `/user` へのアクセスで `sqlalchemy.create_engine()` が実行されるところでエラーが出る）

```bash
$ dev_appserver.py app.yaml                                                                             
INFO     2017-07-27 05:57:06,227 sdk_update_checker.py:231] Checking for updates to the SDK.
INFO     2017-07-27 05:57:06,541 api_server.py:297] Starting API server at: http://localhost:52746
INFO     2017-07-27 05:57:06,545 dispatcher.py:209] Starting module "test" running at: http://localhost:8080
INFO     2017-07-27 05:57:06,548 admin_server.py:116] Starting admin server at: http://localhost:8000
WARNING  2017-07-27 05:57:06,548 devappserver2.py:187] No default module found. Ignoring.
INFO     2017-07-27 05:57:17,859 server.py:125] Server initialized for threading.
ERROR    2017-07-27 05:57:17,875 wsgi.py:263] 
Traceback (most recent call last):
  File "/Users/test/Desktop/study/gae/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 240, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "/Users/test/Desktop/study/gae/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 299, in _LoadHandler
    handler, path, err = LoadObject(self._handler)
  File "/Users/test/Desktop/study/gae/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 85, in LoadObject
    obj = __import__(path[0])
  File "/Users/test/Desktop/test/test/main.py", line 30, in <module>
    engine = create_engine(url, echo=True)
  File "/Users/test/Desktop/test/test/lib/sqlalchemy/engine/__init__.py", line 387, in create_engine
    return strategy.create(*args, **kwargs)
  File "/Users/test/Desktop/test/test/lib/sqlalchemy/engine/strategies.py", line 80, in create
    dbapi = dialect_cls.dbapi(**dbapi_args)
  File "/Users/test/Desktop/test/test/lib/sqlalchemy/dialects/mysql/mysqldb.py", line 110, in dbapi
    return __import__('MySQLdb')
  File "/Users/test/Desktop/test/test/lib/MySQLdb/__init__.py", line 19, in <module>
    import _mysql
  File "/Users/test/Desktop/study/gae/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/python/sandbox.py", line 1024, in load_module
    raise ImportError('No module named %s' % fullname)
ImportError: No module named _mysql
INFO     2017-07-27 05:57:17,882 module.py:809] test: "GET /user HTTP/1.1" 500 -
```
（※秘匿したいところはだいたい `test` に置き換えてます）

`app.yaml` や `appengine_config.py` は以下の通り。

```app.yaml
module: test
runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  script: main.app

libraries:
- name: MySQLdb
  version: "1.2.5"
```

```appengine_config.py
from google.appengine.ext import vendor

# Add any libraries install in the "lib" folder.
vendor.add('lib')
```

ライブラリたちは `lib` ディレクトリに install している。

```requirements.txt
Flask==0.10.1
Flask_SocketIO
itsdangerous==0.24
Jinja2==2.7.3
MarkupSafe==0.23
python-engineio
python-socketio
six==1.9.0
Werkzeug==0.10.4
sqlalchemy==1.1.9
mysql-python==1.2.5
```

```bash
$ sudo pip install -t lib -r requirements.txt
```

試しに、ローカルの Python で `MySQLdb` を import したらエラーは出なかった。

```py
Python 2.7.9 (default, May  9 2017, 14:55:04) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb
>>> 
```

さらに試しに、 `dev` バージョンとしてGAE環境に上げてみたら問題なくデプロイされ、
問題なく CloudSQL への接続が確認できた。

```bash
$ appcfg.py -A test-project -V dev update app.yaml
03:15 PM Host: appengine.google.com
03:15 PM Application: test-project (was: None); module: test; version: dev (was: None)
03:15 PM Starting update of app: test-project, module: test, version: dev
03:15 PM Getting current resource limits.
03:15 PM Scanning files on local disk.
03:15 PM Scanned 500 files.
03:15 PM Cloning 544 application files.
03:15 PM Compilation starting.
03:16 PM Compilation completed.
03:16 PM Starting deployment.
03:16 PM Checking if deployment succeeded.
03:16 PM Deployment successful.
03:16 PM Checking if updated app version is serving.
03:16 PM Completed update of app: test-project, module: test, version: dev
$
```

ローカル環境に `dev_appserver.py` でデプロイしたときのみ、 `_mysql` が迷子になっている。

引き続き解決にあたる。
（ローカルで css や js の挙動確認ができないとしんどいなあ）

---

（追記： 2017/07/27 18:50）

> ローカル環境に `dev_appserver.py` でデプロイしたときのみ、 `_mysql` が迷子になっている。

これを中心にぐぐっていくと、以下の stackoverflow にぶつかった。

[google app engine - how to add lib folder?](https://stackoverflow.com/questions/34662595/google-app-engine-how-to-add-lib-folder)
> appengine_config.py will run in the GAE environment or when you run dev_appserver, which emulates the GAE environment. However if you're ever running outside of a GAE environment, make sure your PYTHONPATH includes the lib folder you want to import from.

つまり、 ローカルで `dev_appserver.py` 実行した状態では `appengine_config.py` が実行されないので、 `lib` ディレクトリを `PYTHONPATH` に加えておけよ、とのこと。

実際にその通りに `PYTHONPATH` を設定し、

```bath
$ export PYTHONPATH=/Users/test/Desktop/test/test/lib:$PYTHONPATH
```

実行すると動いてくれた。

（その後、公式ドキュメントを読み漁ったが、 `dev_appserver.py` と `appengine_config.py` の関係が明記されてるものは見つからなかった...orz）

ひとまず解決してよかった。文章にまとめると問題点が整理されてよい。