On Mar 1, 2004, at 6:02 AM, Alex Martelli wrote:
I currently have _one_ serious problem understanding what's going on (or supposed to go on): nevow:data="whatever" directives. I don't understand the rules regarding WHEN this causes a lookup (e.g. of 'data_whatever' on a rend.Page subclass), WHAT it looks things up when it does do a lookup, WHEN it calls method 'data_whatever' thereafter, and with what arguments. Basically, I must be missing something important here -- it mostly feels that once I've used a nevow:data= directive, I lose control of what's going to happen and get nasty surprises. So, I'm gradually moving to using nevow.render= directives instead -- mixing the "model" aspect (getting the data on which rendering is to be based) with the "view" one (rendering it). Not nice, but with nevow.render I _do_ appear to have complete control, or maybe better expressed, _understanding_, of what's gonna happen...
I just totally changed how this worked today. I'll describe what *now* happens, which is different from what used to happen: def data_foo(context, data): return 4 def render_foo(context, data): return context.tag['Hi: '+data] span(data=data_foo, renderer=render_foo) This will first call data_foo, returning '4'. data_foo's context will be the context of the span (context.tag is the span tag), without its IData remembered yet (thus, the data argument will be whatever the containing data is, in most cases, the rend.Page instance. Then, render_foo will be called, with the tag's context, again, but now with the result of data_foo's result (4) as 'data'. Thus, this will result in the string "<span>Hi: 4</span>". Now, take another example: number = 0 def data_count(context, data): global number number = number + 1 return number span(data=data_count)[str, ' ', str, ' ', str], ' ', span(data=data_count)[str, ' ', str, ' ', str] This will result in the string "<span>1 1 1</span> <span>2 2 2</span>". Note that returning deferreds from the data_ is now supported. In detail, the way this works: nevow.flat.flatstan.TagRenderer calls (quite paraphrased) "context.remember(convertToData(data, context), IData)" when it first sees a tag, right before it calls the function specified by the render= attribute. convertToData causes functions to be called, 'directive' statements to be looked up, and deferreds to be handled. Then, when the actual rendering function is called, it does context.locate(IData), which finds the previously remembered data, and passes it directly (no further fiddling is done!) to the data argument of the renderer. If you use a directive('name') form in a data=, this causes the current data to be adapted to IContainer before calling .get on it. There are currently adapters for dict, list, and tuple for this. Thus, def data_foo(context, data): return defer.succeed({'foo':1, 'bar':2}) span(data=data_foo)[span(data=directive('foo'))[str], span(data=directive('bar')[str]] Should work as expected, and call data_foo *once*, and produce "<span><span>1</span><span>2</span></span>". Hope this helps, James