さて、この仕組みはどうやって実現されているかというと、turbogears/widgets/forms.py の CompoundInputWidget.params_for(self, item, **params)で実装されている。このメソッド自体はTableFormのテンプレートから呼び出される。こんな感じになっている(抜粋)。
<tr py:for="i, field in enumerate(fields)"
class="${i%2 and 'odd' or 'even'}"
>
<th>
<label class="fieldlabel" for="${field.field_id}" py:content="field.label" />
</th>
<td>
<span py:replace="field.display(value_for(field), **params_for(field))" />
</td>
</tr>「py:replace="field.display(value_for(field), **params_for(field))"」の部分がキモで、**params_for(field)の呼び出しによって、「params_for(field)の戻り値の辞書を、キー=値に展開して引数として渡す」ということになる。これはPythonの言語仕様なんだけど、まだ慣れてないので直感的にうまく把握できない。
params_for()の実装は、retrieve_params_by_path() を呼び出している。retrieve_params_by_path()の実装は、こう。
def retrieve_params_by_path(params, path):
if not path:
return None
else:
if not isinstance(path[0], tuple):
path = adapt_path(path)
for name, index in path:
params_to_parse = params.copy()
params = {}
for k,v in params_to_parse.iteritems():
if isinstance(v, dict):
if name in v:
params[k] = v[name]←←←←←←←←←←←←←①
if index is not None:
if isinstance(params[k], list):
try:
params[k] = params[k][index]
except IndexError:
params[k] = None
else:
params[k] = None
return params込み入っているんだけど、①の部分で、kは引数名(上のvalueとかattrsとか)、vはkの値の辞書で、nameはWidgetのnameなので、「value={'input1':'XYZ'}からk='value'、name='input1'、v[name]='XYZ'、しかるにparams['value']='XYZ'」という処理をしている。最終的にこのparamsを返すわけだが、そのときparamsは {'value':'XYZ', 'attrs':'{'readonly':True}} みたいな値になっているわけだ。
関数のパラメータを辞書に詰め込んで取り出したり、それをまたバラして関数を呼び出したり。なるほどこうやって使うと便利なんだなあ。