[Zope3-checkins] CVS: Zope3/src/zope/tal - .cvsignore:1.1.2.1 __init__.py:1.1.2.1 changes.txt:1.1.2.1 driver.py:1.1.2.1 dummyengine.py:1.1.2.1 history.txt:1.1.2.1 htmltalparser.py:1.1.2.1 interfaces.py:1.1.2.1 ndiff.py:1.1.2.1 readme.txt:1.1.2.1 runtest.py:1.1.2.1 setpath.py:1.1.2.1 taldefs.py:1.1.2.1 talgenerator.py:1.1.2.1 talgettext.py:1.1.2.1 talinterpreter.py:1.1.2.1 talparser.py:1.1.2.1 timer.py:1.1.2.1 translationcontext.py:1.1.2.1 xmlparser.py:1.1.2.1

Jim Fulton jim@zope.com
Mon, 23 Dec 2002 14:33:28 -0500


Update of /cvs-repository/Zope3/src/zope/tal
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/tal

Added Files:
      Tag: NameGeddon-branch
	.cvsignore __init__.py changes.txt driver.py dummyengine.py 
	history.txt htmltalparser.py interfaces.py ndiff.py readme.txt 
	runtest.py setpath.py taldefs.py talgenerator.py talgettext.py 
	talinterpreter.py talparser.py timer.py translationcontext.py 
	xmlparser.py 
Log Message:
Initial renaming before debugging

=== Added File Zope3/src/zope/tal/.cvsignore ===
.path


=== Added File Zope3/src/zope/tal/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/tal/changes.txt ===
TAL changes

  This file contains change information for the current release. 
  Change information for previous versions can be found in the
  file HISTORY.txt.

    Version 1.5.0

      Features Added

        - Line and column numbers are added to more exceptions.


=== Added File Zope3/src/zope/tal/driver.py ===
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Driver program to test METAL and TAL implementation.

Usage: driver.py [options] [file]
Options:
    -h / --help
        Print this message and exit.
    -H / --html
    -x / --xml
        Explicitly choose HTML or XML input.  The default is to automatically
        select based on the file extension.  These options are mutually
        exclusive.
    -l
        Lenient structure insertion.
    -m
        Macro expansion only
    -s
        Print intermediate opcodes only
    -t
        Leave TAL/METAL attributes in output
    -i
        Leave I18N substitution strings un-interpolated.
"""

import os
import sys

import getopt

if __name__ == "__main__":
    import setpath                      # Local hack to tweak sys.path etc.

# Import local classes
import zope.tal.taldefs
from zope.tal.dummyengine import DummyEngine
from zope.tal.dummyengine import DummyTranslationService

FILE = "tests/input/test01.xml"

class TestTranslations(DummyTranslationService):
    def translate(self, domain, msgid, mapping=None, context=None,
                  target_language=None):
        if msgid == 'timefmt':
            return '%(minutes)s minutes after %(hours)s %(ampm)s' % mapping
        elif msgid == 'jobnum':
            return '%(jobnum)s is the JOB NUMBER' % mapping
        elif msgid == 'verify':
            s = 'Your contact email address is recorded as %(email)s'
            return s % mapping
        elif msgid == 'mailto:${request/submitter}':
            return 'mailto:bperson@dom.ain'
        elif msgid == 'origin':
            return '%(name)s was born in %(country)s' % mapping
        return DummyTranslationService.translate(self, domain, msgid,
                                                 mapping, context,
                                                 target_language)

class TestEngine(DummyEngine):
    def __init__(self, macros=None):
        DummyEngine.__init__(self, macros)
        self.translationService = TestTranslations()

    def evaluatePathOrVar(self, expr):
        if expr == 'here/currentTime':
            return {'hours'  : 6,
                    'minutes': 59,
                    'ampm'   : 'PM',
                    }
        elif expr == 'context/@@object_name':
            return '7'
        elif expr == 'request/submitter':
            return 'aperson@dom.ain'
        return DummyEngine.evaluatePathOrVar(self, expr)


# This is a disgusting hack so that we can use engines that actually know
# something about certain object paths.  TimeEngine knows about
# here/currentTime.
ENGINES = {'test23.html': TestEngine,
           'test24.html': TestEngine,
           'test26.html': TestEngine,
           'test27.html': TestEngine,
           'test28.html': TestEngine,
           'test29.html': TestEngine,
           'test30.html': TestEngine,
           'test31.html': TestEngine,
           'test32.html': TestEngine,
           }

def usage(code, msg=''):
    # Python 2.1 required
    print >> sys.stderr, __doc__
    if msg:
        print >> sys.stderr, msg
    sys.exit(code)

def main():
    macros = 0
    mode = None
    showcode = 0
    showtal = -1
    strictinsert = 1
    i18nInterpolate = 1
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hHxlmsti",
                                   ['help', 'html', 'xml'])
    except getopt.error, msg:
        usage(2, msg)
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(0)
        if opt in ('-H', '--html'):
            if mode == 'xml':
                usage(1, '--html and --xml are mutually exclusive')
            mode = "html"
        if opt == '-l':
            strictinsert = 0
        if opt == '-m':
            macros = 1
        if opt == '-n':
            versionTest = 0
        if opt in ('-x', '--xml'):
            if mode == 'html':
                usage(1, '--html and --xml are mutually exclusive')
            mode = "xml"
        if opt == '-s':
            showcode = 1
        if opt == '-t':
            showtal = 1
        if opt == '-i':
            i18nInterpolate = 0
    if args:
        file = args[0]
    else:
        file = FILE
    it = compilefile(file, mode)
    if showcode:
        showit(it)
    else:
        # See if we need a special engine for this test
        engine = None
        engineClass = ENGINES.get(os.path.basename(file))
        if engineClass is not None:
            engine = engineClass(macros)
        interpretit(it, engine=engine,
                    tal=(not macros), showtal=showtal,
                    strictinsert=strictinsert,
                    i18nInterpolate=i18nInterpolate)

def interpretit(it, engine=None, stream=None, tal=1, showtal=-1,
                strictinsert=1, i18nInterpolate=1):
    from zope.tal.talinterpreter import TALInterpreter
    program, macros = it
    assert TALDefs.isCurrentVersion(program)
    if engine is None:
        engine = DummyEngine(macros)
    TALInterpreter(program, macros, engine, stream, wrap=0,
                   tal=tal, showtal=showtal, strictinsert=strictinsert,
                   i18nInterpolate=i18nInterpolate)()

def compilefile(file, mode=None):
    assert mode in ("html", "xml", None)
    if mode is None:
        ext = os.path.splitext(file)[1]
        if ext.lower() in (".html", ".htm"):
            mode = "html"
        else:
            mode = "xml"
    if mode == "html":
        from zope.tal.htmltalparser import HTMLTALParser
        p = HTMLTALParser()
    else:
        from zope.tal.talparser import TALParser
        p = TALParser()
    p.parseFile(file)
    return p.getCode()

def showit(it):
    from pprint import pprint
    pprint(it)

if __name__ == "__main__":
    main()


=== Added File Zope3/src/zope/tal/dummyengine.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Dummy TALES engine so that I can test out the TAL implementation.
"""

import re
import sys

from zope.tal.taldefs import NAME_RE, TALESError, ErrorInfo
from zope.tal.interfaces import ITALESCompiler, ITALESEngine
from zope.interfaces.i18n import ITranslationService
from zope.interfaces.i18n import IDomain

Default = object()

name_match = re.compile(r"(?s)(%s):(.*)\Z" % NAME_RE).match

class CompilerError(Exception):
    pass

class DummyEngine:

    position = None
    source_file = None

    __implements__ = ITALESCompiler, ITALESEngine

    def __init__(self, macros=None):
        if macros is None:
            macros = {}
        self.macros = macros
        dict = {'nothing': None, 'default': Default}
        self.locals = self.globals = dict
        self.stack = [dict]
        self.translationService = DummyTranslationService()

    def getCompilerError(self):
        return CompilerError

    def setSourceFile(self, source_file):
        self.source_file = source_file

    def setPosition(self, position):
        self.position = position

    def compile(self, expr):
        return "$%s$" % expr

    def uncompile(self, expression):
        assert (expression.startswith("$") and expression.endswith("$"),
            expression)
        return expression[1:-1]

    def beginScope(self):
        self.stack.append(self.locals)

    def endScope(self):
        assert len(self.stack) > 1, "more endScope() than beginScope() calls"
        self.locals = self.stack.pop()

    def setLocal(self, name, value):
        if self.locals is self.stack[-1]:
            # Unmerge this scope's locals from previous scope of first set
            self.locals = self.locals.copy()
        self.locals[name] = value

    def setGlobal(self, name, value):
        self.globals[name] = value

    def evaluate(self, expression):
        assert (expression.startswith("$") and expression.endswith("$"),
            expression)
        expression = expression[1:-1]
        m = name_match(expression)
        if m:
            type, expr = m.group(1, 2)
        else:
            type = "path"
            expr = expression
        if type in ("string", "str"):
            return expr
        if type in ("path", "var", "global", "local"):
            return self.evaluatePathOrVar(expr)
        if type == "not":
            return not self.evaluate(expr)
        if type == "exists":
            return self.locals.has_key(expr) or self.globals.has_key(expr)
        if type == "python":
            try:
                return eval(expr, self.globals, self.locals)
            except:
                raise TALESError("evaluation error in %s" % `expr`)
        if type == "position":
            # Insert the current source file name, line number,
            # and column offset.
            if self.position:
                lineno, offset = self.position
            else:
                lineno, offset = None, None
            return '%s (%s,%s)' % (self.source_file, lineno, offset)
        raise TALESError("unrecognized expression: " + `expression`)

    def evaluatePathOrVar(self, expr):
        expr = expr.strip()
        if self.locals.has_key(expr):
            return self.locals[expr]
        elif self.globals.has_key(expr):
            return self.globals[expr]
        else:
            raise TALESError("unknown variable: %s" % `expr`)

    def evaluateValue(self, expr):
        return self.evaluate(expr)

    def evaluateBoolean(self, expr):
        return self.evaluate(expr)

    def evaluateText(self, expr):
        text = self.evaluate(expr)
        if text is not None and text is not Default:
            text = str(text)
        return text

    def evaluateStructure(self, expr):
        # XXX Should return None or a DOM tree
        return self.evaluate(expr)

    def evaluateSequence(self, expr):
        # XXX Should return a sequence
        return self.evaluate(expr)

    def evaluateMacro(self, macroName):
        assert (macroName.startswith("$") and macroName.endswith("$"),
            macroName)
        macroName = macroName[1:-1]
        file, localName = self.findMacroFile(macroName)
        if not file:
            # Local macro
            macro = self.macros[localName]
        else:
            # External macro
            import driver
            program, macros = driver.compilefile(file)
            macro = macros.get(localName)
            if not macro:
                raise TALESError("macro %s not found in file %s" %
                                 (localName, file))
        return macro

    def findMacroDocument(self, macroName):
        file, localName = self.findMacroFile(macroName)
        if not file:
            return file, localName
        import driver
        doc = driver.parsefile(file)
        return doc, localName

    def findMacroFile(self, macroName):
        if not macroName:
            raise TALESError("empty macro name")
        i = macroName.rfind('/')
        if i < 0:
            # No slash -- must be a locally defined macro
            return None, macroName
        else:
            # Up to last slash is the filename
            fileName = macroName[:i]
            localName = macroName[i+1:]
            return fileName, localName

    def setRepeat(self, name, expr):
        seq = self.evaluateSequence(expr)
        return Iterator(name, seq, self)

    def createErrorInfo(self, err, position):
        return ErrorInfo(err, position)

    def getDefault(self):
        return Default

    def translate(self, domain, msgid, mapping):
        return self.translationService.translate(domain, msgid, mapping)
    

class Iterator:

    def __init__(self, name, seq, engine):
        self.name = name
        self.seq = seq
        self.engine = engine
        self.nextIndex = 0

    def next(self):
        i = self.nextIndex
        try:
            item = self.seq[i]
        except IndexError:
            return 0
        self.nextIndex = i+1
        self.engine.setLocal(self.name, item)
        return 1

class DummyDomain:
    __implements__ = IDomain

    def translate(self, msgid, mapping=None, context=None,
                  target_language=None):
        # This is a fake translation service which simply uppercases non
        # ${name} placeholder text in the message id.
        #
        # First, transform a string with ${name} placeholders into a list of
        # substrings.  Then upcase everything but the placeholders, then glue
        # things back together.
        def repl(m):
            return mapping[m.group(m.lastindex).lower()]
        cre = re.compile(r'\$(?:([_A-Z]\w*)|\{([_A-Z]\w*)\})')
        return cre.sub(repl, msgid.upper())

class DummyTranslationService:
    __implements__ = ITranslationService

    def translate(self, domain, msgid, mapping=None, context=None,
                  target_language=None):
        # Ignore domain
        return self.getDomain(domain).translate(msgid, mapping, context,
                                                target_language)

    def getDomain(self, domain):
        return DummyDomain()


=== Added File Zope3/src/zope/tal/history.txt ===
TAL history

  This file contains change information for previous versions.
  Change information for the current release can be found
  in the file CHANGES.txt.

    Version 1.4.0

      Features Added

        - Added TAL statement: omit_tag="[<boolean expr>]" replaces
          the statement tag with its contents if the boolean
          expression is true or omitted.

        - The TAL and METAL namespaces can be applied to tag names,
          tags in these namespaces are removed from rendered output
          (leaving the contents in place, as with omit_tag)
          whenever attributes in these namespaces would be, and
          tag attributes without explicit namespaces default to the
          tag's namespace (per XML spec).

    Version 1.3.3

      Bugs Fixed

        - tal:atributes was creating stray attributes in METAL
          expansion, and there was no unit test for this behavior.

        - tal:attributes parsing was not catching badly malformed
          values, and used "print" instead of raising exceptions.

    Version 1.3.2

      Features Added

        - Adopted Zope-style CHANGES.txt and HISTORY.txt
        - Improved execution performance
        - Added simple ZPT vs. TAL vs. DTML benchmarks, run by markbench.py

    Version 1.3.0

      Features Added

        - New builtin variable 'attrs'.

      Bug Fixed

        - Nested macros were not working correctly.

    Version 1.2.0

      Features Added

        - The 'if' path modifier can cancel any TAL action.

      Bug Fixed

        - tal:attributes inserted empty attributes into source.

    Version 1.1.0

      Features Added
        - TAL does not try to parse replacement structural text.
        - Changed tests to match TAL's omitted attributes.

    Version 1.0.0

        - Various minor bugs fixed

    Version 1.0.0b1

	- All functionality described in the Project Wiki is implemented


=== Added File Zope3/src/zope/tal/htmltalparser.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Parse HTML and compile to TALInterpreter intermediate code.
"""

import sys

from zope.tal.talgenerator import TALGenerator
from HTMLParser import HTMLParser, HTMLParseError
from zope.tal.taldefs import \
     ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS, METALError, TALError, \
     I18NError

BOOLEAN_HTML_ATTRS = [
    # List of Boolean attributes in HTML that may be given in
    # minimized form (e.g. <img ismap> rather than <img ismap="">)
    # From http://www.w3.org/TR/xhtml1/#guidelines (C.10)
    "compact", "nowrap", "ismap", "declare", "noshade", "checked",
    "disabled", "readonly", "multiple", "selected", "noresize",
    "defer"
    ]

EMPTY_HTML_TAGS = [
    # List of HTML tags with an empty content model; these are
    # rendered in minimized form, e.g. <img />.
    # From http://www.w3.org/TR/xhtml1/#dtds
    "base", "meta", "link", "hr", "br", "param", "img", "area",
    "input", "col", "basefont", "isindex", "frame",
    ]

PARA_LEVEL_HTML_TAGS = [
    # List of HTML elements that close open paragraph-level elements
    # and are themselves paragraph-level.
    "h1", "h2", "h3", "h4", "h5", "h6", "p",
    ]

BLOCK_CLOSING_TAG_MAP = {
    "tr": ("tr", "td", "th"),
    "td": ("td", "th"),
    "th": ("td", "th"),
    "li": ("li",),
    "dd": ("dd", "dt"),
    "dt": ("dd", "dt"),
    }

BLOCK_LEVEL_HTML_TAGS = [
    # List of HTML tags that denote larger sections than paragraphs.
    "blockquote", "table", "tr", "th", "td", "thead", "tfoot", "tbody",
    "noframe", "ul", "ol", "li", "dl", "dt", "dd", "div",
    ]

TIGHTEN_IMPLICIT_CLOSE_TAGS = (PARA_LEVEL_HTML_TAGS
                               + BLOCK_CLOSING_TAG_MAP.keys())


class NestingError(HTMLParseError):
    """Exception raised when elements aren't properly nested."""

    def __init__(self, tagstack, endtag, position=(None, None)):
        self.endtag = endtag
        if tagstack:
            if len(tagstack) == 1:
                msg = ('Open tag <%s> does not match close tag </%s>'
                       % (tagstack[0], endtag))
            else:
                msg = ('Open tags <%s> do not match close tag </%s>'
                       % ('>, <'.join(tagstack), endtag))
        else:
            msg = 'No tags are open to match </%s>' % endtag
        HTMLParseError.__init__(self, msg, position)

class EmptyTagError(NestingError):
    """Exception raised when empty elements have an end tag."""

    def __init__(self, tag, position=(None, None)):
        self.tag = tag
        msg = 'Close tag </%s> should be removed' % tag
        HTMLParseError.__init__(self, msg, position)

class OpenTagError(NestingError):
    """Exception raised when a tag is not allowed in another tag."""

    def __init__(self, tagstack, tag, position=(None, None)):
        self.tag = tag
        msg = 'Tag <%s> is not allowed in <%s>' % (tag, tagstack[-1])
        HTMLParseError.__init__(self, msg, position)

class HTMLTALParser(HTMLParser):

    # External API

    def __init__(self, gen=None):
        HTMLParser.__init__(self)
        if gen is None:
            gen = TALGenerator(xml=0)
        self.gen = gen
        self.tagstack = []
        self.nsstack = []
        self.nsdict = {'tal': ZOPE_TAL_NS,
                       'metal': ZOPE_METAL_NS,
                       'i18n': ZOPE_I18N_NS,
                       }

    def parseFile(self, file):
        f = open(file)
        data = f.read()
        f.close()
        try:
            self.parseString(data)
        except TALError, e:
            e.setFile(file)
            raise

    def parseString(self, data):
        self.feed(data)
        self.close()
        while self.tagstack:
            self.implied_endtag(self.tagstack[-1], 2)
        assert self.nsstack == [], self.nsstack

    def getCode(self):
        return self.gen.getCode()

    def getWarnings(self):
        return ()

    # Overriding HTMLParser methods

    def handle_starttag(self, tag, attrs):
        self.close_para_tags(tag)
        self.scan_xmlns(attrs)
        tag, attrlist, taldict, metaldict, i18ndict \
             = self.process_ns(tag, attrs)
        self.tagstack.append(tag)
        self.gen.emitStartElement(tag, attrlist, taldict, metaldict, i18ndict,
                                  self.getpos())
        if tag in EMPTY_HTML_TAGS:
            self.implied_endtag(tag, -1)

    def handle_startendtag(self, tag, attrs):
        self.close_para_tags(tag)
        self.scan_xmlns(attrs)
        tag, attrlist, taldict, metaldict, i18ndict \
             = self.process_ns(tag, attrs)
        if taldict.get("content"):
            self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
                                      i18ndict, self.getpos())
            self.gen.emitEndElement(tag, implied=-1)
        else:
            self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
                                      i18ndict, self.getpos(), isend=1)
        self.pop_xmlns()

    def handle_endtag(self, tag):
        if tag in EMPTY_HTML_TAGS:
            # </img> etc. in the source is an error
            raise EmptyTagError(tag, self.getpos())
        self.close_enclosed_tags(tag)
        self.gen.emitEndElement(tag)
        self.pop_xmlns()
        self.tagstack.pop()

    def close_para_tags(self, tag):
        if tag in EMPTY_HTML_TAGS:
            return
        close_to = -1
        if BLOCK_CLOSING_TAG_MAP.has_key(tag):
            blocks_to_close = BLOCK_CLOSING_TAG_MAP[tag]
            for i in range(len(self.tagstack)):
                t = self.tagstack[i]
                if t in blocks_to_close:
                    if close_to == -1:
                        close_to = i
                elif t in BLOCK_LEVEL_HTML_TAGS:
                    close_to = -1
        elif tag in PARA_LEVEL_HTML_TAGS + BLOCK_LEVEL_HTML_TAGS:
            i = len(self.tagstack) - 1
            while i >= 0:
                closetag = self.tagstack[i]
                if closetag in BLOCK_LEVEL_HTML_TAGS:
                    break
                if closetag in PARA_LEVEL_HTML_TAGS:
                    if closetag != "p":
                        raise OpenTagError(self.tagstack, tag, self.getpos())
                    close_to = i
                i = i - 1
        if close_to >= 0:
            while len(self.tagstack) > close_to:
                self.implied_endtag(self.tagstack[-1], 1)

    def close_enclosed_tags(self, tag):
        if tag not in self.tagstack:
            raise NestingError(self.tagstack, tag, self.getpos())
        while tag != self.tagstack[-1]:
            self.implied_endtag(self.tagstack[-1], 1)
        assert self.tagstack[-1] == tag

    def implied_endtag(self, tag, implied):
        assert tag == self.tagstack[-1]
        assert implied in (-1, 1, 2)
        isend = (implied < 0)
        if tag in TIGHTEN_IMPLICIT_CLOSE_TAGS:
            # Pick out trailing whitespace from the program, and
            # insert the close tag before the whitespace.
            white = self.gen.unEmitWhitespace()
        else:
            white = None
        self.gen.emitEndElement(tag, isend=isend, implied=implied)
        if white:
            self.gen.emitRawText(white)
        self.tagstack.pop()
        self.pop_xmlns()

    def handle_charref(self, name):
        self.gen.emitRawText("&#%s;" % name)

    def handle_entityref(self, name):
        self.gen.emitRawText("&%s;" % name)

    def handle_data(self, data):
        self.gen.emitRawText(data)

    def handle_comment(self, data):
        self.gen.emitRawText("<!--%s-->" % data)

    def handle_decl(self, data):
        self.gen.emitRawText("<!%s>" % data)

    def handle_pi(self, data):
        self.gen.emitRawText("<?%s>" % data)

    # Internal thingies

    def scan_xmlns(self, attrs):
        nsnew = {}
        for key, value in attrs:
            if key.startswith("xmlns:"):
                nsnew[key[6:]] = value
        if nsnew:
            self.nsstack.append(self.nsdict)
            self.nsdict = self.nsdict.copy()
            self.nsdict.update(nsnew)
        else:
            self.nsstack.append(self.nsdict)

    def pop_xmlns(self):
        self.nsdict = self.nsstack.pop()

    def fixname(self, name):
        if ':' in name:
            prefix, suffix = name.split(':', 1)
            if prefix == 'xmlns':
                nsuri = self.nsdict.get(suffix)
                if nsuri in (ZOPE_TAL_NS, ZOPE_METAL_NS, ZOPE_I18N_NS):
                    return name, name, prefix
            else:
                nsuri = self.nsdict.get(prefix)
                if nsuri == ZOPE_TAL_NS:
                    return name, suffix, 'tal'
                elif nsuri == ZOPE_METAL_NS:
                    return name, suffix,  'metal'
                elif nsuri == ZOPE_I18N_NS:
                    return name, suffix, 'i18n'
        return name, name, 0

    def process_ns(self, name, attrs):
        attrlist = []
        taldict = {}
        metaldict = {}
        i18ndict = {}
        name, namebase, namens = self.fixname(name)
        for item in attrs:
            key, value = item
            key, keybase, keyns = self.fixname(key)
            ns = keyns or namens # default to tag namespace
            if ns and ns != 'unknown':
                item = (key, value, ns)
            if ns == 'tal':
                if taldict.has_key(keybase):
                    raise TALError("duplicate TAL attribute " +
                                   `keybase`, self.getpos())
                taldict[keybase] = value
            elif ns == 'metal':
                if metaldict.has_key(keybase):
                    raise METALError("duplicate METAL attribute " +
                                     `keybase`, self.getpos())
                metaldict[keybase] = value
            elif ns == 'i18n':
                if i18ndict.has_key(keybase):
                    raise I18NError("duplicate i18n attribute " +
                                    `keybase`, self.getpos())
                i18ndict[keybase] = value
            attrlist.append(item)
        if namens in ('metal', 'tal'):
            taldict['tal tag'] = namens
        return name, attrlist, taldict, metaldict, i18ndict


=== Added File Zope3/src/zope/tal/interfaces.py ===
"""Interface that a TALES engine provides to the METAL/TAL implementation."""

from zope.interface import Interface
from zope.interface.element import Attribute


class ITALESCompiler(Interface):
    """Compile-time interface provided by a TALES implementation.

    The TAL compiler needs an instance of this interface to support
    compilation of TALES expressions embedded in documents containing
    TAL and METAL constructs.
    """

    def getCompilerError():
        """Return the exception class raised for compilation errors.
        """

    def compile(expression):
        """Return a compiled form of 'expression' for later evaluation.

        'expression' is the source text of the expression.

        The return value may be passed to the various evaluate*()
        methods of the ITALESEngine interface.  No compatibility is
        required for the values of the compiled expression between
        different ITALESEngine implementations.
        """


class ITALESEngine(Interface):
    """Render-time interface provided by a TALES implementation.

    The TAL interpreter uses this interface to TALES to support
    evaluation of the compiled expressions returned by
    ITALESCompiler.compile().
    """

    def getDefault():
        """Return the value of the 'default' TALES expression.

        Checking a value for a match with 'default' should be done
        using the 'is' operator in Python.
        """

    def setPosition((lineno, offset)):
        """Inform the engine of the current position in the source file.

        This is used to allow the evaluation engine to report
        execution errors so that site developers can more easily
        locate the offending expression.
        """

    def setSourceFile(filename):
        """Inform the engine of the name of the current source file.

        This is used to allow the evaluation engine to report
        execution errors so that site developers can more easily
        locate the offending expression.
        """

    def beginScope():
        """Push a new scope onto the stack of open scopes.
        """

    def endScope():
        """Pop one scope from the stack of open scopes.
        """

    def evaluate(compiled_expression):
        """Evaluate an arbitrary expression.

        No constraints are imposed on the return value.
        """

    def evaluateBoolean(compiled_expression):
        """Evaluate an expression that must return a Boolean value.
        """

    def evaluateMacro(compiled_expression):
        """Evaluate an expression that must return a macro program.
        """

    def evaluateStructure(compiled_expression):
        """Evaluate an expression that must return a structured
        document fragment.

        The result of evaluating 'compiled_expression' must be a
        string containing a parsable HTML or XML fragment.  Any TAL
        markup cnotained in the result string will be interpreted.
        """

    def evaluateText(compiled_expression):
        """Evaluate an expression that must return text.

        The returned text should be suitable for direct inclusion in
        the output: any HTML or XML escaping or quoting is the
        responsibility of the expression itself.
        """

    def evaluateValue(compiled_expression):
        """Evaluate an arbitrary expression.

        No constraints are imposed on the return value.
        """

    def createErrorInfo(exception, (lineno, offset)):
        """Returns an ITALESErrorInfo object.

        The returned object is used to provide information about the
        error condition for the on-error handler.
        """

    def setGlobal(name, value):
        """Set a global variable.

        The variable will be named 'name' and have the value 'value'.
        """

    def setLocal(name, value):
        """Set a local variable in the current scope.

        The variable will be named 'name' and have the value 'value'.
        """

    def setRepeat(name, compiled_expression):
        """
        """

    def translate(domain, msgid, mapping):
        """
        See ITranslationService.translate()
        """


class ITALESErrorInfo(Interface):
    
    type = Attribute("type",
                     "The exception class.")

    value = Attribute("value",
                      "The exception instance.")

    lineno = Attribute("lineno",
                       "The line number the error occurred on in the source.")

    offset = Attribute("offset",
                       "The character offset at which the error occurred.")


=== Added File Zope3/src/zope/tal/ndiff.py === (547/647 lines abridged)
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################

# Module ndiff version 1.6.0
# Released to the public domain 08-Dec-2000,
# by Tim Peters (tim.one@home.com).

# Provided as-is; use at your own risk; no warranty; no promises; enjoy!

"""ndiff [-q] file1 file2
    or
ndiff (-r1 | -r2) < ndiff_output > file1_or_file2

Print a human-friendly file difference report to stdout.  Both inter-
and intra-line differences are noted.  In the second form, recreate file1
(-r1) or file2 (-r2) on stdout, from an ndiff report on stdin.

In the first form, if -q ("quiet") is not specified, the first two lines
of output are

-: file1
+: file2

Each remaining line begins with a two-letter code:

    "- "    line unique to file1
    "+ "    line unique to file2
    "  "    line common to both files
    "? "    line not present in either input file

Lines beginning with "? " attempt to guide the eye to intraline
differences, and were not present in either input file.  These lines can be
confusing if the source files contain tab characters.

The first file can be recovered by retaining only lines that begin with
"  " or "- ", and deleting those 2-character prefixes; use ndiff with -r1.

The second file can be recovered similarly, but by retaining only "  " and

[-=- -=- -=- 547 lines omitted -=- -=- -=-]

    try:
        opts, args = getopt.getopt(args, "qr:")
    except getopt.error, detail:
        return fail(str(detail))
    noisy = 1
    qseen = rseen = 0
    for opt, val in opts:
        if opt == "-q":
            qseen = 1
            noisy = 0
        elif opt == "-r":
            rseen = 1
            whichfile = val
    if qseen and rseen:
        return fail("can't specify both -q and -r")
    if rseen:
        if args:
            return fail("no args allowed with -r option")
        if whichfile in "12":
            restore(whichfile)
            return 1
        return fail("-r value must be 1 or 2")
    if len(args) != 2:
        return fail("need 2 filename args")
    f1name, f2name = args
    if noisy:
        print '-:', f1name
        print '+:', f2name
    return fcompare(f1name, f2name)

def restore(which):
    import sys
    tag = {"1": "- ", "2": "+ "}[which]
    prefixes = ("  ", tag)
    for line in sys.stdin.readlines():
        if line[:2] in prefixes:
            print line[2:],

if __name__ == '__main__':
    import sys
    args = sys.argv[1:]
    if "-profile" in args:
        import profile, pstats
        args.remove("-profile")
        statf = "ndiff.pro"
        profile.run("main(args)", statf)
        stats = pstats.Stats(statf)
        stats.strip_dirs().sort_stats('time').print_stats()
    else:
        main(args)


=== Added File Zope3/src/zope/tal/readme.txt ===
TAL - Template Attribute Language
---------------------------------

This is an implementation of TAL, the Zope Template Attribute
Language.  For TAL, see the Zope Presentation Templates ZWiki:

    http://dev.zope.org/Wikis/DevSite/Projects/ZPT/FrontPage

It is not a Zope product nor is it designed exclusively to run inside
of Zope, but if you have a Zope checkout that includes
Products/ParsedXML, its Expat parser will be used.

Prerequisites
-------------

You need:

- A recent checkout of Zope2; don't forget to run the wo_pcgi.py
  script to compile everything.  (See above -- this is now optional.)

- A recent checkout of the Zope2 product ParsedXML, accessible
  throught <Zope2>/lib/python/Products/ParsedXML; don't forget to run
  the setup.py script to compiles Expat.  (Again, optional.)

- Python 1.5.2; the driver script refuses to work with other versions
  unless you specify the -n option; this is done so that I don't
  accidentally use Python 2.x features.

- Create a .path file containing proper module search path; it should
  point the <Zope2>/lib/python directory that you want to use.

How To Play
-----------

(Don't forget to edit .path, see above!)

The script driver.py takes an XML file with TAL markup as argument and
writes the expanded version to standard output.  The filename argument
defaults to tests/input/test01.xml.

Regression test
---------------

There are unit test suites in the 'tests' subdirectory; these can be
run with tests/run.py.  This should print the testcase names plus
progress info, followed by a final line saying "OK".  It requires that
../unittest.py exists.

There are a number of test files in the 'tests' subdirectory, named
tests/input/test<number>.xml and tests/input/test<number>.html.  The
Python script ./runtest.py calls driver.main() for each test file, and
should print "<file> OK" for each one.  These tests are also run as
part of the unit test suites, so tests/run.py is all you need.

What's Here
-----------

DummyEngine.py		simple-minded TALES execution engine
TALInterpreter.py	class to interpret intermediate code
TALGenerator.py		class to generate intermediate code
XMLParser.py		base class to parse XML, avoiding DOM
TALParser.py		class to parse XML with TAL into intermediate code
HTMLTALParser.py	class to parse HTML with TAL into intermediate code
HTMLParser.py		HTML-parsing base class
driver.py		script to demonstrate TAL expansion
timer.py		script to time various processing phases
setpath.py		hack to set sys.path and import ZODB
__init__.py		empty file that makes this directory a package
runtest.py		Python script to run file-comparison tests
ndiff.py		helper for runtest.py to produce diffs
tests/			drectory with test files and output
tests/run.py		Python script to run all tests

Author and License
------------------

This code is written by Guido van Rossum (project lead), Fred Drake,
and Tim Peters.  It is owned by Digital Creations and can be
redistributed under the Zope Public License.

TO DO
-----

(See also http://www.zope.org/Members/jim/ZPTIssueTracker .)

- Need to remove leading whitespace and newline when omitting an
  element (either through tal:replace with a value of nothing or
  tal:condition with a false condition).

- Empty TAL/METAL attributes are ignored: tal:replace="" is ignored
  rather than causing an error.

- HTMLTALParser.py and TALParser.py are silly names.  Should be
  HTMLTALCompiler.py and XMLTALCompiler.py (or maybe shortened,
  without "TAL"?)

- Should we preserve case of tags and attribute names in HTML?


=== Added File Zope3/src/zope/tal/runtest.py ===
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Driver program to run METAL and TAL regression tests.
"""

import sys
import os
from cStringIO import StringIO
import glob
import traceback

if __name__ == "__main__":
    import setpath                      # Local hack to tweak sys.path etc.

import zope.tal.driver
import zope.tal.tests.utils

def showdiff(a, b):
    import ndiff
    cruncher = ndiff.SequenceMatcher(ndiff.IS_LINE_JUNK, a, b)
    for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
        if tag == "equal":
            continue
        print nicerange(alo, ahi) + tag[0] + nicerange(blo, bhi)
        ndiff.dump('<', a, alo, ahi)
        if a and b:
            print '---'
        ndiff.dump('>', b, blo, bhi)

def nicerange(lo, hi):
    if hi <= lo+1:
        return str(lo+1)
    else:
        return "%d,%d" % (lo+1, hi)

def main():
    opts = []
    args = sys.argv[1:]
    quiet = 0
    unittesting = 0
    if args and args[0] == "-q":
        quiet = 1
        del args[0]
    if args and args[0] == "-Q":
        unittesting = 1
        del args[0]
    while args and args[0].startswith('-'):
        opts.append(args[0])
        del args[0]
    if not args:
        prefix = os.path.join("tests", "input", "test*.")
        if tests.utils.skipxml:
            xmlargs = []
        else:
            xmlargs = glob.glob(prefix + "xml")
            xmlargs.sort()
        htmlargs = glob.glob(prefix + "html")
        htmlargs.sort()
        args = xmlargs + htmlargs
        if not args:
             sys.stderr.write("No tests found -- please supply filenames\n")
             sys.exit(1)
    errors = 0
    for arg in args:
        locopts = []
        if arg.find("metal") >= 0 and "-m" not in opts:
            locopts.append("-m")
        if not unittesting:
            print arg,
            sys.stdout.flush()
        if tests.utils.skipxml and arg.endswith(".xml"):
            print "SKIPPED (XML parser not available)"
            continue
        save = sys.stdout, sys.argv
        try:
            try:
                sys.stdout = stdout = StringIO()
                sys.argv = [""] + opts + locopts + [arg]
                driver.main()
            finally:
                sys.stdout, sys.argv = save
        except SystemExit:
            raise
        except:
            errors = 1
            if quiet:
                print sys.exc_type
                sys.stdout.flush()
            else:
                if unittesting:
                    print
                else:
                    print "Failed:"
                    sys.stdout.flush()
                traceback.print_exc()
            continue
        head, tail = os.path.split(arg)
        outfile = os.path.join(
            head.replace("input", "output"),
            tail)
        try:
            f = open(outfile)
        except IOError:
            expected = None
            print "(missing file %s)" % outfile,
        else:
            expected = f.readlines()
            f.close()
        stdout.seek(0)
        if hasattr(stdout, "readlines"):
            actual = stdout.readlines()
        else:
            actual = readlines(stdout)
        if actual == expected:
            if not unittesting:
                print "OK"
        else:
            if unittesting:
                print
            else:
                print "not OK"
            errors = 1
            if not quiet and expected is not None:
                showdiff(expected, actual)
    if errors:
        sys.exit(1)

def readlines(f):
    L = []
    while 1:
        line = f.readline()
        if not line:
            break
        L.append(line)
    return L

if __name__ == "__main__":
    main()


=== Added File Zope3/src/zope/tal/setpath.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Read a module search path from .path file.

If .path file isn't found in the directory of the setpath.py module, then try
to import ZODB.  If that succeeds, we assume the path is already set up
correctly.  If that import fails, an IOError is raised.
"""
import os
import sys

dir = os.path.dirname(__file__)
path = os.path.join(dir, ".path")
try:
    f = open(path)
except IOError:
    try:
        # If we can import ZODB, our sys.path is set up well enough already
        import ZODB
    except ImportError:
        raise IOError("Can't find ZODB package.  Please edit %s to point to "
                      "your Zope's lib/python directory" % path)
else:
    for line in f.readlines():
        line = line.strip()
        if line and line[0] != '#':
            for dir in line.split(os.pathsep):
                dir = os.path.expanduser(os.path.expandvars(dir))
                if dir not in sys.path:
                    sys.path.append(dir)
        # Must import this first to initialize Persistence properly
        import ZODB


=== Added File Zope3/src/zope/tal/taldefs.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Common definitions used by TAL and METAL compilation an transformation.
"""

from types import ListType, TupleType

from zope.tal.interfaces import ITALESErrorInfo

TAL_VERSION = "1.4"

XML_NS = "http://www.w3.org/XML/1998/namespace" # URI for XML namespace
XMLNS_NS = "http://www.w3.org/2000/xmlns/" # URI for XML NS declarations

ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal"
ZOPE_METAL_NS = "http://xml.zope.org/namespaces/metal"
ZOPE_I18N_NS = "http://xml.zope.org/namespaces/i18n"

NAME_RE = "[a-zA-Z_][a-zA-Z0-9_]*"

KNOWN_METAL_ATTRIBUTES = [
    "define-macro",
    "use-macro",
    "define-slot",
    "fill-slot",
    ]

KNOWN_TAL_ATTRIBUTES = [
    "define",
    "condition",
    "content",
    "replace",
    "repeat",
    "attributes",
    "on-error",
    "omit-tag",
    "tal tag",
    ]

KNOWN_I18N_ATTRIBUTES = [
    "translate",
    "domain",
    "target",
    "source",
    "attributes",
    "data",
    "name",
    ]

class TALError(Exception):

    def __init__(self, msg, position=(None, None)):
        assert msg != ""
        self.msg = msg
        self.lineno = position[0]
        self.offset = position[1]
        self.filename = None

    def setFile(self, filename):
        self.filename = filename

    def __str__(self):
        result = self.msg
        if self.lineno is not None:
            result = result + ", at line %d" % self.lineno
        if self.offset is not None:
            result = result + ", column %d" % (self.offset + 1)
        if self.filename is not None:
            result = result + ', in file %s' % self.filename
        return result

class METALError(TALError):
    pass

class TALESError(TALError):
    pass

class I18NError(TALError):
    pass


class ErrorInfo:

    __implements__ = ITALESErrorInfo

    def __init__(self, err, position=(None, None)):
        if isinstance(err, Exception):
            self.type = err.__class__
            self.value = err
        else:
            self.type = err
            self.value = None
        self.lineno = position[0]
        self.offset = position[1]



import re
_attr_re = re.compile(r"\s*([^\s]+)\s+([^\s].*)\Z", re.S)
_subst_re = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S)
del re

def parseAttributeReplacements(arg):
    dict = {}
    for part in splitParts(arg):
        m = _attr_re.match(part)
        if not m:
            raise TALError("Bad syntax in attributes:" + `part`)
        name, expr = m.group(1, 2)
        name = name.lower()
        if dict.has_key(name):
            raise TALError("Duplicate attribute name in attributes:" + `part`)
        dict[name] = expr
    return dict

def parseSubstitution(arg, position=(None, None)):
    m = _subst_re.match(arg)
    if not m:
        raise TALError("Bad syntax in substitution text: " + `arg`, position)
    key, expr = m.group(1, 2)
    if not key:
        key = "text"
    return key, expr

def splitParts(arg):
    # Break in pieces at undoubled semicolons and
    # change double semicolons to singles:
    arg = arg.replace(";;", "\0")
    parts = arg.split(';')
    parts = [p.replace("\0", ";") for p in parts]
    if len(parts) > 1 and not parts[-1].strip():
        del parts[-1] # It ended in a semicolon
    return parts

def isCurrentVersion(program):
    version = getProgramVersion(program)
    return version == TAL_VERSION

def getProgramMode(program):
    version = getProgramVersion(program)
    if (version == TAL_VERSION and isinstance(program[1], TupleType) and
        len(program[1]) == 2):
        opcode, mode = program[1]
        if opcode == "mode":
            return mode
    return None

def getProgramVersion(program):
    if (len(program) >= 2 and
        isinstance(program[0], TupleType) and len(program[0]) == 2):
        opcode, version = program[0]
        if opcode == "version":
            return version
    return None

import re
_ent1_re = re.compile('&(?![A-Z#])', re.I)
_entch_re = re.compile('&([A-Z][A-Z0-9]*)(?![A-Z0-9;])', re.I)
_entn1_re = re.compile('&#(?![0-9X])', re.I)
_entnx_re = re.compile('&(#X[A-F0-9]*)(?![A-F0-9;])', re.I)
_entnd_re = re.compile('&(#[0-9][0-9]*)(?![0-9;])')
del re
 
def attrEscape(s):
    """Replace special characters '&<>' by character entities,
    except when '&' already begins a syntactically valid entity."""
    s = _ent1_re.sub('&amp;', s)
    s = _entch_re.sub(r'&amp;\1', s)
    s = _entn1_re.sub('&amp;#', s)
    s = _entnx_re.sub(r'&amp;\1', s)
    s = _entnd_re.sub(r'&amp;\1', s)
    s = s.replace('<', '&lt;')
    s = s.replace('>', '&gt;')
    s = s.replace('"', '&quot;')
    return s

import cgi
def quote(s, escape=cgi.escape):
    return '"%s"' % escape(s, 1)
del cgi


=== Added File Zope3/src/zope/tal/talgenerator.py === (685/785 lines abridged)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Code generator for TALInterpreter intermediate code.
"""

import re
import cgi

import zope.tal.taldefs

from zope.tal.taldefs import NAME_RE, TAL_VERSION
from zope.tal.taldefs import I18NError, METALError, TALError
from zope.tal.taldefs import parseSubstitution
from zope.tal.translationcontext import TranslationContext, DEFAULT_DOMAIN

I18N_REPLACE = 1
I18N_CONTENT = 2
I18N_EXPRESSION = 3

class TALGenerator:

    inMacroUse = 0
    inMacroDef = 0
    source_file = None

    def __init__(self, expressionCompiler=None, xml=1, source_file=None):
        if not expressionCompiler:
            from zope.tal.dummyengine import DummyEngine
            expressionCompiler = DummyEngine()
        self.expressionCompiler = expressionCompiler
        self.CompilerError = expressionCompiler.getCompilerError()
        # This holds the emitted opcodes representing the input
        self.program = []
        # The program stack for when we need to do some sub-evaluation for an
        # intermediate result.  E.g. in an i18n:name tag for which the
        # contents describe the ${name} value.
        self.stack = []
        # Another stack of postponed actions.  Elements on this stack are a

[-=- -=- -=- 685 lines omitted -=- -=- -=-]

        # If i18n:name appeared in the same tag as tal:replace then we're
        # going to do the substitution a little bit differently.  The results
        # of the expression go into the i18n substitution dictionary.
        if replace:
            self.emitSubstitution(replace, repldict)
        elif varname:
            if varname[1] is not None:
                i18nNameAction = I18N_EXPRESSION
            # o varname[0] is the variable name
            # o i18nNameAction is either
            #   - I18N_REPLACE for implicit tal:replace
            #   - I18N_CONTENT for tal:content
            #   - I18N_EXPRESSION for explicit tal:replace
            # o varname[1] will be None for the first two actions and the
            #   replacement tal expression for the third action.
            self.emitI18nVariable(varname[0], i18nNameAction, varname[1])
        # Do not test for "msgid is not None", i.e. we only want to test for
        # explicit msgids here.  See comment above.
        if msgid is not None and varname:
            self.emitTranslation(msgid, i18ndata)
        if repeat:
            self.emitRepeat(repeat)
        if condition:
            self.emitCondition(condition)
        if onError:
            self.emitOnError(name, onError)
        if scope:
            self.emit("endScope")
        if i18ncontext:
            self.emit("endI18nContext")
            assert self.i18nContext.parent is not None
            self.i18nContext = self.i18nContext.parent
        if defineSlot:
            self.emitDefineSlot(defineSlot)
        if fillSlot:
            self.emitFillSlot(fillSlot)
        if useMacro:
            self.emitUseMacro(useMacro)
        if defineMacro:
            self.emitDefineMacro(defineMacro)

def test():
    t = TALGenerator()
    t.pushProgram()
    t.emit("bar")
    p = t.popProgram()
    t.emit("foo", p)

if __name__ == "__main__":
    test()


=== Added File Zope3/src/zope/tal/talgettext.py ===
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################

"""Program to extract internationalization markup from Page Templates.

Once you have marked up a Page Template file with i18n: namespace tags, use
this program to extract GNU gettext .po file entries.

Usage: talgettext.py [options] files
Options:
    -h / --help
        Print this message and exit.
"""

import sys
import os
import getopt

from zope.tal.htmltalparser import HTMLTALParser
from zope.tal.talinterpreter import TALInterpreter
from zope.tal.dummyengine import DummyEngine
from zope.tal.interfaces import ITALESEngine


def usage(code, msg=''):
    # Python 2.1 required
    print >> sys.stderr, __doc__
    if msg:
        print >> sys.stderr, msg
    sys.exit(code)


class POEngine(DummyEngine):
    __implements__ = ITALESEngine

    catalog = {}

    def evaluatePathOrVar(self, expr):
        return 'who cares'

    def translate(self, domain, msgid, mapping):
        self.catalog[msgid] = ''
        

def main():
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            'ho:',
            ['help', 'output='])
    except getopt.error, msg:
        usage(1, msg)

    outfile = None
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(0)
        elif opt in ('-o', '--output'):
            outfile = arg

    if not args:
        print 'nothing to do'
        return

    # We don't care about the rendered output of the .pt file
    class Devnull:
        def write(self, s):
            pass

    engine = POEngine()
    for file in args:
        p = HTMLTALParser()
        p.parseFile(file)
        program, macros = p.getCode()
        TALInterpreter(program, macros, engine, stream=Devnull())()

    # Now print all the entries in the engine
    msgids = engine.catalog.keys()
    msgids.sort()
    for msgid in msgids:
        msgstr = engine.catalog[msgid]
        print 'msgid "%s"' % msgid
        print 'msgstr "%s"' % msgstr
        print


if __name__ == '__main__':
    main()


=== Added File Zope3/src/zope/tal/talinterpreter.py === (631/731 lines abridged)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Interpreter for a pre-compiled TAL program.
"""

import sys
import getopt
import re
from types import ListType
from cgi import escape
# Do not use cStringIO here!  It's not unicode aware. :(
from StringIO import StringIO

from zope.tal.taldefs import quote, TAL_VERSION, TALError, METALError
from zope.tal.taldefs import isCurrentVersion, getProgramVersion, getProgramMode
from zope.tal.talgenerator import TALGenerator
from zope.tal.translationcontext import TranslationContext

BOOLEAN_HTML_ATTRS = [
    # List of Boolean attributes in HTML that should be rendered in
    # minimized form (e.g. <img ismap> rather than <img ismap="">)
    # From http://www.w3.org/TR/xhtml1/#guidelines (C.10)
    # XXX The problem with this is that this is not valid XML and
    # can't be parsed back!
    "compact", "nowrap", "ismap", "declare", "noshade", "checked",
    "disabled", "readonly", "multiple", "selected", "noresize",
    "defer"
]

def normalize(text):
    # Now we need to normalize the whitespace in implicit message ids and
    # implicit $name substitution values by stripping leading and trailing
    # whitespace, and folding all internal whitespace to a single space.
    return ' '.join(text.split())


class AltTALGenerator(TALGenerator):


[-=- -=- -=- 631 lines omitted -=- -=- -=-]

            slot = slots.get(slotName)
            if slot is not None:
                prev_source = self.sourceFile
                self.interpret(slot)
                if self.sourceFile != prev_source:
                    self.engine.setSourceFile(prev_source)
                    self.sourceFile = prev_source
                self.pushMacro(macroName, slots, entering=0)
                return
            self.pushMacro(macroName, slots)
            # Falling out of the 'if' allows the macro to be interpreted.
        self.interpret(block)
    bytecode_handlers["defineSlot"] = do_defineSlot

    def do_onError(self, (block, handler)):
        self.interpret(block)

    def do_onError_tal(self, (block, handler)):
        state = self.saveState()
        self.stream = stream = StringIO()
        self._stream_write = stream.write
        try:
            self.interpret(block)
        except:
            exc = sys.exc_info()[1]
            self.restoreState(state)
            engine = self.engine
            engine.beginScope()
            error = engine.createErrorInfo(exc, self.position)
            engine.setLocal('error', error)
            try:
                self.interpret(handler)
            finally:
                engine.endScope()
        else:
            self.restoreOutputState(state)
            self.stream_write(stream.getvalue())
    bytecode_handlers["onError"] = do_onError

    bytecode_handlers_tal = bytecode_handlers.copy()
    bytecode_handlers_tal["rawtextBeginScope"] = do_rawtextBeginScope_tal
    bytecode_handlers_tal["beginScope"] = do_beginScope_tal
    bytecode_handlers_tal["setLocal"] = do_setLocal_tal
    bytecode_handlers_tal["setGlobal"] = do_setGlobal_tal
    bytecode_handlers_tal["insertStructure"] = do_insertStructure_tal
    bytecode_handlers_tal["insertText"] = do_insertText_tal
    bytecode_handlers_tal["loop"] = do_loop_tal
    bytecode_handlers_tal["onError"] = do_onError_tal
    bytecode_handlers_tal["<attrAction>"] = attrAction_tal
    bytecode_handlers_tal["optTag"] = do_optTag_tal


=== Added File Zope3/src/zope/tal/talparser.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Parse XML and compile to TALInterpreter intermediate code.
"""

from zope.tal.xmlparser import XMLParser
from zope.tal.taldefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS
from zope.tal.talgenerator import TALGenerator

class TALParser(XMLParser):

    ordered_attributes = 1

    def __init__(self, gen=None): # Override
        XMLParser.__init__(self)
        if gen is None:
            gen = TALGenerator()
        self.gen = gen
        self.nsStack = []
        self.nsDict = {XML_NS: 'xml'}
        self.nsNew = []

    def getCode(self):
        return self.gen.getCode()

    def getWarnings(self):
        return ()

    def StartNamespaceDeclHandler(self, prefix, uri):
        self.nsStack.append(self.nsDict.copy())
        self.nsDict[uri] = prefix
        self.nsNew.append((prefix, uri))

    def EndNamespaceDeclHandler(self, prefix):
        self.nsDict = self.nsStack.pop()

    def StartElementHandler(self, name, attrs):
        if self.ordered_attributes:
            # attrs is a list of alternating names and values
            attrlist = []
            for i in range(0, len(attrs), 2):
                key = attrs[i]
                value = attrs[i+1]
                attrlist.append((key, value))
        else:
            # attrs is a dict of {name: value}
            attrlist = attrs.items()
            attrlist.sort() # For definiteness
        name, attrlist, taldict, metaldict, i18ndict \
              = self.process_ns(name, attrlist)
        attrlist = self.xmlnsattrs() + attrlist
        self.gen.emitStartElement(name, attrlist, taldict, metaldict, i18ndict)

    def process_ns(self, name, attrlist):
        taldict = {}
        metaldict = {}
        i18ndict = {}
        fixedattrlist = []
        name, namebase, namens = self.fixname(name)
        for key, value in attrlist:
            key, keybase, keyns = self.fixname(key)
            ns = keyns or namens # default to tag namespace
            item = key.lower(), value
            if ns == 'metal':
                metaldict[keybase] = value
                item = item + ("metal",)
            elif ns == 'tal':
                taldict[keybase] = value
                item = item + ("tal",)
            elif ns == 'i18n':
                assert 0, "dealing with i18n: " + `(keybase, value)`
                i18ndict[keybase] = value
                item = item + ('i18n',)
            fixedattrlist.append(item)
        if namens in ('metal', 'tal', 'i18n'):
            taldict['tal tag'] = namens
        return name, fixedattrlist, taldict, metaldict, i18ndict

    def xmlnsattrs(self):
        newlist = []
        for prefix, uri in self.nsNew:
            if prefix:
                key = "xmlns:" + prefix
            else:
                key = "xmlns"
            if uri in (ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS):
                item = (key, uri, "xmlns")
            else:
                item = (key, uri)
            newlist.append(item)
        self.nsNew = []
        return newlist

    def fixname(self, name):
        if ' ' in name:
            uri, name = name.split(' ')
            prefix = self.nsDict[uri]
            prefixed = name
            if prefix:
                prefixed = "%s:%s" % (prefix, name)
            ns = 'x'
            if uri == ZOPE_TAL_NS:
                ns = 'tal'
            elif uri == ZOPE_METAL_NS:
                ns = 'metal'
            elif uri == ZOPE_I18N_NS:
                ns = 'i18n'
            return (prefixed, name, ns)
        return (name, name, None)

    def EndElementHandler(self, name):
        name = self.fixname(name)[0]
        self.gen.emitEndElement(name)

    def DefaultHandler(self, text):
        self.gen.emitRawText(text)

def test():
    import sys
    p = TALParser()
    file = "tests/input/test01.xml"
    if sys.argv[1:]:
        file = sys.argv[1]
    p.parseFile(file)
    program, macros = p.getCode()
    from zope.tal.talinterpreter import TALInterpreter
    from zope.tal.dummyengine import DummyEngine
    engine = DummyEngine(macros)
    TALInterpreter(program, macros, engine, sys.stdout, wrap=0)()

if __name__ == "__main__":
    test()


=== Added File Zope3/src/zope/tal/timer.py ===
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
Helper program to time compilation and interpretation
"""

import sys
import time
import getopt
from cPickle import dumps, loads
from cStringIO import StringIO

from zope.tal.driver import FILE, compilefile, interpretit

def main():
    count = 10
    try:
        opts, args = getopt.getopt(sys.argv[1:], "n:")
    except getopt.error, msg:
        print msg
        sys.exit(2)
    for o, a in opts:
        if o == "-n":
            count = int(a)
    if not args:
        args = [FILE]
    for file in args:
        print file
        dummyfile = StringIO()
        it = timefunc(count, compilefile, file)
        timefunc(count, interpretit, it, None, dummyfile)

def timefunc(count, func, *args):
    sys.stderr.write("%-14s: " % func.__name__)
    sys.stderr.flush()
    t0 = time.clock()
    for i in range(count):
        result = apply(func, args)
    t1 = time.clock()
    sys.stderr.write("%6.3f secs for %d calls, i.e. %4.0f msecs per call\n"
                     % ((t1-t0), count, 1000*(t1-t0)/count))
    return result

if __name__ == "__main__":
    main()


=== Added File Zope3/src/zope/tal/translationcontext.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""Translation context object for the TALInterpreter's I18N support.

The translation context provides a container for the information
needed to perform translation of a marked string from a page template.

$Id: translationcontext.py,v 1.1.2.1 2002/12/23 19:33:27 jim Exp $
"""

DEFAULT_DOMAIN = "default"

class TranslationContext:
    """Information about the I18N settings of a TAL processor."""

    def __init__(self, parent=None, domain=None, target=None, source=None):
        if parent:
            if not domain:
                domain = parent.domain
            if not target:
                target = parent.target
            if not source:
                source = parent.source
        elif domain is None:
            domain = DEFAULT_DOMAIN

        self.parent = parent
        self.domain = domain
        self.target = target
        self.source = source


=== Added File Zope3/src/zope/tal/xmlparser.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Generic expat-based XML parser base class.
"""

import logging

class XMLParser:

    ordered_attributes = 0

    handler_names = [
        "StartElementHandler",
        "EndElementHandler",
        "ProcessingInstructionHandler",
        "CharacterDataHandler",
        "UnparsedEntityDeclHandler",
        "NotationDeclHandler",
        "StartNamespaceDeclHandler",
        "EndNamespaceDeclHandler",
        "CommentHandler",
        "StartCdataSectionHandler",
        "EndCdataSectionHandler",
        "DefaultHandler",
        "DefaultHandlerExpand",
        "NotStandaloneHandler",
        "ExternalEntityRefHandler",
        "XmlDeclHandler",
        "StartDoctypeDeclHandler",
        "EndDoctypeDeclHandler",
        "ElementDeclHandler",
        "AttlistDeclHandler"
        ]

    def __init__(self, encoding=None):
        self.parser = p = self.createParser()
        if self.ordered_attributes:
            try:
                self.parser.ordered_attributes = self.ordered_attributes
            except AttributeError:
                logging.warn("TAL.XMLParser: Can't set ordered_attributes")
                self.ordered_attributes = 0
        for name in self.handler_names:
            method = getattr(self, name, None)
            if method is not None:
                try:
                    setattr(p, name, method)
                except AttributeError:
                    logging.error("TAL.XMLParser: Can't set "
                                  "expat handler %s" % name)

    def createParser(self, encoding=None):
        global XMLParseError
        from xml.parsers import expat
        XMLParseError = expat.ExpatError
        return expat.ParserCreate(encoding, ' ')

    def parseFile(self, filename):
        self.parseStream(open(filename))

    def parseString(self, s):
        self.parser.Parse(s, 1)

    def parseURL(self, url):
        import urllib
        self.parseStream(urllib.urlopen(url))

    def parseStream(self, stream):
        self.parser.ParseFile(stream)

    def parseFragment(self, s, end=0):
        self.parser.Parse(s, end)