emptypage.jp


texttemplate.py

テキストのテンプレート処理を行うシンプルな Python モジュールです。条件分岐や反復処理が可能です。

履歴

2012-09-09 (rev. 2199)
テンプレートの {% exec %} ブロック内で __escape__(value) 関数が定義されている時はそれがデフォルトのエスケープ関数として使用されるようになった。{{ x }} とするだけで、__escape__(x) の結果が適用・展開される。__escape__ を定義すると自動的に __nonescape__ も定義され、{{ __nonescape__(x) }} と書くと x はエスケープ処理されなくなる。
2010-03-12 (rev. 1514)
compile() 関数廃止。Template クラスのインスタンスを作成するようにした。このほうがわかりやすい。
if ブロックで elif 節を使えるように。
コメントタグの追加。
コマンドラインオプションの長いオプション名を見直し。
その他結構リファクタした。
2008-03-04 (rev. 1183)
行頭空白のエスケープ機能を追加。
2008-01-16 (rev. 977)
コマンドラインの仕様を変更し、-e, -f オプションを追加。
2008-01-15 (rev. 976)
for ブロック内で既に存在する変数が更新されなかったのを修正。
2008-01-14 (rev. 973)
公開。

目次

はじめに

Python には、テキストのテンプレート処理用のライブラリが星の数ほどあります。こんな辺境のライブラリに手を出す前に、ほかのライブラリも検討してみましょう。「ウノウラボ Unoh Labs: Pythonのテンプレートエンジン」に、CheetahDjangoJinja をはじめとする代表的な Python 用のテンプレートシステムが紹介されています。標準モジュールのテンプレート文字列処理機能string.Template)もお忘れなく。

強いて本ライブラリの利点を挙げるとするなら、

といったところでしょうか。

ダウンロード

どちらも中身は同じです。

コマンドライン

texttemplate.py はスクリプトとして使用することもできます。

使い方
python texttemplate.py [options] template
-e, --execute <code>
テンプレートを評価する前に実行する Python コードを指定します。
-f, --filename <script>
テンプレートを評価する前に実行する Python スクリプトを指定します。

template にはテンプレートを記述したファイル名を指定します。与えられたテンプレートファイルを評価して、結果を標準出力に表示します。

モジュール

モジュールとして、以下のクラスや関数を利用できます。

Template クラス

class texttemplate.Template(code)
テンプレートのインスタンスを作成します。code にはテンプレートのコードを指定します。

属性

encoding
テンプレート文字列のエンコーディング名が格納されます。エンコーディングが指定されていなかった場合には None になっています。

メソッド

evaluate(namespace)
テンプレートを評価して、その結果文字列を返します。namespace は、変数の展開に用いられる、名前空間の辞書です。キーワード引数の形式で指定することもできます。

テンプレートの構文

コメント

{# ... #} で囲まれた部分はコメントです。評価時には空文字列になります。コメントは入れ子にできません。

変数

{{...}} で囲まれた部分は、式として評価されその結果に置き換わります。式として有効な任意の Python コードを書くことができます。

例 1:
>>> import texttemplate  # 以降の例では省略
>>> code = 'hello, {{ name }}!'
>>> template = texttemplate.Template(code)
>>> template.evaluate(name='world')
'hello world!'
例 2:
>>> code = '{{ a + b * c }}'
>>> template = texttemplate.Template(code)
>>> template.evaluate(a=1, b=2, c=3)
'7'
例 3:
>>> code = "{{ d['A'] }}, {{ d.get('Z', 0) }}"
>>> template = texttemplate.Template(code)
>>> template.evaluate(d={'A': 1, 'B': 2, 'C': 3})
'1, 0'
>>> template.evaluate(d={'A': 'X'})
'X, 0'

ブロック

{%...%} で囲まれた部分は、ブロックを指定するタグとして認識されます。ブロックには後述の種類があります。ブロックの終了は、{% end %} で指定します。ブロックは入れ子にできます。

if ブロック

expr を評価して、真であればブロックの中身を評価します。偽の場合には空文字列になります。{% else %} 節を付けて、偽の場合に評価される中身を指定できます。Python 同様、elif 節も付けることができます。

例:
>>> code = '{% if a == b %}equal{% else %}different{% end %}!'
>>> template = texttemplate.Template(code)
>>> template.evaluate(a=1, b=1)
'equal!'
>>> template.evaluate(a=1, b=2)
'different!'
>>>

for ブロック

iterable として渡された列挙可能なオブジェクトに対して要素をひとつずつ取り出して target に挙げられた変数に代入し、ブロックの中身を繰り返し評価します。Python の for 構文とほぼ同じですが、break, continue, else に相当する機能はありません。テンプレートを宣言的なコードにしたいのであえてそうなっています。

例 1:
>>> code = '{% for i in range(3) %}{{ i+1 }}! {% end %}'
>>> template = texttemplate.Template(code)
>>> template.evaluate()
'1! 2! 3! '
例 2:
>>> code = '{% for i, name in enumerate(names) %}{{ i+1 }}:{{ name }} {% end %}'
>>> template = texttemplate.Template(code)
>>> template.evaluate(names=['apple', 'banana', 'orange'])
'1:apple 2:banana 3:orange '

exec ブロック

exec ブロック内には、テンプレート用の文字列ではなく、Python のコードを書きます。このブロックは Template インスタンスの生成時にコードとして評価され、コードの実行結果がテンプレートのインスタンスに格納されます。テンプレートの評価時には、このブロックは空文字列として扱われます。

「テンプレート先頭に exec ブロックを置いて、そのテンプレート固有の定数やエスケープ用関数を定義する」というのが、テンプレートファイルの一般的な書き方になります。

例:
>>> code = """{% exec %}
... def escape(s):
...   return s.replace('<', '&lt;').replace('>', '&gt;')
...
... {% end %}{{ escape(data) }}"""
>>> template = texttemplate.Template(code)
>>> template.evaluate(data='<i>like this</i>')
'&lt;i&gt;like this&lt;/i&gt;'

(上記の escape 関数は説明のために間に合わせで載せた欠陥品なのでこのまま使わないように!)

encoding ブロック

このブロックで囲まれた文字列は、コンパイル時にテンプレートのエンコーディング名として認識されます。これによりテンプレートは指定されたエンコーディングによってデコードされ、Unicode 文字列に変換されます。エンコーディング名は、Template インスタンスの encoding 属性に格納されます。評価時にはブロック内の文字列がそのまま出力されます。

テンプレートはエンコーディング情報をデータのデコードに使うだけで、評価時に結果文字列をエンコードするのには使いません。必要であれば、template.evaluate(namespace).encode(template.encoding) のように、明示的にエンコードしてください。

テキスト

コメント、変数、ブロックのタグ以外の文字列は本文としてそのまま出力結果に使われます。ただし、改行の直前に \ を書くと、その行は後続の行と連結されます。また、行頭の空白に続けて \ を書くと、行頭からそこまでの空白が除去されます。この仕組みは、可読性を上げるためにブロック指定を行分けして書く時などに、結果に余分な改行が入らないようにしたい場合に使います。

構文について

今のところ、texttemplate.py のテンプレートは Django やその影響を受けた Jinja に似たものになっています。しかし個人的には {% foo %}こういうタグ{% end %} はあまり好きになれないので、将来変更するかもしれません。この点に関しては、web.py の Aaron Swartz のテンプレート論のほうに同意。

サンプル

samples ディレクトリに簡単なテンプレートファイルのサンプルが入っています。参考にしてください。コマンドラインから次のように実行すると、結果を確認できます。

C:\...\texttemplate>python texttemplate.py samples\listdir.txt.tmpl
- samples
- texttemplate.html
- texttemplate.py

サンプルファイル

listdir.txt.tmpl
カレントディレクトリにあるファイルの一覧を表示します。
listdir.html.tmpl
listdir.txt.tmpl の HTML 版です。エスケープ処理に注意してください。

想定 FAQ

テキスト内で “{{” や “{%” などを表示するにはどうしたらいいの?

{{ '{{' }}{{ '{%' }} を使ってください。

変数タグの中で “}}” を使ったり、ブロックタグの中で “%}” を使うには?

こちらはちょっと面倒くさいです。ひとつは {{ '}'+'}' }} とする方法があります。もうひとつの方法として、テンプレート冒頭で {% exec %}var_etag = '}}'{% end %} などとしておくか、テンプレートを使用するスクリプトの側で template.evaluate(var_etag='}}') としておいて、{{ var_etag }} とすることでも実現できます。

前者の方法の応用としてじつは {{ '}''}' }} と書くこともできます。

検討事項

ライセンスおよび免責

パブリックドメインです。好きに使ってください。

なお、本モジュールを使って生じた損害等についての責任は負いかねますのであしからず。


$Date: 2012-09-09 23:39:16 +0900 (日, 09  9 2012) $