Python の WSGI プログラムで出力をするたび
yield '<h1>テスト</h1>'.encode('utf-8')
yield '<p>あいうえお</p>'.encode('utf-8')
と encode('utf-8') を付けていましたが、どう考えてもこれは手間だし無駄な気がします。Flask を使えばいいと言われたらお終いなので、何か手はないかと考えてみました。
Python にはデコレータ(decorator) という仕組があって、任意の関数の前後に好きな処理を追加することができます。関数も第一級オブジェクトである Python の特性をうまく利用した素敵機能ですね。
yield を扱った decorator の例があまり見つからなくてやや苦戦しましたが、次のような decorator で実際に動作することを確認しました。
def encode_utf8(func):
def wrapper(*args, **kwargs):
for r in func(*args, **kwargs):
yield r.encode('utf-8')
return wrapper
前回の WSGI でフォームを扱うプログラムに適用させてみたのが次のコードです。
# coding: utf-8
from urllib.parse import parse_qsl
def encode_utf8(func):
def wrapper(*args, **kwargs):
for r in func(*args, **kwargs):
yield r.encode('utf-8')
return wrapper
@encode_utf8
def application(env, res):
res('200 OK', [('Content-type', 'text/html; charset=utf-8')])
# Form
yield '<form method="get" accept-charset="UTF-8"><p><input type="text" name="get" value=""><input type="submit" value="GET"></form>'
yield '<form method="post" accept-charset="UTF-8"><p><input type="text" name="post" value=""><input type="submit" value="POST"></form>'
# Request method
method = env.get('REQUEST_METHOD')
yield '<p>Method: {}</p>'.format(method)
# Get query string
if method == 'POST':
wsgi_input = env['wsgi.input']
query = wsgi_input.read(int(env.get('CONTENT_LENGTH', 0))).decode('utf-8')
else:
query = env.get('QUERY_STRING', '')
yield '<p>Query: {}</p>'.format(query)
# Output form fields
form = parse_qsl(query)
for k, v in form:
yield '<p>{} = {}</p>'.format(k, v)
行数は変らないのでぱっと見の変化少ないですが、書き忘れて Internal Server Error をもらう率は少し減りますね。