[Twisted-web] Re: new nevow know-how needs...;-)

Alex Martelli twisted-web@twistedmatrix.com
Fri, 05 Mar 2004 12:15:56 +0100


James Y Knight wrote:
   ...
> 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>".

Wonderful!  This new behavior, as you describe it, perfectly matches my
intuition about "what _should_ happen".


> 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>".

One more thing that feels just right -- two "calls" (data directives that
mention data_count), two "executions" (actual calls to the function).


> Note that returning deferreds from the data_ is now supported. In

Again, wonderful!  I'll be able to dismantle my pesky "fake renderers"
that basically dealt with data being intrinsically deferred.

> 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.

Superb.  Feels simple, direct, and predictable.


> 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.

Hmmm, yes, I had to write an adapter to IContainer for my application's
"business object" class just to redirect data lookups back to the resource,
because this kind of thing just seemed to happen "when it felt like it":-).

> 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

Sure does!  Thanks James.  The one aspect that's still a bit obscure to
me is, how is this distinction between "data=data_foo" (where I want to
use function or method named data_foo as the data supplier) and
"data=directive('bar')" (where I want the IContainer adaptation and
call to .get) distinguished, _when the HTML comes from an on-disk
template, rather than from a stan expression in my code_?  My application
is thoroughly based on on-disk templates -- I don't code much stan stuff
at all, except basically for quick and dirty prototyping of stuff that's
destined to move to an on-disk template soon anyway.  So, won't every
HTML construct coming from a template of some form such as:

<span nevow:data="bar">
    etc, etc
</span>

end up "compiled" into a "span(data=directive('bar'))[etc, etc]" anyway,
leading me right back into subtle issues of "WHERE is this 'bar'
going to be looked up, depending on phase of the moon etc"...?  Or are
there two different ways I should code that '<span nevow:data="bar">' in
the on-disk template depending on whether I mean one thing or the other?

Anyway, if I understand correctly, I'll now get this "new behavior" with
just a cvs up on the (stand-alone) Nevow CVS, which I intend to do at
once, so I can experiment.  But, if I can get some explanation of how
things are designed to work, this might help.  Perhaps as small a change
as "if the current IData remembrance is not adaptable to IContainer [and
perhaps also if it is, but its .get raises?], just back off to 'external'
IData remembrances until a satisfactory one is found" would be sufficient
for my purposes, and -- if you can confirm there's no other solution that
better matches the current intent of the code -- I could experiment with
that kind of approach.


Thanks again,

Alex