[Zope3-checkins] CVS: Zope3/src/zope/tal - .cvsignore:1.2 __init__.py:1.2 changes.txt:1.2 driver.py:1.2 dummyengine.py:1.2 history.txt:1.2 htmltalparser.py:1.2 interfaces.py:1.2 ndiff.py:1.2 readme.txt:1.2 runtest.py:1.2 setpath.py:1.2 taldefs.py:1.2 talgenerator.py:1.2 talgettext.py:1.2 talinterpreter.py:1.2 talparser.py:1.2 timer.py:1.2 translationcontext.py:1.2 xmlparser.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:16:00 -0500
Update of /cvs-repository/Zope3/src/zope/tal
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/tal
Added Files:
.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:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/zope/tal/.cvsignore 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/.cvsignore Wed Dec 25 09:15:29 2002
@@ -0,0 +1 @@
+.path
=== Zope3/src/zope/tal/__init__.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/__init__.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
=== Zope3/src/zope/tal/changes.txt 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/changes.txt Wed Dec 25 09:15:29 2002
@@ -0,0 +1,11 @@
+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.
=== Zope3/src/zope/tal/driver.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/driver.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,197 @@
+#!/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 zope.tal.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()
=== Zope3/src/zope/tal/dummyengine.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/dummyengine.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,241 @@
+##############################################################################
+#
+# 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()
=== Zope3/src/zope/tal/history.txt 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/history.txt Wed Dec 25 09:15:29 2002
@@ -0,0 +1,72 @@
+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
=== Zope3/src/zope/tal/htmltalparser.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/htmltalparser.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,309 @@
+##############################################################################
+#
+# 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 HTMLParser import HTMLParser, HTMLParseError
+
+from zope.tal.taldefs import ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS, \
+ METALError, TALError, I18NError
+from zope.tal.talgenerator import TALGenerator
+
+
+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
=== Zope3/src/zope/tal/interfaces.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/interfaces.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,147 @@
+"""Interface that a TALES engine provides to the METAL/TAL implementation."""
+
+from zope.interface import Attribute, Interface
+
+
+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.")
=== Zope3/src/zope/tal/ndiff.py 1.1 => 1.2 === (550/650 lines abridged)
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/ndiff.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,647 @@
+#! /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
[-=- -=- -=- 550 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)
=== Zope3/src/zope/tal/readme.txt 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/readme.txt Wed Dec 25 09:15:29 2002
@@ -0,0 +1,97 @@
+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?
=== Zope3/src/zope/tal/runtest.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/runtest.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,152 @@
+#! /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 glob
+import os
+import sys
+import traceback
+
+from cStringIO import StringIO
+
+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 zope.tal.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 zope.tal.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]
+ zope.tal.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()
=== Zope3/src/zope/tal/setpath.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/setpath.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# 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
=== Zope3/src/zope/tal/taldefs.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/taldefs.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,190 @@
+##############################################################################
+#
+# 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 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], tuple) 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], tuple) 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('&', s)
+ s = _entch_re.sub(r'&\1', s)
+ s = _entn1_re.sub('&#', s)
+ s = _entnx_re.sub(r'&\1', s)
+ s = _entnd_re.sub(r'&\1', s)
+ s = s.replace('<', '<')
+ s = s.replace('>', '>')
+ s = s.replace('"', '"')
+ return s
+
+import cgi
+def quote(s, escape=cgi.escape):
+ return '"%s"' % escape(s, 1)
+del cgi
=== Zope3/src/zope/tal/talgenerator.py 1.1 => 1.2 === (688/788 lines abridged)
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/talgenerator.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,785 @@
+##############################################################################
+#
+# 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 cgi
+import re
+
+from zope.tal import 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
[-=- -=- -=- 688 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()
=== Zope3/src/zope/tal/talgettext.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/talgettext.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,100 @@
+#!/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 getopt
+import os
+import sys
+
+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()
=== Zope3/src/zope/tal/talinterpreter.py 1.1 => 1.2 === (635/735 lines abridged)
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/talinterpreter.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,732 @@
+##############################################################################
+#
+# 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 getopt
+import re
+import sys
+
+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
+from zope.tal.taldefs import 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())
[-=- -=- -=- 635 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
=== Zope3/src/zope/tal/talparser.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/talparser.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,145 @@
+##############################################################################
+#
+# 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.taldefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS
+from zope.tal.talgenerator import TALGenerator
+from zope.tal.xmlparser import XMLParser
+
+
+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()
=== Zope3/src/zope/tal/timer.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/timer.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,59 @@
+#! /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 getopt
+import sys
+import time
+
+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()
=== Zope3/src/zope/tal/translationcontext.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/translationcontext.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,41 @@
+##############################################################################
+#
+# 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$
+"""
+
+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
=== Zope3/src/zope/tal/xmlparser.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:16:00 2002
+++ Zope3/src/zope/tal/xmlparser.py Wed Dec 25 09:15:29 2002
@@ -0,0 +1,85 @@
+##############################################################################
+#
+# 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)