[ZPT] CVS: Zope/lib/python/TAL - DummyEngine.py:1.32.4.2 TALGenerator.py:1.55.4.2 TALInterpreter.py:1.69.4.3
Chris McDonough
chrism@zope.com
Tue, 8 Oct 2002 14:41:44 -0400
Update of /cvs-repository/Zope/lib/python/TAL
In directory cvs.zope.org:/tmp/cvs-serv20268/lib/python/TAL
Modified Files:
Tag: chrism-install-branch
DummyEngine.py TALGenerator.py TALInterpreter.py
Log Message:
Merging HEAD into chrism-install-branch.
=== Zope/lib/python/TAL/DummyEngine.py 1.32.4.1 => 1.32.4.2 ===
--- Zope/lib/python/TAL/DummyEngine.py:1.32.4.1 Sat Sep 28 21:40:35 2002
+++ Zope/lib/python/TAL/DummyEngine.py Tue Oct 8 14:41:13 2002
@@ -8,7 +8,7 @@
# 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.
+# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
@@ -18,20 +18,13 @@
import re
import sys
+import driver
+
from TALDefs import NAME_RE, TALESError, ErrorInfo
-from ITALES import ITALESCompiler, ITALESEngine
-from DocumentTemplate.DT_Util import ustr
-try:
- from Zope.I18n.ITranslationService import ITranslationService
- from Zope.I18n.IDomain import IDomain
-except ImportError:
- # Before 2.7
- class ITranslationService: pass
- class IDomain: pass
-class _Default:
+class Default:
pass
-Default = _Default()
+Default = Default()
name_match = re.compile(r"(?s)(%s):(.*)\Z" % NAME_RE).match
@@ -43,8 +36,6 @@
position = None
source_file = None
- __implements__ = ITALESCompiler, ITALESEngine
-
def __init__(self, macros=None):
if macros is None:
macros = {}
@@ -52,7 +43,6 @@
dict = {'nothing': None, 'default': Default}
self.locals = self.globals = dict
self.stack = [dict]
- self.translationService = DummyTranslationService()
def getCompilerError(self):
return CompilerError
@@ -100,7 +90,13 @@
if type in ("string", "str"):
return expr
if type in ("path", "var", "global", "local"):
- return self.evaluatePathOrVar(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`)
if type == "not":
return not self.evaluate(expr)
if type == "exists":
@@ -120,15 +116,6 @@
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)
@@ -138,7 +125,7 @@
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is not None and text is not Default:
- text = ustr(text)
+ text = str(text)
return text
def evaluateStructure(self, expr):
@@ -159,7 +146,6 @@
macro = self.macros[localName]
else:
# External macro
- import driver
program, macros = driver.compilefile(file)
macro = macros.get(localName)
if not macro:
@@ -171,7 +157,6 @@
file, localName = self.findMacroFile(macroName)
if not file:
return file, localName
- import driver
doc = driver.parsefile(file)
return doc, localName
@@ -198,10 +183,6 @@
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):
@@ -219,31 +200,3 @@
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, mapping=mapping):
- 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()
=== Zope/lib/python/TAL/TALGenerator.py 1.55.4.1 => 1.55.4.2 ===
--- Zope/lib/python/TAL/TALGenerator.py:1.55.4.1 Sat Sep 28 21:40:35 2002
+++ Zope/lib/python/TAL/TALGenerator.py Tue Oct 8 14:41:13 2002
@@ -8,7 +8,7 @@
# 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.
+# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
@@ -18,16 +18,7 @@
import re
import cgi
-import TALDefs
-
-from TALDefs import NAME_RE, TAL_VERSION
-from TALDefs import I18NError, METALError, TALError
-from TALDefs import parseSubstitution
-from TranslationContext import TranslationContext, DEFAULT_DOMAIN
-
-I18N_REPLACE = 1
-I18N_CONTENT = 2
-I18N_EXPRESSION = 3
+from TALDefs import *
class TALGenerator:
@@ -41,15 +32,8 @@
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
- # dictionary; key/values contain useful information that
- # emitEndElement needs to finish its calculations
self.todoStack = []
self.macros = {}
self.slots = {}
@@ -60,7 +44,6 @@
if source_file is not None:
self.source_file = source_file
self.emit("setSourceFile", source_file)
- self.i18nContext = TranslationContext()
def getCode(self):
assert not self.stack
@@ -99,12 +82,6 @@
# instructions to be joined together.
output.append(self.optimizeArgsList(item))
continue
- if opcode == 'noop':
- # This is a spacer for end tags in the face of i18n:name
- # attributes. We can't let the optimizer collect immediately
- # following end tags into the same rawtextOffset.
- opcode = None
- pass
text = "".join(collect)
if text:
i = text.rfind("\n")
@@ -125,28 +102,9 @@
else:
return item[0], tuple(item[1:])
- # These codes are used to indicate what sort of special actions
- # are needed for each special attribute. (Simple attributes don't
- # get action codes.)
- #
- # The special actions (which are modal) are handled by
- # TALInterpreter.attrAction() and .attrAction_tal().
- #
- # Each attribute is represented by a tuple:
- #
- # (name, value) -- a simple name/value pair, with
- # no special processing
- #
- # (name, value, action, *extra) -- attribute with special
- # processing needs, action is a
- # code that indicates which
- # branch to take, and *extra
- # contains additional,
- # action-specific information
- # needed by the processing
- #
+ actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4,
+ 0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
def optimizeStartTag(self, collect, name, attrlist, end):
- # return true if the tag can be converted to plain text
if not attrlist:
collect.append("<%s%s" % (name, end))
return 1
@@ -157,15 +115,18 @@
if len(item) > 2:
opt = 0
name, value, action = item[:3]
+ action = self.actionIndex[action]
attrlist[i] = (name, value, action) + item[3:]
else:
if item[1] is None:
s = item[0]
else:
- s = '%s="%s"' % (item[0], cgi.escape(item[1], 1))
+ s = "%s=%s" % (item[0], quote(item[1]))
attrlist[i] = item[0], s
- new.append(" " + s)
- # if no non-optimizable attributes were found, convert to plain text
+ if item[1] is None:
+ new.append(" " + item[0])
+ else:
+ new.append(" %s=%s" % (item[0], quote(item[1])))
if opt:
new.append(end)
collect.extend(new)
@@ -261,7 +222,7 @@
self.emitRawText(cgi.escape(text))
def emitDefines(self, defines):
- for part in TALDefs.splitParts(defines):
+ for part in splitParts(defines):
m = re.match(
r"(?s)\s*(?:(global|local)\s+)?(%s)\s+(.*)\Z" % NAME_RE, part)
if not m:
@@ -313,49 +274,6 @@
assert key == "structure"
self.emit("insertStructure", cexpr, attrDict, program)
- def emitI18nVariable(self, varname, action, expression):
- # Used for i18n:name attributes. arg is extra information describing
- # how the contents of the variable should get filled in, and it will
- # either be a 1-tuple or a 2-tuple. If arg[0] is None, then the
- # i18n:name value is taken implicitly from the contents of the tag,
- # e.g. "I live in <span i18n:name="country">the USA</span>". In this
- # case, arg[1] is the opcode sub-program describing the contents of
- # the tag.
- #
- # When arg[0] is not None, it contains the tal expression used to
- # calculate the contents of the variable, e.g.
- # "I live in <span i18n:name="country"
- # tal:replace="here/countryOfOrigin" />"
- key = cexpr = None
- program = self.popProgram()
- if action == I18N_REPLACE:
- # This is a tag with an i18n:name and a tal:replace (implicit or
- # explicit). Get rid of the first and last elements of the
- # program, which are the start and end tag opcodes of the tag.
- program = program[1:-1]
- elif action == I18N_CONTENT:
- # This is a tag with an i18n:name and a tal:content
- # (explicit-only). Keep the first and last elements of the
- # program, so we keep the start and end tag output.
- pass
- else:
- assert action == I18N_EXPRESSION
- key, expr = parseSubstitution(expression)
- cexpr = self.compileExpression(expr)
- # XXX Would key be anything but 'text' or None?
- assert key in ('text', None)
- self.emit('i18nVariable', varname, program, cexpr)
-
- def emitTranslation(self, msgid, i18ndata):
- program = self.popProgram()
- if i18ndata is None:
- self.emit('insertTranslation', msgid, program)
- else:
- key, expr = parseSubstitution(i18ndata)
- cexpr = self.compileExpression(expr)
- assert key == 'text'
- self.emit('insertTranslation', msgid, program, cexpr)
-
def emitDefineMacro(self, macroName):
program = self.popProgram()
macroName = macroName.strip()
@@ -443,30 +361,23 @@
return None
def replaceAttrs(self, attrlist, repldict):
- # Each entry in attrlist starts like (name, value).
- # Result is (name, value, action, expr, xlat) if there is a
- # tal:attributes entry for that attribute. Additional attrs
- # defined only by tal:attributes are added here.
- #
- # (name, value, action, expr, xlat)
if not repldict:
return attrlist
newlist = []
for item in attrlist:
key = item[0]
if repldict.has_key(key):
- expr, xlat = repldict[key]
- item = item[:2] + ("replace", expr, xlat)
+ item = item[:2] + ("replace", repldict[key])
del repldict[key]
newlist.append(item)
- # Add dynamic-only attributes
- for key, (expr, xlat) in repldict.items():
- newlist.append((key, None, "insert", expr, xlat))
+ for key, value in repldict.items(): # Add dynamic-only attributes
+ item = (key, None, "insert", value)
+ newlist.append(item)
return newlist
- def emitStartElement(self, name, attrlist, taldict, metaldict, i18ndict,
+ def emitStartElement(self, name, attrlist, taldict, metaldict,
position=(None, None), isend=0):
- if not taldict and not metaldict and not i18ndict:
+ if not taldict and not metaldict:
# Handle the simple, common case
self.emitStartTag(name, attrlist, isend)
self.todoPush({})
@@ -476,24 +387,18 @@
self.position = position
for key, value in taldict.items():
- if key not in TALDefs.KNOWN_TAL_ATTRIBUTES:
+ if key not in KNOWN_TAL_ATTRIBUTES:
raise TALError("bad TAL attribute: " + `key`, position)
if not (value or key == 'omit-tag'):
raise TALError("missing value for TAL attribute: " +
`key`, position)
for key, value in metaldict.items():
- if key not in TALDefs.KNOWN_METAL_ATTRIBUTES:
+ if key not in KNOWN_METAL_ATTRIBUTES:
raise METALError("bad METAL attribute: " + `key`,
- position)
+ position)
if not value:
raise TALError("missing value for METAL attribute: " +
`key`, position)
- for key, value in i18ndict.items():
- if key not in TALDefs.KNOWN_I18N_ATTRIBUTES:
- raise I18NError("bad i18n attribute: " + `key`, position)
- if not value and key in ("attributes", "data", "id"):
- raise I18NError("missing value for i18n attribute: " +
- `key`, position)
todo = {}
defineMacro = metaldict.get("define-macro")
useMacro = metaldict.get("use-macro")
@@ -508,31 +413,13 @@
onError = taldict.get("on-error")
omitTag = taldict.get("omit-tag")
TALtag = taldict.get("tal tag")
- i18nattrs = i18ndict.get("attributes")
- # Preserve empty string if implicit msgids are used. We'll generate
- # code with the msgid='' and calculate the right implicit msgid during
- # interpretation phase.
- msgid = i18ndict.get("translate")
- varname = i18ndict.get('name')
- i18ndata = i18ndict.get('data')
-
- if i18ndata and not msgid:
- raise I18NError("i18n:data must be accompanied by i18n:translate",
- position)
-
if len(metaldict) > 1 and (defineMacro or useMacro):
raise METALError("define-macro and use-macro cannot be used "
"together or with define-slot or fill-slot",
position)
- if replace:
- if content:
- raise TALError(
- "tal:content and tal:replace are mutually exclusive",
- position)
- if msgid is not None:
- raise I18NError(
- "i18n:translate and tal:replace are mutually exclusive",
- position)
+ if content and replace:
+ raise TALError("content and replace are mutually exclusive",
+ position)
repeatWhitespace = None
if repeat:
@@ -550,8 +437,8 @@
self.inMacroUse = 0
else:
if fillSlot:
- raise METALError("fill-slot must be within a use-macro",
- position)
+ raise METALError, ("fill-slot must be within a use-macro",
+ position)
if not self.inMacroUse:
if defineMacro:
self.pushProgram()
@@ -568,29 +455,13 @@
self.inMacroUse = 1
if defineSlot:
if not self.inMacroDef:
- raise METALError(
+ raise METALError, (
"define-slot must be within a define-macro",
position)
self.pushProgram()
todo["defineSlot"] = defineSlot
- if defineSlot or i18ndict:
-
- domain = i18ndict.get("domain") or self.i18nContext.domain
- source = i18ndict.get("source") or self.i18nContext.source
- target = i18ndict.get("target") or self.i18nContext.target
- if ( domain != DEFAULT_DOMAIN
- or source is not None
- or target is not None):
- self.i18nContext = TranslationContext(self.i18nContext,
- domain=domain,
- source=source,
- target=target)
- self.emit("beginI18nContext",
- {"domain": domain, "source": source,
- "target": target})
- todo["i18ncontext"] = 1
- if taldict or i18ndict:
+ if taldict:
dict = {}
for item in attrlist:
key, value = item[:2]
@@ -616,43 +487,16 @@
if content:
todo["content"] = content
if replace:
- # tal:replace w/ i18n:name has slightly different semantics. What
- # we're actually replacing then is the contents of the ${name}
- # placeholder.
- if varname:
- todo['i18nvar'] = (varname, replace)
- else:
- todo["replace"] = replace
+ todo["replace"] = replace
self.pushProgram()
- # i18n:name w/o tal:replace uses the content as the interpolation
- # dictionary values
- elif varname:
- todo['i18nvar'] = (varname, None)
- self.pushProgram()
- if msgid is not None:
- todo['msgid'] = msgid
- if i18ndata:
- todo['i18ndata'] = i18ndata
optTag = omitTag is not None or TALtag
if optTag:
todo["optional tag"] = omitTag, TALtag
self.pushProgram()
- if attrsubst or i18nattrs:
- if attrsubst:
- repldict = TALDefs.parseAttributeReplacements(attrsubst)
- else:
- repldict = {}
- if i18nattrs:
- i18nattrs = i18nattrs.split()
- else:
- i18nattrs = ()
- # Convert repldict's name-->expr mapping to a
- # name-->(compiled_expr, translate) mapping
+ if attrsubst:
+ repldict = parseAttributeReplacements(attrsubst)
for key, value in repldict.items():
- repldict[key] = self.compileExpression(value), key in i18nattrs
- for key in i18nattrs:
- if not repldict.has_key(key):
- repldict[key] = None, 1
+ repldict[key] = self.compileExpression(value)
else:
repldict = {}
if replace:
@@ -663,8 +507,6 @@
self.pushProgram()
if content:
self.pushProgram()
- if msgid is not None:
- self.pushProgram()
if todo and position != (None, None):
todo["position"] = position
self.todoPush(todo)
@@ -693,10 +535,6 @@
repldict = todo.get("repldict", {})
scope = todo.get("scope")
optTag = todo.get("optional tag")
- msgid = todo.get('msgid')
- i18ncontext = todo.get("i18ncontext")
- varname = todo.get('i18nvar')
- i18ndata = todo.get('i18ndata')
if implied > 0:
if defineMacro or useMacro or defineSlot or fillSlot:
@@ -708,51 +546,14 @@
raise exc("%s attributes on <%s> require explicit </%s>" %
(what, name, name), position)
- # If there's no tal:content or tal:replace in the tag with the
- # i18n:name, tal:replace is the default.
- i18nNameAction = I18N_REPLACE
if content:
- if varname:
- i18nNameAction = I18N_CONTENT
self.emitSubstitution(content, {})
- # If we're looking at an implicit msgid, emit the insertTranslation
- # opcode now, so that the end tag doesn't become part of the implicit
- # msgid. If we're looking at an explicit msgid, it's better to emit
- # the opcode after the i18nVariable opcode so we can better handle
- # tags with both of them in them (and in the latter case, the contents
- # would be thrown away for msgid purposes).
- if msgid is not None and not varname:
- self.emitTranslation(msgid, i18ndata)
if optTag:
self.emitOptTag(name, optTag, isend)
elif not isend:
- # If we're processing the end tag for a tag that contained
- # i18n:name, we need to make sure that optimize() won't collect
- # immediately following end tags into the same rawtextOffset, so
- # put a spacer here that the optimizer will recognize.
- if varname:
- self.emit('noop')
self.emitEndTag(name)
- # 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:
@@ -761,10 +562,6 @@
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:
=== Zope/lib/python/TAL/TALInterpreter.py 1.69.4.2 => 1.69.4.3 === (460/560 lines abridged)
--- Zope/lib/python/TAL/TALInterpreter.py:1.69.4.2 Sat Sep 28 21:40:35 2002
+++ Zope/lib/python/TAL/TALInterpreter.py Tue Oct 8 14:41:13 2002
@@ -8,7 +8,7 @@
# 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.
+# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
@@ -17,17 +17,17 @@
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 DocumentTemplate.DT_Util import ustr
-from TALDefs import TAL_VERSION, TALError, METALError
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+from TALDefs import quote, TAL_VERSION, TALError, METALError
from TALDefs import isCurrentVersion, getProgramVersion, getProgramMode
from TALGenerator import TALGenerator
-from TranslationContext import TranslationContext
BOOLEAN_HTML_ATTRS = [
# List of Boolean attributes in HTML that should be rendered in
@@ -40,12 +40,13 @@
"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())
-
+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
[-=- -=- -=- 460 lines omitted -=- -=- -=-]
- self.stream = stream = self.StringIO()
+ self.stream = stream = StringIO()
self._stream_write = stream.write
try:
self.interpret(block)
@@ -732,24 +598,24 @@
bytecode_handlers_tal["optTag"] = do_optTag_tal
-class FasterStringIO(StringIO):
- """Append-only version of StringIO.
-
- This let's us have a much faster write() method.
- """
- def close(self):
- if not self.closed:
- self.write = _write_ValueError
- StringIO.close(self)
-
- def seek(self, pos, mode=0):
- raise RuntimeError("FasterStringIO.seek() not allowed")
-
- def write(self, s):
- #assert self.pos == self.len
- self.buflist.append(s)
- self.len = self.pos = self.pos + len(s)
-
+def test():
+ from driver import FILE, parsefile
+ from DummyEngine import DummyEngine
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "")
+ except getopt.error, msg:
+ print msg
+ sys.exit(2)
+ if args:
+ file = args[0]
+ else:
+ file = FILE
+ doc = parsefile(file)
+ compiler = TALCompiler(doc)
+ program, macros = compiler()
+ engine = DummyEngine()
+ interpreter = TALInterpreter(program, macros, engine)
+ interpreter()
-def _write_ValueError(s):
- raise ValueError, "I/O operation on closed file"
+if __name__ == "__main__":
+ test()