[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('&', 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
=== 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)