[Twisted-Python] Zope PageTemplates for Twisted

Sune Kirkeby sune-twisted at mel.interspace.dk
Thu May 30 15:13:02 MDT 2002


Whee!

In a fit of God-how-I-hate-all-other-template-languages I decided to
take another look at Zopes PageTemplates (TAL, METAL and friends).

I ended up liking the beast so much I decided to start porting it to
Twisted, which resulted in the attached t.w.tal module.  It is still
very incomplete, and most likely also very buggy.  But, in the
interest of feedback I post it here.

It dislikes it when I reuse deferreds (t.w.w.RenderSession becomes
very confused and claims to be "rendering unknown" for all but the
first instances).

Other than that the only problem I have had so far was that I had to
remove a str'ing in the TAL-engine.

-- 
Sune Kirkeby | 5 out of 4 people have trouble with fractions.
-------------- next part --------------
# I need a license.

"""
TAL interpreter for Twisted.
"""

import string, types

from twisted.web import widgets
from twisted.web.server import NOT_DONE_YET

from TAL.TALDefs import quote
from TAL.TALDefs import TAL_VERSION, isCurrentVersion, getProgramMode
from TAL.TALDefs import TALError, METALError
from TAL.TALParser import TALParser
from TAL.TALInterpreter import TALInterpreter
from TAL.TALGenerator import TALGenerator
#from TAL.DummyEngine import DummyEngine
from PageTemplates.Expressions import getEngine

class TwistedTALInterpreter:
    def __init__(self, engine):
        self.engine = engine
        self.mode = None

    def interpret(self, program):
        lst = []

        for (opcode, args) in program:
            handler = getattr(self, 'do_' + opcode, None)
            if not handler:
                raise TALError, ("Unknown opcode: %s (%r)" % (opcode, args))

            result = handler(args)
            if isinstance(result, types.ListType):
                lst.extend(result)
            elif result:
                lst.append(result)

        return lst

    def do_version(self, version):
        assert version == TAL_VERSION

    def do_mode(self, mode):
        assert mode in ("html", "xml")
        self.mode = mode

    def do_rawtextOffset(self, (text, somenumber)):
        return text

    def do_rawtextColumn(self, (text, somenumber)):
        return text

    def do_startTag(self, (name, attributes)):
        txt = '<%s' % name

        def format_attribute(attr):
            if len(attr) == 2:
                return attr[1]
            elif attr[2] == 0:
                return '%s=%s' % (attr[0], quote(attr[1]))
        attributes = map(format_attribute, attributes)
        formatted = string.join([ a for a in attributes if a ], ' ')

        if formatted:
            txt = txt + ' ' + formatted
        txt = txt + '>'

        return txt

    def do_optTag(self, foo):
        return self.interpret(foo[5])

    def do_loop(self, (name, expr, block)):
        iterator = self.engine.setRepeat(name, expr)
        lst = []
        while iterator.next():
            lst.extend(self.interpret(block))
        return lst

    def do_beginScope(self, attrs):
        self.engine.beginScope()

    def do_endScope(self, nothing):
        self.engine.endScope()

    def do_insertText(self, (text, insteadOf)):
        val = self.engine.evaluateText(text)
        if not val:
            return
        if val is self.engine.getDefault():
            self.interpret(insteadOf)
        return val

    def do_useMacro(self, (macroName, macroExpr, compiledSlots, block)):
        macro = self.engine.evaluateMacro(macroExpr)
        if macro is self.engine.getDefault():
            macro = block

        else:
            if not isCurrentVersion(macro):
                raise METALError("macro %s has incompatible version %s" %
                                 (`macroName`, `getProgramVersion(macro)`),
                                 self.position)
            mode = getProgramMode(macro)
            if not mode == self.mode:
                raise METALError("macro %s has incompatible mode %s" %
                                 (`macroName`, `mode`), self.position)

        return self.interpret(macro)

    def do_defineMacro(self, (macroName, macro)):
        pass

class TALWidget(widgets.Widget):
    def preDisplay(self, request):
        pass

    def getTemplates(self, request):
        raise UnimplementedError

    def getContext(self, request):
        return {}

    def display(self, request):
        self.preDisplay(request)

        ctx = {
            'request': request,
            'here': self,
            'macros': {},
        }
        ctx.update(self.getContext(request))

        engine = getEngine()
        context = engine.getContext(ctx)
        generator = TALGenerator(engine)

        result = []
        for template in self.getTemplates(request):
            parser = TALParser(generator)
            parser.parseString(template)
            program, macros = parser.getCode()
            ctx['macros'].update(macros)

            interpreter = TwistedTALInterpreter(context)
            result.extend(interpreter.interpret(program))

        return result

if __name__ == '__main__':
    import sys
    from twisted.web import widgets
    from TAL.TALParser import TALParser
    from TAL.DummyEngine import DummyEngine
    
    parser = TALParser()
    engine = DummyEngine()
    interpreter = TwistedTALInterpreter(engine)

    engine.setGlobal('here/title', 'TITLE, GOD DAMMIT!')

    file = sys.argv[1]
    parser.parseFile(file)
    program, macros = parser.getCode()
    result = interpreter.interpret(program)

    class Request:
        def write(self, txt):
            print txt
        def finish(self):
            pass

    widgets.RenderSession(result, Request())


More information about the Twisted-Python mailing list