[Twisted-web] Re: Nevow and template like files

Nuutti Kotivuori naked at iki.fi
Thu Sep 7 05:18:42 CDT 2006

Valentino Volonghi aka Dialtone wrote:
> On Wed, 06 Sep 2006 11:03:28 +0300, Nuutti Kotivuori <naked at iki.fi> wrote:
>> Valentino Volonghi aka Dialtone wrote:
>> Okay, consider the content page shown above. Now let's assume that
>> most pages don't need the "head" pattern at all. I wouldn't want to
>> write the empty pattern on all pages just because it has to be
>> there, instead I would just want to say that put T.invisible()
>> there if the pattern doesn't exist. (With just the "head" pattern
>> it wouldn't be so much of a problem, but I might later on have to
>> add some more, and I don't want to change every content page just
>> because one content page requires something special.)
>> So this is what I'd like the default for. But ofcourse, if I load
>> the entire page first, and then just use the inevow.IQ machinery,
>> making the default patterns is easy.
> Another solution is to write your own loader by implementing
> inevow.IDocFactory and the using loaders.xmlfile internally so that
> you can catch exceptions and do what you want with the
> behavior. This also sounds reasonable a reasonably easy.

Yup. This I actually did already. It is used below.

>>> loaded = loaders.xmlfile('file') some_pattern =
>>> inevow.IQ(loaded).onePattern('whatever')
>> That seems to work at first - but if there are any macros or render
>> methods inside the file I load, then I get Could not adapt error to
>> IMacroFactory.
> Uhmmm... Indeed the problem of the macro factory is not of secondary
> importance. Basically it doesn't make sense to call load() without a
> MacroFactory. This can be solved by building your own WovenContext
> before passing it to the loader.

I'm still working on this. This would be for the snippet code.

>>> Don't pass the context, what's the problem here?
>> Macros and render methods. In the case of the snippet pages, I
>> would like them to be interpreted in the context of the Fragment
>> class that is loading the entire snippet page.
> That is done by default if you pass the context you are given by any
> of the methods in the Fragment.

But I need to call externally into the Fragment, so it doesn't go
through any of the normal entrypoints - so I guess I will have to
build the context myself.

>> I understand that the macro is not reloaded - but each macro will
>> load the page once. Which would be a problem on a page with
>> hundreds of snippets if every snippet would cause the page to be
>> loaded once.
> It's still a one time cost.


> I'd rewrite the example in the following way:
> class TestPage(rend.Page):
>     docFactory = loaders.xmlfile('testpage.xhtml')
>     patternGenerator = None
>     def macro_title(self, ctx):
>         return inevow.IQ(self.patternGenerator).onePattern('title')
>     def macro_head(self, ctx):
>         return inevow.IQ(self.patternGenerator).onePattern('head')
>     def macro_content(self, ctx):
>         return inevow.IQ(self.patternGenerator).onePattern('content')
> class DataPage(TestPage):
>     patternGenerator = loaders.xmlfile('datapage.xhtml')
>     def macro_test(self, ctx):
>         return 'Test successful.'

> I think the solution above should work without any problems, except
> I'm not sure that macro_test could actually work at all in nevow
> currently, but it may work.

The macro_test thing does not work in your example, but did work in my
original example.

> In case you want to avoid to call loaders.xmlfile('datapage.html')
> each time you might try a simple metaclass solution that grabs
> patternGenerator attribute before building the class and changes it
> to be an instance of loaders.xmlfile.

Not a problem, actually better that way I think.

Okay, I have the first part of my problem now solved, without the
snippet part. This is my solution:

,----[ testsite.py ]
| _no_default = object()
| class patternLoader(loaders.xmlfile):
|     implements(inevow.IDocFactory)
|     def __init__(self, original, pattern, default=_no_default):
|         self.original = original
|         self.pattern = pattern
|         self.default = default
|     def load(self, *args, **kw):
|         doc = self.original.load(*args, **kw)
|         if self.default is _no_default:
|             return inevow.IQ(doc).onePattern(self.pattern)
|         else:
|             try:
|                 return inevow.IQ(doc).onePattern(self.pattern)
|             except stan.NodeNotFound:
|                 return self.default
| class TestPage(rend.Page):
|     docFactory = loaders.xmlfile('testpage.xhtml')
|     datapageFactory = None
|     def macro_title(self, ctx):
|         return patternLoader(self.datapageFactory, 'title')
|     def macro_head(self, ctx):
|         return patternLoader(self.datapageFactory, 'head', T.invisible())
|     def macro_content(self, ctx):
|         return patternLoader(self.datapageFactory, 'content')
| class DataPage(TestPage):
|     datapageFactory = loaders.xmlfile('datapage.xhtml')
|     def macro_test(self, ctx):
|         return 'Test successful.'

It even does the optional part nicely.

Does this look reasonable? I'm diving in the snippet part next.

-- Naked

More information about the Twisted-web mailing list