[ZPT] CVS: Zope/lib/python/TAL - DummyEngine.py:1.32.4.3 TALGenerator.py:1.55.4.3 TALInterpreter.py:1.69.4.4
Chris McDonough
chrism@zope.com
Tue, 8 Oct 2002 17:46:25 -0400
Update of /cvs-repository/Zope/lib/python/TAL
In directory cvs.zope.org:/tmp/cvs-serv17057/lib/python/TAL
Modified Files:
Tag: chrism-install-branch
DummyEngine.py TALGenerator.py TALInterpreter.py
Log Message:
More merges from HEAD.
=== Zope/lib/python/TAL/DummyEngine.py 1.32.4.2 => 1.32.4.3 ===
--- Zope/lib/python/TAL/DummyEngine.py:1.32.4.2 Tue Oct 8 14:41:13 2002
+++ Zope/lib/python/TAL/DummyEngine.py Tue Oct 8 17:45:54 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,13 +18,25 @@
import re
import sys
-import driver
-
from TALDefs import NAME_RE, TALESError, ErrorInfo
+from ITALES import ITALESCompiler, ITALESEngine
+from DocumentTemplate.DT_Util import ustr
+
+IDomain = None
+if sys.modules.has_key('Zope'):
+ try:
+ from Zope.I18n.ITranslationService import ITranslationService
+ from Zope.I18n.IDomain import IDomain
+ except ImportError:
+ pass
+if IDomain is None:
+ # Before 2.7, or not in Zope
+ 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
@@ -36,6 +48,8 @@
position = None
source_file = None
+ __implements__ = ITALESCompiler, ITALESEngine
+
def __init__(self, macros=None):
if macros is None:
macros = {}
@@ -43,6 +57,7 @@
dict = {'nothing': None, 'default': Default}
self.locals = self.globals = dict
self.stack = [dict]
+ self.translationService = DummyTranslationService()
def getCompilerError(self):
return CompilerError
@@ -90,13 +105,7 @@
if type in ("string", "str"):
return expr
if type in ("path", "var", "global", "local"):
- 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`)
+ return self.evaluatePathOrVar(expr)
if type == "not":
return not self.evaluate(expr)
if type == "exists":
@@ -116,6 +125,15 @@
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)
@@ -125,7 +143,7 @@
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is not None and text is not Default:
- text = str(text)
+ text = ustr(text)
return text
def evaluateStructure(self, expr):
@@ -146,6 +164,7 @@
macro = self.macros[localName]
else:
# External macro
+ import driver
program, macros = driver.compilefile(file)
macro = macros.get(localName)
if not macro:
@@ -157,6 +176,7 @@
file, localName = self.findMacroFile(macroName)
if not file:
return file, localName
+ import driver
doc = driver.parsefile(file)
return doc, localName
@@ -183,6 +203,10 @@
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):
@@ -200,3 +224,31 @@
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.2 => 1.55.4.3 ===
--- Zope/lib/python/TAL/TALGenerator.py:1.55.4.2 Tue Oct 8 14:41:13 2002
+++ Zope/lib/python/TAL/TALGenerator.py Tue Oct 8 17:45:54 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,7 +18,16 @@
import re
import cgi
-from TALDefs import *
+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
class TALGenerator:
@@ -32,8 +41,15 @@
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 = {}
@@ -44,6 +60,7 @@
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
@@ -82,6 +99,12 @@
# 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")
@@ -102,9 +125,28 @@
else:
return item[0], tuple(item[1:])
- actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4,
- 0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
+ # 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
+ #
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
@@ -115,18 +157,15 @@
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], quote(item[1]))
+ s = '%s="%s"' % (item[0], cgi.escape(item[1], 1))
attrlist[i] = item[0], s
- if item[1] is None:
- new.append(" " + item[0])
- else:
- new.append(" %s=%s" % (item[0], quote(item[1])))
+ new.append(" " + s)
+ # if no non-optimizable attributes were found, convert to plain text
if opt:
new.append(end)
collect.extend(new)
@@ -222,7 +261,7 @@
self.emitRawText(cgi.escape(text))
def emitDefines(self, defines):
- for part in splitParts(defines):
+ for part in TALDefs.splitParts(defines):
m = re.match(
r"(?s)\s*(?:(global|local)\s+)?(%s)\s+(.*)\Z" % NAME_RE, part)
if not m:
@@ -236,7 +275,7 @@
else:
self.emit("setGlobal", name, cexpr)
- def emitOnError(self, name, onError):
+ def emitOnError(self, name, onError, TALtag, isend):
block = self.popProgram()
key, expr = parseSubstitution(onError)
cexpr = self.compileExpression(expr)
@@ -245,7 +284,10 @@
else:
assert key == "structure"
self.emit("insertStructure", cexpr, {}, [])
- self.emitEndTag(name)
+ if TALtag:
+ self.emitOptTag(name, (None, 1), isend)
+ else:
+ self.emitEndTag(name)
handler = self.popProgram()
self.emit("onError", block, handler)
@@ -274,6 +316,49 @@
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()
@@ -361,23 +446,30 @@
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):
- item = item[:2] + ("replace", repldict[key])
+ expr, xlat = repldict[key]
+ item = item[:2] + ("replace", expr, xlat)
del repldict[key]
newlist.append(item)
- for key, value in repldict.items(): # Add dynamic-only attributes
- item = (key, None, "insert", value)
- newlist.append(item)
+ # Add dynamic-only attributes
+ for key, (expr, xlat) in repldict.items():
+ newlist.append((key, None, "insert", expr, xlat))
return newlist
- def emitStartElement(self, name, attrlist, taldict, metaldict,
+ def emitStartElement(self, name, attrlist, taldict, metaldict, i18ndict,
position=(None, None), isend=0):
- if not taldict and not metaldict:
+ if not taldict and not metaldict and not i18ndict:
# Handle the simple, common case
self.emitStartTag(name, attrlist, isend)
self.todoPush({})
@@ -387,18 +479,24 @@
self.position = position
for key, value in taldict.items():
- if key not in KNOWN_TAL_ATTRIBUTES:
+ if key not in TALDefs.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 KNOWN_METAL_ATTRIBUTES:
+ if key not in TALDefs.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")
@@ -413,13 +511,31 @@
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 content and replace:
- raise TALError("content and replace are mutually exclusive",
- 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)
repeatWhitespace = None
if repeat:
@@ -437,8 +553,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()
@@ -455,13 +571,29 @@
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 taldict:
+ 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:
dict = {}
for item in attrlist:
key, value = item[:2]
@@ -470,7 +602,11 @@
todo["scope"] = 1
if onError:
self.pushProgram() # handler
+ if TALtag:
+ self.pushProgram() # start
self.emitStartTag(name, list(attrlist)) # Must copy attrlist!
+ if TALtag:
+ self.pushProgram() # start
self.pushProgram() # block
todo["onError"] = onError
if define:
@@ -487,16 +623,43 @@
if content:
todo["content"] = content
if replace:
- todo["replace"] = 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
+ 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:
- repldict = parseAttributeReplacements(attrsubst)
+ 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
for key, value in repldict.items():
- repldict[key] = self.compileExpression(value)
+ repldict[key] = self.compileExpression(value), key in i18nattrs
+ for key in i18nattrs:
+ if not repldict.has_key(key):
+ repldict[key] = None, 1
else:
repldict = {}
if replace:
@@ -507,6 +670,8 @@
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)
@@ -535,6 +700,10 @@
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:
@@ -546,22 +715,63 @@
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:
self.emitCondition(condition)
if onError:
- self.emitOnError(name, onError)
+ self.emitOnError(name, onError, optTag and optTag[1], isend)
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.3 => 1.69.4.4 === (486/586 lines abridged)
--- Zope/lib/python/TAL/TALInterpreter.py:1.69.4.3 Tue Oct 8 14:41:13 2002
+++ Zope/lib/python/TAL/TALInterpreter.py Tue Oct 8 17:45:54 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
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-from TALDefs import quote, TAL_VERSION, TALError, METALError
+from TALDefs import 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,13 +40,29 @@
"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",
-]
+def normalize(text):
+ # Now we need to normalize the whitespace in implicit message ids and
+ # implicit $name substitution values by stripping leading and trailing
[-=- -=- -=- 486 lines omitted -=- -=- -=-]
- self.stream = stream = StringIO()
+ self.stream = stream = self.StringIO()
self._stream_write = stream.write
try:
self.interpret(block)
@@ -598,24 +758,24 @@
bytecode_handlers_tal["optTag"] = do_optTag_tal
-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()
+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)
+
-if __name__ == "__main__":
- test()
+def _write_ValueError(s):
+ raise ValueError, "I/O operation on closed file"