[Zope3-dev] a bug in the Zope 3 (and 2) page template engine

kit BLAKE kitblake at gmail.com
Fri Apr 20 00:45:09 EDT 2007


2007/4/19, Martijn Faassen <faassen at startifact.com>:
> Hi there,
>
> I just tracked down what I believe is a bug in the Zope 3 page template
> engine. I didn't know it was a bug in the Zope 3 page template engine as
> I was tracking down a bug in Silva with Zope 2.10, which of course uses
> the Zope 3 page template engine. For the proposed solution, see the
> bottom of the mail.
>
> Let's consider the following code fragment:
>
> <img tal:replace="structure python:'foo'" tal:attributes="title
> python:'bar'" />
>
> In Zope 2 before Zope 2.10, this line works without error. The
> tal:attributes line of course is pretty useless, as the whole img tag
> gets replaced anyway, but it doesn't bail out with an error.

But those attributes *do* work. If you call that image without the
attributes, it won't have a title attribute. If you keep the tag as it
is, it will have a title attribute.

In fact, you can override an attribute that is provided by the
replace, since the attributes are executed later. For instance you can
override the alt attribute, which in the replace might be taken from
the image's title, with some other string that's more informative.

> In Zope 2.10 and Zope 3.3, this gives us the following error [taken from
> Zope 3]:
>
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 271, in __call__
>      self.interpret(self.program)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 346, in interpret
>      handlers[opcode](self, args)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 534, in do_optTag_tal
>      self.no_tag(stuff[-2], stuff[-1])
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 516, in no_tag
>      self.interpret(program)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 346, in interpret
>      handlers[opcode](self, args)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 760, in do_insertStructure_tal
>      self.insertHTMLStructure(text, repldict)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 784, in insertHTMLStructure
>      gen = AltTALGenerator(repldict, self.engine, 0)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talinterpreter.py",
> line 65, in __init__
>      TALGenerator.__init__(self, expressionCompiler, xml)
>    File
> "/home/faassen/buildout/z331/lib/python/zope/tal/talgenerator.py", line
> 41, in __init__
>      self.CompilerError = expressionCompiler.getCompilerError()
> AttributeError: 'ZopeContext' object has no attribute 'getCompilerError'
>
> The difference between Zope 2 and Zope 3 is that ZopeContext is defined
> somewhere else, but that is irrelevant to this error.
>
> Let's take a look at talgenerator.py, line 41:
>
>          self.CompilerError = expressionCompiler.getCompilerError()
>
> An AttributeError occurs here as getCompilerError does not exist. That's
> correct, as ZopeContext doesn't define one, nor should it. We should've
> gotten a ZopeEngine instance here, and in other code paths, that's what
> we get. This provides the interface ITALExpressionCompiler, which has
> getCompilerError.
>
> What is calling talgenerator with the wrong argument for
> expressionCompiler? Let's go to talinterpreter, around line 784:
>
>      def insertHTMLStructure(self, text, repldict):
>          from zope.tal.htmltalparser import HTMLTALParser
>          gen = AltTALGenerator(repldict, self.engine, 0)
>
> That looks innocent enough. 'self.engine' is passed into AltTALGenerator
> (which in turns ends up in __init__ of TALGenerator). Unfortunately
> self.engine is very very badly named and is in fact a ZopeContext instance!
>
> Why does this bug not happen more often? Because insertHTMLStructure is
> *only* called if tal:attributes there. Let's go to the relevant section,
> do_insertStructure_tal in talinterpreter.py:
>
>      def do_insertStructure_tal(self, (expr, repldict, block)):
>          ...
>          if not (repldict or self.strictinsert):
>              # Take a shortcut, no error checking
>              self.stream_write(text)
>              return
>          if self.html:
>              self.insertHTMLStructure(text, repldict)
>          else:
>              self.insertXMLStructure(text, repldict)
>
> What is going on here is that normally, if no repldict exists (this is
> generated by tal:attributes), stream_write(text) is called. This works
> just fine.
>
> In the case where there *is* a repldict, the broken version of
> insertHTMLStructure is called, leading to this error.
>
> Now what's the hacky fix? It turns out that self.engine has an attribute
> _engine, that *is* the expression compiler. We can therefore fix the
> broken insertHTMLStructure like this:
>
>      def insertHTMLStructure(self, text, repldict):
>          from zope.tal.htmltalparser import HTMLTALParser
>          gen = AltTALGenerator(repldict, self.engine._engine, 0)
>
> That's ugly however. I looked at the old Zope 2 implementation, and that
> looks prettier:
>
>      def insertHTMLStructure(self, text, repldict):
>          from zope.tal.htmltalparser import HTMLTALParser
>          gen = AltTALGenerator(repldict, self.engine.getCompiler(), 0)
>
> Unfortunately, 'getCompiler' doesn't exist in Zope 3, so that won't work
> unless we add it again to the appropriate interface.
>
> I imagine adding this back would be the best way to go. Anyone have any
> objections? I will go and fix this in Zope 3.3 and trunk. I will add a
> test. I will need to make sure the interface also has getCompiler() added.
>
> This might also be a good occasion for a new Zope 2.10 bugfix release.
> Silva is running into this currently, though of course now that we know
> what the cause is we can work around it.

Just please keep the attributes working with replace :-)

Kit


-- 
Kit BLAKE · Infrae · http://infrae.com/ + 31 10 243 7051
Hoevestraat 10 · 3033 GC · Rotterdam + The Netherlands
OpenPGP 0xE67AD0F2 · Contact = http://xri.net/=kitblake


More information about the Zope3-dev mailing list