ローカル開発中、index.yamlの不要なインデックス設定を削除しても復活してしまう場合

datastoreのModelクラスを何度もリファクタしながらローカル開発サーバでテストしていると、index.yamlに、不要なインデックス設定が残ったままになってしまう。
これをきれいにするため、index.yamlの中身を全削除してから実行すると、ローカル開発サーバが、インデックスが必要になった時に自動的にindex.yamlに設定を追加してくれる。
これはとても便利なのだが、もう使っていないはずのインデックス設定が復活してしまうこともある。

これは、いずれかの*.pycに、リファクタ前のプログラムが残ってしまっているのが原因。
ソースコード中の*.pycを全削除して、index.yamlの中身を全削除、そのうえで実行すると万全。

参考

bashで、あるディレクトリ配下の*.pycを削除するコマンド

find . -type f -name '*.pyc' | while read FILE
> do
> rm ${FILE}
> done

$.Deferredを使って、一定間隔でスリープしながらループする

まずはSleepメソッドを自作する。

function Sleep(msec) {
    var d = $.Deferred();
    setTimeout(function() {
      d.resolve();
    }, msec);
    return d.promise();	
}

繰り返したい処理の本体の実装。こちらも、Deferredを返すようにしておく。

function MyFunc(cnt) {
    var d = $.Deferred();
    $("#divProgress").append("<p>in MyFunc() " + cnt + "</p>");
    d.resolve();
    return d.promise();
};

で、これをループで呼ぶ。ここでは10回呼ぶようにしてみる。

function() {
    var counter = [1,2,3,4,5,6,7,8,9,10];
	
    var d = (new $.Deferred()).resolve();
    $.each(counter, function(i, value) {
        d = d
            .then(function() { return MyFunc(value);})
            .then(function() { return Sleep(1000);});
    });
}

ポイントは、ループ開始前にDeferredオブジェクトをresolveしておき、それにつなげてthen, then…とやること。各functionの戻り値として、Deferredオブジェクトをリターンしまくること。

サーバ上のdatastoreをローカル開発サーバにリストアする

サーバのデータをローカル開発サーバ上にリストアする方法。
自身のアプリケーションIDを APP_ID とします。

appcfg.py download_data 
--application=`APP_ID` 
--url=http://APP_ID.appspot.com/_ah/remote_api 
--filename=hoge.dump 
--email=Googleアカウントのメールアドレス

これで、サーバ側のデータが hoge.dump というファイル名で保存されます。
上記例では、全カインドをダウンロードしますが、

--kind=カインド名

のオプションを付与する事により、指定したカインドだけをダウンロードできます。
ここでポイントは、--application=に指定するAPP_IDを、バッククオートで囲むこと。APP_IDに記号が含まれている場合、バッククオートで囲まないと、次のようなエラーになります。

google.appengine.api.datastore\erros.BadRequestError: 
app s~APP_ID cannot access app APP_ID's data

次に、ダウンロードしたものを、ローカルにリストアします。
ローカル開発サーバを起動した状態としたうえで、次のコマンドでリストアを実行。

appcfg.py upload_data
--application=dev~APP_ID 
--url=http://localhost:ポート番号/_ah/remote_api 
--filename=hoge.dump 
--email=Googleアカウントのメールアドレス

ここでのポイントは2つ。
1つめは、ポート番号はAdmin Portではなく、通常のアプリケーションのポートのほうであるということ。
2つめは、applicationIDの指定の仕方。頭に"dev~"を付与することと、ダウンロードの時とは違い、APP_IDに記号が含まれていても、バッククオートで囲まずともエラーにはなりませんでした。


ndbのquery結果の件数を取得する方法

自分の中でごっちゃになっていたので整理する。
Queryのcount()のリファレンスには、こうある。

This returns the same result as len(q.fetch(limit)) but more efficiently.

query = MyModel.query(条件)
cnt = query.count() #OK
cnt = len(query)    #NG
cnt = len(query.fetch()) #OK

list = MyModel.query(条件).fetch(keys_only=True)
cnt = list.count() #NG
cnt = len(list) #OK

queryメソッドの戻り値(Queryオブジェクト)はイテレータではあるがリストではないので、len(query)はできない。
fetchメソッドの戻り値は単なるリストなので、

last_data = list[cnt-1]

みたいなことも出来る。

ローカル開発サーバ起動時に import docker でエラーが発生するようになった(2015/02/26時点)

つい今しがた、1ヶ月ぶりぐらいに、gcloud components update をやったら、ローカル開発サーバが起動しなくなってしまった。
エラーログ

Running dev_appserver with the following flags:
--skip_sdk_update_check=yes --port=12080 --admin_port=8004 --log_level=debug --datastore_path=/Users/noazoh/develop/app_id/datastore/dev_appserver.datastore Python command: /usr/bin/python2.7 Traceback (most recent call last): File "/Users/noazoh/google-cloud-sdk/platform/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/dev_appserver.py", line 83, in _run_file(__file__, globals()) File "/Users/noazoh/google-cloud-sdk/platform/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/dev_appserver.py", line 79, in _run_file execfile(_PATHS.script_file(script_name), globals_) File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/devappserver2.py", line 36, in from google.appengine.tools.devappserver2 import dispatcher File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/dispatcher.py", line 29, in from google.appengine.tools.devappserver2 import module File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/module.py", line 71, in from google.appengine.tools.devappserver2 import vm_runtime_factory File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/vm_runtime_factory.py", line 25, in from google.appengine.tools.devappserver2 import vm_runtime_proxy File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/vm_runtime_proxy.py", line 29, in from google.appengine.tools.devappserver2 import log_manager File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/log_manager.py", line 34, in from google.appengine.tools.docker import containers File "/Users/noazoh/google-cloud-sdk/platform/google_appengine/google/appengine/tools/docker/containers.py", line 47, in import docker ImportError: No module named docker
dockerがimportできないよ、と言っている。 dockerが何かわからなかったので軽く調べたところ、最近betaとして公開された、Managed VMs に関連するもののようだ。 dockerがインストールされている事を前提としてしまっているようだ。 いつか修正されるような気はするのだが、とりあえず動くようにするためには、dockerをインストールする必要がある。
sudo easy_install pip
でpipをインストールしてから、
sudo pip install docker-py
でdockerをインストールすることにより、エラーが解消しました。 Managed VM の機能を使わないのだから(app.yaml にて、vm: true にはしていない)、dockerをimportしないようにするとか何かしてほしい。

ローカル開発サーバでCronJobのテストをする際の注意点

cron:
- description: test job
  url: /hoge
  schedule: every day 18:00
  timezone: Asia/Tokyo
  target: rev01

という感じでタイムゾーンを指定しても、ローカル開発サーバではタイムゾーンを考慮してくれません。