Genshiのtag面白いね
TracでWikiMacroを作る場合はWikiMacroBaseを継承してexpand_macroを実装します。
本家にあるサンプルだとこうですね。
from datetime import datetime # Note: since Trac 0.11, datetime objects are used internally from genshi.builder import tag from trac.util.datefmt import format_datetime, utc from trac.wiki.macros import WikiMacroBase class TimeStampMacro(WikiMacroBase): """Inserts the current time (in seconds) into the wiki page.""" revision = "$Rev$" url = "$URL$" def expand_macro(self, formatter, name, text): t = datetime.now(utc) return tag.b(format_datetime(t, '%c'))
returnのところにあるようにGenshiのtagでHTML構文を作ります。インタラクティブシェルだと以下のようになります。
>>> print tag.b('hoge') <b>hoge</b>
タグをネストさせたり属性を指定する場合はこんな感じですね。
>>> print tag.a(tag.b('hoge'), href='a.html') <a href="a.html"><b>hoge</b></a>
キーワード引数は最後に指定しないといけません。
>>> print tag.a(href='a.html', tag.b('hoge')) File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg
で、このtagはどうなっているのかgenshi/builder.pyを見てみるとトップレベルで以下のようになっています。ElementFactoryクラスのオブジェクトですね。
tag = ElementFactory()
tag.b('hoge')と書けますが、ElementFactoryクラスのオブジェクトであるtagにはbなんてアトリビュートはありません。
どうやっているかというと__getattr__でフックしてElementオブジェクトを返しています。つまりHTMLのタグをアトリビュートとして用意しないでダイナミックに作っているわけです。
Elementの__call__は下記のようになっています。
def __call__(self, *args, **kwargs): """Append any positional arguments as child nodes, and keyword arguments as attributes. :return: the element itself so that calls can be chained :rtype: `Element` :see: `Fragment.append` """ self.attrib |= _kwargs_to_attrs(kwargs) Fragment.__call__(self, *args) return self
__call__は特殊メソッドで
インスタンスが関数として ``呼ばれた'' 際に呼び出されます; このメソッドが定義されている場合、x(arg1, arg2, ...) は x.__call__(arg1, arg2, ...) を短く書いたものに なります。
http://www.python.jp/doc/nightly/ref/callable-types.html
なので、tag.b('hoge')とtag.b.__call__('hoge')は同じです。tag.a(tag.b('hoge'), href='a.html')とtag.a.__call__(tag.b.__call__('hoge'), href='a.html')も同じですね。
pythonでは*argsのようにすればキーワード指定しない引数をいくつでも受け付けることができます。いわゆる可変長引数。ポインタじゃないよ。**argsとやればキーワード指定された未定義の引数をとれます。ポインタのポインタじゃないよ。詳しくはみんpy p173-174参照
__call__の引数のargsでネストしたタグを受け取りkwargsでタグの属性を受け取っているわけですね。この辺の引数の仕組みは呪文のようではありますがAPI設計者にとっては強力でしょう。
いやあ上記のようなHTMLのタグや属性の扱い方はずっとJavaをやってきた僕からすると新鮮ですね。面白い。