google app engine でfaviconの配備

favicon.icoファイルの作成

faviconの元になる画像がある場合には、

テキストのfaviconを作りたい場合は、

あたりが便利かな。

ビットマップを自分で作成するツールもあるけど面倒。ものぐさマンだから。

webapp.WSGIApplicationの引数であるURLマッピングのどのURLパターンにもマッチしない場合の処理

webapp.WSGIApplicationのコンストラクタでURLマップを引数として渡します。URLマップに登録されるどのURLパターンにもマッチしない場合の処理を独自で定義できるのか?

ぐぐってみる。

最後のURLパターンを'/.*'とし、otherwise的に使っている。少なくとも最後のURLパターンにマッチするようにして、そこでpage not foundの処理をするのが定石のよう。

eclipseでgoogle app engineプロジェクト(python)作成

eclipsegoogle app engineプロジェクトを作成する手順をメモしておこうと思います。
チュートリアルは、初めてアプリケーションを開発することを想定して作成されています。なので、チュートリアルには、最初の1回だけすればいい作業と、2回目以降に別のアプリケーションを開発するときに、その度にする作業とが一緒になって説明されています。ここでは、2回目以降にアプリケーションを開発するときに、その度に行うeclipseでのプロジェクト作成について説明します。よって、初回のみ必要となる以下の作業は完了しているものとします。

  • Python をダウンロードし、インストールする。
  • Eclipse をダウンロードし、解凍する。
  • Google App Engine SDK をダウンロードし、インストールする。
  • Eclipse に PyDev をインストールする。

作成手順

1. プロジェクト作成

eclipseを起動し、
File -> New -> Other -> Pydev -> Pydev Google App Engine Project
各設定項目は以下のように記入。

2. リクエストハンドラと設定ファイルの作成

helloworld/srcフォルダ以下にhelloworld.py(リクエストハンドラ)とapp.yaml(設定ファイル)を作成。

helloworld.py

print 'Content-Type: text/plain'
print ''
print 'Hello, world!'

app.yaml

application: helloworld
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: helloworld.py
3. Run Configurationの作成

eclipseで、
Run -> Run Configurations -> Python Runを右クリック -> New
以下のように設定し、Applyで保存。

  • Name: helloworld
  • Mainタブ
    • Project: helloworld
    • Main Module: C:\Program Files\Google\google_appengine\dev_appserver.py
  • Argumentsタブ
    • Program arguments:
 "${project_loc}/src"
 --port=9999
4. 実行

3のRun Configurationsウィンドウで、作成したhelloworldを選択して Run。
ブラウザで http://localhost:9999 にアクセスして Hello, world! が表示されることを確認。

google app engineでInternal server error 500の動作を独自に定義

google app engine では、webapp.RequestHandler の継承クラスを定義することでHTTPリクエストのハンドラを定義します。例外(Internal server error 500 に相当)が発生した場合の動作を、handle_exception メソッドをオーバーライドすることで独自に定義できます。

サンプル

http://code.google.com/intl/ja/appengine/docs/python/gettingstarted/usingwebapp.htmlのコードがベース。

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

import cgi
import logging
import sys
import traceback

class MainPage(webapp.RequestHandler):
  def get(self):
    # test
    raise RuntimeError('runtime error')

    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, webapp World!')
    
  def handle_exception(self, exception, debug_mode):
    self.error(500)
    logging.exception(exception)
    if debug_mode:
      lines = ''.join(traceback.format_exception(*sys.exc_info()))
      self.response.clear()
      self.response.out.write('<pre>%s</pre>' % (cgi.escape(lines, quote=True)))
    else:
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write('Internal Server Error 500')
          
application = webapp.WSGIApplication(
                                     [('/', MainPage)],
                                     debug=False)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

debug=Trueのときは、ベースコードと全く同じように動作し、debug=Falseのときは、Internal Server Error 500 をブラウザに表示する点がベースコードの場合と異なる。

ベースコードからの変更点は、

  • handle_exceptionメソッドのオーバーライド
  • handle_exceptionメソッドのオーバーライドに伴う追加のimport
  • debug=False

getメソッドの raise RuntimeError('runtime error') はテスト用に挿入。本来不要。

RequestHandler クラス

RequestHandler クラスの handle_exception メソッドと error メソッド。error は handle_exception から呼び出される。

  def handle_exception(self, exception, debug_mode):
    """Called if this handler throws an exception during execution.

    The default behavior is to call self.error(500) and print a stack trace
    if debug_mode is True.

    Args:
      exception: the exception that was thrown
      debug_mode: True if the web application is running in debug mode
    """
    self.error(500)
    logging.exception(exception)
    if debug_mode:
      lines = ''.join(traceback.format_exception(*sys.exc_info()))
      self.response.clear()
      self.response.out.write('<pre>%s</pre>' % (cgi.escape(lines, quote=True)))
  def error(self, code):
    """Clears the response output stream and sets the given HTTP error code.

    Args:
      code: the HTTP status error code (e.g., 501)
    """
    self.response.set_status(code)
    self.response.clear()

handle_exception では、ステータスコードを 500 にセットし、ログをはいている。そして、デバッグモードであれば、トレース情報を出力。

で、どんな感じで作ればいいか

我々は、RequestHandler の継承クラスで handle_exception をオーバーライドするわけだが、RequestHandler の handle_exception を参考にできる。継承クラスにおける handle_exception の処理の流れとしては、errorメソッドでステータスコードをセットし、ログをはく。その後、デバッグモードの場合の処理と、そうでない場合の処理を記述すればよい。

pythonでURLからクエリパラメータを抽出する。

URL http://localhost/test?id=777&name=hoge からクエリパラメータ idname の値を抽出します。

import urlparse
import cgi

o = urlparse.urlparse('http://localhost/test?id=777&name=hoge')
params = cgi.parse_qs(o.query)
print params['id'][0]
print params['name'][0]

出力結果

777
hoge

Google App Engine: .pyファイルに日本語を入れると 500 Server Error

.pyファイルに日本語を入れると 500 Server Error となります。

サンプルプログラム
http://code.google.com/intl/ja/appengine/docs/python/gettingstarted/usingwebapp.htmlのサンプルににコメントを加えたもの)

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
  def get(self):
    # コメント
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, webapp World!')

application = webapp.WSGIApplication(
                                     [('/', MainPage)],
                                     debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

実行すると、エラーになります。MS932でも、utf-8でもだめでした。

Error: Server Error
The server encountered an error and could not complete your request.

If the problem persists, please report your problem and mention this error message and the query that caused it.

正しくページを取得できる場合、以下のようにブラウザに表示されます。

Hello, webapp World!

対処

" #!-*- coding:utf-8 -*-"を記述することで、対応できるようです。

#!-*- coding:utf-8 -*-
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
  def get(self):
    # コメント
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, webapp World!')

application = webapp.WSGIApplication(
                                     [('/', MainPage)],
                                     debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

以下のような場所に書いてもだめみたいです。

  def get(self):
    #!-*- coding:utf-8 -*-
    # コメント
    self.response.headers['Content-Type'] = 'text/plain'

感想

日本語に問題があることにたどり着くのに時間がかかりました。ローカルでは動いていたので。