pythonのテンプレートエンジンmakoを使ってみる

公式サイトは以下の通り。
http://www.makotemplates.org/

インストール

とりあえず手元の環境にインストールしてみる。
rubyのgemとかにあたるpythonのpipでインストールできるらしいので、とりあえずpipを導入することからはじめる。

まずはeasy_installをインストールを・・・と思ったらすでに手元の環境にはあったので飛ばす。
入れるならソースをサイトから持ってきてez_setup.pyとかを叩けばいいのかな?

pipをインストール。
$ sudo easy_install pip
...
動作確認。
$ pip --version
pip 1.1 from /Library/Python/2.7/site-packages/pip-1.1-py2.7.egg (python 2.7)

pipの使い方は以下のサイトが参考になりそう。
http://d.hatena.ne.jp/rudi/20110107/1294409385

makoをインストール。
$ pip search mako <- 念のため
$ sudo pip install mako

使い方

とりあえず使ってみる。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# test_mako.py
#

from mako.template import Template
from mako.lookup import TemplateLookup

def test_text():
    myTemplate = Template("Hello! My name is ${name}!")
    print myTemplate.render(name = "hoge")
    print ""

def test_file():
    """
    $ cat templates/test_file.txt
    Good morning! My name is ${name}!
    """
    myTemplate = Template(filename = "templates/test_file.txt")
    print myTemplate.render(name = "fuga")

def test_lookup():
    myLookup = TemplateLookup(directories = ["templates"])
    """
    $ cat templates/test_lookup.txt
    Goo after noon! My name is ${name}!
    """
    myTemplate = myLookup.get_template("test_lookup.txt")
    print myTemplate.render(name = "puni")

def main():
    print "--- test_text ---"
    test_text()

    print "--- test_file ---"
    test_file()

    print "--- test_lookup ---"
    test_lookup()

main()

$ python test_mako.py
--- test_text ---
Hello! My name is hoge!

--- test_file ---
Good morning! My name is fuga!

--- test_lookup ---
Good after noon! My name is puni!

上記の例は最も単純な"テンプレートファイル内の変数を置き換える"という例。
string指定だったり、ファイル指定だったり、ディレクトリの中からlookupしたりしてるけど、
基本的にやってることは同じ。

テンプレートファイルの中にループとかpythonのコードとかを書くこともできる。

## -*- coding:utf-8 -*-
##
## test_inline.txt
##
## コメント<%doc>
複数行のコメント
タグ末尾の"\"は改行を抑止するためのもの\
\
\
## 埋め込み
変数の展開 : name = ${name}
pythonのコードも埋め込める : name.upper() = ${name.upper()}
\

## pythonのコード<%
space_name = " %s " % name
%>\
filterの利用 : trim_space_name = "${space_name | trim}"
\

## templateが作成されるタイミングでコードを実行した場合は"<% -- %>"の代わりに"<%! -- %>"を使う?<%!
import math
sin_30 = math.sin(math.pi/6)
%>\
sin(30) = ${sin_30}
\

## 制御構文
% if name == "hoge":
hogehogehogehoge!!!!
% else:
who are you?
% endif
% for i in range(10):
% if i < 5:
${i} is smaller than five
% elif i > 5:
${i} is larger than five
% else:
${i} is five
% endif
% endfor
\

## 関数の定義<%def name="checkNullpo(text)">\
% if text == "nullpo":
ga!\
% else:
it is not nullpo\
% endif\
定義した関数の利用 : ${checkNullpo("nullpo")}
filterとしても使える : nullpo = ${name | checkNullpo}
\

## pythonのコードとして関数定義してもOK<%
def checkNullpo2(text):
if text == "nullpo":
return "ga!"
else:
return "it is not nullpo"
%>\
pythonコードで定義した関数の利用 : ${checkNullpo2("nullpo")}
こっちもfilterとして使える : nullpo = ${name | checkNullpo2}

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# test_mako_inline.py
#

from mako.template import Template
from mako import exceptions

def main():
    try:
        myTemplate = Template(filename = "templates/test_inline.txt")
        print myTemplate.render(name = "hoge")
    except:
        print exceptions.text_error_template().render()

main()

$ python test_mako_inline.py
変数の展開 : name = hoge
pythonのコードも埋め込める : name.upper() = HOGE

filterの利用 : trim_space_name = "hoge"

sin(30) = 0.5

hogehogehogehoge!!!!
0 is smaller than five
1 is smaller than five
2 is smaller than five
3 is smaller than five
4 is smaller than five
5 is five
6 is larger than five
7 is larger than five
8 is larger than five
9 is larger than five

定義した関数の利用 : ga!
filterとしても使える : nullpo = it is not nullpo

pythonコードで定義した関数の利用 : ga!
こっちもfilterとして使える : nullpo = it is not nullpo

ところどころに"\"が入っているのは、余計な改行を排除するため。
ただ、最後の改行が消せてなかったりとか改行がどこで入るかの仕様がよくわからん。

まとめると

  1. テンプレートファイルを用意
  2. pythonのコードから必要な変数をrenderの引数に渡して実行

という流れでいいのかな?
renderの引数はxmlとか別の形式で用意して読み込ませるとかするといいかも。