[Zope-Checkins]
SVN: Zope/branches/ajung-zpt-end-game/lib/python/TAL/
Deprecate TAL package and make it a facade of zope.tal.
Philipp von Weitershausen
philikon at philikon.de
Sun May 28 19:07:08 EDT 2006
Log message for revision 68317:
Deprecate TAL package and make it a facade of zope.tal.
Changed:
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/DummyEngine.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/HTMLTALParser.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/ITALES.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALDefs.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALGenerator.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALInterpreter.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALParser.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/TranslationContext.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/XMLParser.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/driver.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/ndiff.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/runtest.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/talgettext.py
U Zope/branches/ajung-zpt-end-game/lib/python/TAL/timer.py
-=-
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/DummyEngine.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/DummyEngine.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/DummyEngine.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,239 +13,14 @@
##############################################################################
"""
Dummy TALES engine so that I can test out the TAL implementation.
+
+BBB 2005/05/01 -- to be removed after 12 months
"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.dummyengine', '2.12')
-import re
-import sys
+from zope.tal.dummyengine import DummyTranslationDomain as DummyDomain
-from TALDefs import NAME_RE, TALESError, ErrorInfo
-from ITALES import ITALESCompiler, ITALESEngine
-from DocumentTemplate.DT_Util import ustr
-
-class _Default:
- pass
-Default = _Default()
-
-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 getCompiler(self):
- return self
-
- 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 = ustr(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, default=None):
- return self.translationService.translate(domain, msgid, mapping,
- default=default)
-
-
-class Iterator:
-
- # This is not an implementation of a Python iterator. The next()
- # method returns true or false to indicate whether another item is
- # available; if there is another item, the iterator instance calls
- # setLocal() on the evaluation engine passed to the constructor.
-
- 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:
-
- def translate(self, msgid, mapping=None, context=None,
- target_language=None, default=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.
-
- # simulate an unknown msgid by returning None
- text = msgid
- if msgid == "don't translate me":
- if default is not None:
- text = default
- else:
- text = msgid.upper()
-
- def repl(m, mapping=mapping):
- return ustr(mapping[m.group(m.lastindex).lower()])
- cre = re.compile(r'\$(?:(%s)|\{(%s)\})' % (NAME_RE, NAME_RE))
- return cre.sub(repl, text)
-
class DummyTranslationService:
def translate(self, domain, msgid, mapping=None, context=None,
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/HTMLTALParser.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/HTMLTALParser.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/HTMLTALParser.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,303 +13,8 @@
##############################################################################
"""
Parse HTML and compile to TALInterpreter intermediate code.
-"""
-import sys
-
-from TALGenerator import TALGenerator
-from HTMLParser import HTMLParser, HTMLParseError
-from 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)
- if tag in EMPTY_HTML_TAGS and taldict.get("content"):
- raise TALError(
- "empty HTML tags cannot use tal:content: %s" % `tag`,
- self.getpos())
- 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"):
- if tag in EMPTY_HTML_TAGS:
- raise TALError(
- "empty HTML tags cannot use tal:content: %s" % `tag`,
- self.getpos())
- 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
+BBB 2005/05/01 -- to be removed after 12 months
+"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.htmltalparser', '2.12')
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/ITALES.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/ITALES.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/ITALES.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -1,156 +1,11 @@
"""Interface that a TALES engine provides to the METAL/TAL implementation."""
-try:
- from Interface import Interface
- from Interface.Attribute import Attribute
-except:
- # Before 2.7
- class Interface: pass
- def Attribute(*args): pass
+import zope.deferredimport
+zope.deferredimport.deprecatedFrom(
+ "The TAL implementation has moved to zope.tal. Import expression "
+ "interfaces from zope.tal.interfaces. The old references will be "
+ "gone in Zope 2.12.",
+ 'zope.tal.interfaces'
+ 'ITALExpressionCompiler', 'ITALExpressionEngine', 'ITALExpressionErrorInfo'
+ )
-
-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 getCompiler():
- """Return an object that supports ITALESCompiler."""
-
- 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, default=None):
- """
- 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.")
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALDefs.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALDefs.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALDefs.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,179 +13,16 @@
##############################################################################
"""
Common definitions used by TAL and METAL compilation an transformation.
+
+BBB 2005/05/01 -- to be removed after 12 months
"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.taldefs', '2.12')
-from types import ListType, TupleType
-
-from ITALES import ITALESErrorInfo
-
-TAL_VERSION = "1.5"
-
-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"
-
-# This RE must exactly match the expression of the same name in the
-# zope.i18n.simpletranslationservice module:
-NAME_RE = "[a-zA-Z_][-a-zA-Z0-9_]*"
-
-KNOWN_METAL_ATTRIBUTES = [
- "define-macro",
- "use-macro",
- "define-slot",
- "fill-slot",
- "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, xml):
- 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)
- if not xml:
- 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 zope.deferredimport
+zope.deferredimport.deprecated(
+ "TALESError has been renamed TALExpressionError and should be "
+ "imported from zope.tal.taldefs. This reference will be gone in "
+ "Zope 2.12.",
+ TALESError = 'zope.tal.taldefs.TALExpressionError'
+ )
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALGenerator.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALGenerator.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALGenerator.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,880 +13,8 @@
##############################################################################
"""
Code generator for TALInterpreter intermediate code.
-"""
-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
-
-_name_rx = re.compile(NAME_RE)
-
-
-class TALGenerator:
-
- inMacroUse = 0
- inMacroDef = 0
- source_file = None
-
- def __init__(self, expressionCompiler=None, xml=1, source_file=None):
- if not expressionCompiler:
- from 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
- # dictionary; key/values contain useful information that
- # emitEndElement needs to finish its calculations
- self.todoStack = []
- self.macros = {}
- self.slots = {}
- self.slotStack = []
- self.xml = xml
- self.emit("version", TAL_VERSION)
- self.emit("mode", xml and "xml" or "html")
- if source_file is not None:
- self.source_file = source_file
- self.emit("setSourceFile", source_file)
- self.i18nContext = TranslationContext()
- self.i18nLevel = 0
-
- def getCode(self):
- assert not self.stack
- assert not self.todoStack
- return self.optimize(self.program), self.macros
-
- def optimize(self, program):
- output = []
- collect = []
- cursor = 0
- if self.xml:
- endsep = "/>"
- else:
- endsep = " />"
- for cursor in xrange(len(program)+1):
- try:
- item = program[cursor]
- except IndexError:
- item = (None, None)
- opcode = item[0]
- if opcode == "rawtext":
- collect.append(item[1])
- continue
- if opcode == "endTag":
- collect.append("</%s>" % item[1])
- continue
- if opcode == "startTag":
- if self.optimizeStartTag(collect, item[1], item[2], ">"):
- continue
- if opcode == "startEndTag":
- if self.optimizeStartTag(collect, item[1], item[2], endsep):
- continue
- if opcode in ("beginScope", "endScope"):
- # Push *Scope instructions in front of any text instructions;
- # this allows text instructions separated only by *Scope
- # 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")
- if i >= 0:
- i = len(text) - (i + 1)
- output.append(("rawtextColumn", (text, i)))
- else:
- output.append(("rawtextOffset", (text, len(text))))
- if opcode != None:
- output.append(self.optimizeArgsList(item))
- collect = []
- return self.optimizeCommonTriple(output)
-
- def optimizeArgsList(self, item):
- if len(item) == 2:
- return item
- 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
- #
- 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
- opt = 1
- new = ["<" + name]
- for i in range(len(attrlist)):
- item = attrlist[i]
- if len(item) > 2:
- opt = 0
- name, value, action = item[:3]
- attrlist[i] = (name, value, action) + item[3:]
- else:
- if item[1] is None:
- s = item[0]
- else:
- s = '%s="%s"' % (item[0], TALDefs.attrEscape(item[1]))
- attrlist[i] = item[0], s
- new.append(" " + s)
- # if no non-optimizable attributes were found, convert to plain text
- if opt:
- new.append(end)
- collect.extend(new)
- return opt
-
- def optimizeCommonTriple(self, program):
- if len(program) < 3:
- return program
- output = program[:2]
- prev2, prev1 = output
- for item in program[2:]:
- if ( item[0] == "beginScope"
- and prev1[0] == "setPosition"
- and prev2[0] == "rawtextColumn"):
- position = output.pop()[1]
- text, column = output.pop()[1]
- prev1 = None, None
- closeprev = 0
- if output and output[-1][0] == "endScope":
- closeprev = 1
- output.pop()
- item = ("rawtextBeginScope",
- (text, column, position, closeprev, item[1]))
- output.append(item)
- prev2 = prev1
- prev1 = item
- return output
-
- def todoPush(self, todo):
- self.todoStack.append(todo)
-
- def todoPop(self):
- return self.todoStack.pop()
-
- def compileExpression(self, expr):
- try:
- return self.expressionCompiler.compile(expr)
- except self.CompilerError, err:
- raise TALError('%s in expression %s' % (err.args[0], `expr`),
- self.position)
-
- def pushProgram(self):
- self.stack.append(self.program)
- self.program = []
-
- def popProgram(self):
- program = self.program
- self.program = self.stack.pop()
- return self.optimize(program)
-
- def pushSlots(self):
- self.slotStack.append(self.slots)
- self.slots = {}
-
- def popSlots(self):
- slots = self.slots
- self.slots = self.slotStack.pop()
- return slots
-
- def emit(self, *instruction):
- self.program.append(instruction)
-
- def emitStartTag(self, name, attrlist, isend=0):
- if isend:
- opcode = "startEndTag"
- else:
- opcode = "startTag"
- self.emit(opcode, name, attrlist)
-
- def emitEndTag(self, name):
- if self.xml and self.program and self.program[-1][0] == "startTag":
- # Minimize empty element
- self.program[-1] = ("startEndTag",) + self.program[-1][1:]
- else:
- self.emit("endTag", name)
-
- def emitOptTag(self, name, optTag, isend):
- program = self.popProgram() #block
- start = self.popProgram() #start tag
- if (isend or not program) and self.xml:
- # Minimize empty element
- start[-1] = ("startEndTag",) + start[-1][1:]
- isend = 1
- cexpr = optTag[0]
- if cexpr:
- cexpr = self.compileExpression(optTag[0])
- self.emit("optTag", name, cexpr, optTag[1], isend, start, program)
-
- def emitRawText(self, text):
- self.emit("rawtext", text)
-
- def emitText(self, text):
- self.emitRawText(cgi.escape(text))
-
- def emitDefines(self, 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:
- raise TALError("invalid define syntax: " + `part`,
- self.position)
- scope, name, expr = m.group(1, 2, 3)
- scope = scope or "local"
- cexpr = self.compileExpression(expr)
- if scope == "local":
- self.emit("setLocal", name, cexpr)
- else:
- self.emit("setGlobal", name, cexpr)
-
- def emitOnError(self, name, onError, TALtag, isend):
- block = self.popProgram()
- key, expr = parseSubstitution(onError)
- cexpr = self.compileExpression(expr)
- if key == "text":
- self.emit("insertText", cexpr, [])
- else:
- assert key == "structure"
- self.emit("insertStructure", cexpr, {}, [])
- if TALtag:
- self.emitOptTag(name, (None, 1), isend)
- else:
- self.emitEndTag(name)
- handler = self.popProgram()
- self.emit("onError", block, handler)
-
- def emitCondition(self, expr):
- cexpr = self.compileExpression(expr)
- program = self.popProgram()
- self.emit("condition", cexpr, program)
-
- def emitRepeat(self, arg):
- m = re.match("(?s)\s*(%s)\s+(.*)\Z" % NAME_RE, arg)
- if not m:
- raise TALError("invalid repeat syntax: " + `arg`,
- self.position)
- name, expr = m.group(1, 2)
- cexpr = self.compileExpression(expr)
- program = self.popProgram()
- self.emit("loop", name, cexpr, program)
-
- def emitSubstitution(self, arg, attrDict={}):
- key, expr = parseSubstitution(arg)
- cexpr = self.compileExpression(expr)
- program = self.popProgram()
- if key == "text":
- self.emit("insertText", cexpr, program)
- else:
- assert key == "structure"
- self.emit("insertStructure", cexpr, attrDict, program)
-
- def emitI18nVariable(self, stuff):
- # 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" />"
- varname, action, expression = stuff
- m = _name_rx.match(varname)
- if m is None or m.group() != varname:
- raise TALError("illegal i18n:name: %r" % varname, self.position)
- 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)
- self.emit('i18nVariable',
- varname, program, cexpr, int(key == "structure"))
-
- 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()
- if self.macros.has_key(macroName):
- raise METALError("duplicate macro definition: %s" % `macroName`,
- self.position)
- if not re.match('%s$' % NAME_RE, macroName):
- raise METALError("invalid macro name: %s" % `macroName`,
- self.position)
- self.macros[macroName] = program
- self.inMacroDef = self.inMacroDef - 1
- self.emit("defineMacro", macroName, program)
-
- def emitUseMacro(self, expr):
- cexpr = self.compileExpression(expr)
- program = self.popProgram()
- self.inMacroUse = 0
- self.emit("useMacro", expr, cexpr, self.popSlots(), program)
-
- def emitDefineSlot(self, slotName):
- program = self.popProgram()
- slotName = slotName.strip()
- if not re.match('%s$' % NAME_RE, slotName):
- raise METALError("invalid slot name: %s" % `slotName`,
- self.position)
- self.emit("defineSlot", slotName, program)
-
- def emitFillSlot(self, slotName):
- program = self.popProgram()
- slotName = slotName.strip()
- if self.slots.has_key(slotName):
- raise METALError("duplicate fill-slot name: %s" % `slotName`,
- self.position)
- if not re.match('%s$' % NAME_RE, slotName):
- raise METALError("invalid slot name: %s" % `slotName`,
- self.position)
- self.slots[slotName] = program
- self.inMacroUse = 1
- self.emit("fillSlot", slotName, program)
-
- def unEmitWhitespace(self):
- collect = []
- i = len(self.program) - 1
- while i >= 0:
- item = self.program[i]
- if item[0] != "rawtext":
- break
- text = item[1]
- if not re.match(r"\A\s*\Z", text):
- break
- collect.append(text)
- i = i-1
- del self.program[i+1:]
- if i >= 0 and self.program[i][0] == "rawtext":
- text = self.program[i][1]
- m = re.search(r"\s+\Z", text)
- if m:
- self.program[i] = ("rawtext", text[:m.start()])
- collect.append(m.group())
- collect.reverse()
- return "".join(collect)
-
- def unEmitNewlineWhitespace(self):
- collect = []
- i = len(self.program)
- while i > 0:
- i = i-1
- item = self.program[i]
- if item[0] != "rawtext":
- break
- text = item[1]
- if re.match(r"\A[ \t]*\Z", text):
- collect.append(text)
- continue
- m = re.match(r"(?s)^(.*)(\n[ \t]*)\Z", text)
- if not m:
- break
- text, rest = m.group(1, 2)
- collect.reverse()
- rest = rest + "".join(collect)
- del self.program[i:]
- if text:
- self.emit("rawtext", text)
- return rest
- 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, msgid = repldict[key]
- item = item[:2] + ("replace", expr, xlat, msgid)
- del repldict[key]
- newlist.append(item)
- # Add dynamic-only attributes
- for key, (expr, xlat, msgid) in repldict.items():
- newlist.append((key, None, "insert", expr, xlat, msgid))
- return newlist
-
- def emitStartElement(self, name, attrlist, taldict, metaldict, i18ndict,
- position=(None, None), isend=0):
- if not taldict and not metaldict and not i18ndict:
- # Handle the simple, common case
- self.emitStartTag(name, attrlist, isend)
- self.todoPush({})
- if isend:
- self.emitEndElement(name, isend)
- return
-
- self.position = position
- for key, value in taldict.items():
- 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 TALDefs.KNOWN_METAL_ATTRIBUTES:
- raise METALError("bad METAL attribute: " + `key`,
- 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")
- defineSlot = metaldict.get("define-slot")
- fillSlot = metaldict.get("fill-slot")
- define = taldict.get("define")
- condition = taldict.get("condition")
- repeat = taldict.get("repeat")
- content = taldict.get("content")
- replace = taldict.get("replace")
- attrsubst = taldict.get("attributes")
- 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 varname and not self.i18nLevel:
- raise I18NError(
- "i18n:name can only occur inside a translation unit",
- position)
-
- 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)
-
- repeatWhitespace = None
- if repeat:
- # Hack to include preceding whitespace in the loop program
- repeatWhitespace = self.unEmitNewlineWhitespace()
- if position != (None, None):
- # XXX at some point we should insist on a non-trivial position
- self.emit("setPosition", position)
- if self.inMacroUse:
- if fillSlot:
- self.pushProgram()
- if self.source_file is not None:
- self.emit("setSourceFile", self.source_file)
- todo["fillSlot"] = fillSlot
- self.inMacroUse = 0
- else:
- if fillSlot:
- raise METALError("fill-slot must be within a use-macro",
- position)
- if not self.inMacroUse:
- if defineMacro:
- self.pushProgram()
- self.emit("version", TAL_VERSION)
- self.emit("mode", self.xml and "xml" or "html")
- if self.source_file is not None:
- self.emit("setSourceFile", self.source_file)
- todo["defineMacro"] = defineMacro
- self.inMacroDef = self.inMacroDef + 1
- if useMacro:
- self.pushSlots()
- self.pushProgram()
- todo["useMacro"] = useMacro
- self.inMacroUse = 1
- if defineSlot:
- if not self.inMacroDef:
- 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:
- dict = {}
- for item in attrlist:
- key, value = item[:2]
- dict[key] = value
- self.emit("beginScope", dict)
- 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:
- self.emitDefines(define)
- todo["define"] = define
- if condition:
- self.pushProgram()
- todo["condition"] = condition
- if repeat:
- todo["repeat"] = repeat
- self.pushProgram()
- if repeatWhitespace:
- self.emitText(repeatWhitespace)
- if content:
- if varname:
- todo['i18nvar'] = (varname, I18N_CONTENT, None)
- todo["content"] = content
- self.pushProgram()
- else:
- todo["content"] = content
- elif 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, I18N_EXPRESSION, 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, I18N_REPLACE, None)
- self.pushProgram()
- if msgid is not None:
- self.i18nLevel += 1
- 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,
- self.xml)
- else:
- repldict = {}
- if i18nattrs:
- i18nattrs = _parseI18nAttributes(i18nattrs, attrlist, repldict,
- self.position, self.xml,
- self.source_file)
- else:
- i18nattrs = {}
- # Convert repldict's name-->expr mapping to a
- # name-->(compiled_expr, translate) mapping
- for key, value in repldict.items():
- if i18nattrs.get(key, None):
- raise I18NError(
- ("attribute [%s] cannot both be part of tal:attributes" +
- " and have a msgid in i18n:attributes") % key,
- position)
- ce = self.compileExpression(value)
- repldict[key] = ce, key in i18nattrs, i18nattrs.get(key)
- for key in i18nattrs:
- if not repldict.has_key(key):
- repldict[key] = None, 1, i18nattrs.get(key)
- else:
- repldict = {}
- if replace:
- todo["repldict"] = repldict
- repldict = {}
- self.emitStartTag(name, self.replaceAttrs(attrlist, repldict), isend)
- if optTag:
- self.pushProgram()
- if content and not varname:
- self.pushProgram()
- if msgid is not None:
- self.pushProgram()
- if content and varname:
- self.pushProgram()
- if todo and position != (None, None):
- todo["position"] = position
- self.todoPush(todo)
- if isend:
- self.emitEndElement(name, isend)
-
- def emitEndElement(self, name, isend=0, implied=0):
- todo = self.todoPop()
- if not todo:
- # Shortcut
- if not isend:
- self.emitEndTag(name)
- return
-
- self.position = position = todo.get("position", (None, None))
- defineMacro = todo.get("defineMacro")
- useMacro = todo.get("useMacro")
- defineSlot = todo.get("defineSlot")
- fillSlot = todo.get("fillSlot")
- repeat = todo.get("repeat")
- content = todo.get("content")
- replace = todo.get("replace")
- condition = todo.get("condition")
- onError = todo.get("onError")
- 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:
- exc = METALError
- what = "METAL"
- else:
- exc = TALError
- what = "TAL"
- 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.
- if 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).
- #
- # Still, we should emit insertTranslation opcode before i18nVariable
- # in case tal:content, i18n:translate and i18n:name in the same tag
- if msgid is not None:
- if (not varname) or (
- varname and (varname[1] == I18N_CONTENT)):
- self.emitTranslation(msgid, i18ndata)
- self.i18nLevel -= 1
- 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:
- # o varname[0] is the variable name
- # o varname[1] is either
- # - I18N_REPLACE for implicit tal:replace
- # - I18N_CONTENT for tal:content
- # - I18N_EXPRESSION for explicit tal:replace
- # o varname[2] will be None for the first two actions and the
- # replacement tal expression for the third action. This
- # can include a 'text' or 'structure' indicator.
- assert (varname[1]
- in [I18N_REPLACE, I18N_CONTENT, I18N_EXPRESSION])
- self.emitI18nVariable(varname)
- # 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:
- # in case tal:content, i18n:translate and i18n:name in the
- # same tag insertTranslation opcode has already been
- # emitted
- if varname and (varname[1] <> I18N_CONTENT):
- self.emitTranslation(msgid, i18ndata)
- if repeat:
- self.emitRepeat(repeat)
- if condition:
- self.emitCondition(condition)
- if 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:
- self.emitFillSlot(fillSlot)
- if useMacro:
- self.emitUseMacro(useMacro)
- if defineMacro:
- self.emitDefineMacro(defineMacro)
-
-
-def _parseI18nAttributes(i18nattrs, attrlist, repldict, position,
- xml, source_file):
-
- def addAttribute(dic, attr, msgid, position, xml):
- if not xml:
- attr = attr.lower()
- if attr in dic:
- raise TALError(
- "attribute may only be specified once in i18n:attributes: "
- + attr,
- position)
- dic[attr] = msgid
-
- d = {}
- if ';' in i18nattrs:
- i18nattrlist = i18nattrs.split(';')
- i18nattrlist = [attr.strip().split()
- for attr in i18nattrlist if attr.strip()]
- for parts in i18nattrlist:
- if len(parts) > 2:
- raise TALError("illegal i18n:attributes specification: %r"
- % parts, position)
- if len(parts) == 2:
- attr, msgid = parts
- else:
- # len(parts) == 1
- attr = parts[0]
- msgid = None
- addAttribute(d, attr, msgid, position, xml)
- else:
- i18nattrlist = i18nattrs.split()
- if len(i18nattrlist) == 1:
- addAttribute(d, i18nattrlist[0], None, position, xml)
- elif len(i18nattrlist) == 2:
- staticattrs = [attr[0] for attr in attrlist if len(attr) == 2]
- if (not i18nattrlist[1] in staticattrs) and (
- not i18nattrlist[1] in repldict):
- attr, msgid = i18nattrlist
- addAttribute(d, attr, msgid, position, xml)
- else:
- import warnings
- warnings.warn(I18N_ATTRIBUTES_WARNING
- % (source_file, str(position), i18nattrs)
- , DeprecationWarning)
- msgid = None
- for attr in i18nattrlist:
- addAttribute(d, attr, msgid, position, xml)
- else:
- import warnings
- warnings.warn(I18N_ATTRIBUTES_WARNING
- % (source_file, str(position), i18nattrs)
- , DeprecationWarning)
- msgid = None
- for attr in i18nattrlist:
- addAttribute(d, attr, msgid, position, xml)
- return d
-
-I18N_ATTRIBUTES_WARNING = (
- 'Space separated attributes in i18n:attributes'
- ' are deprecated (i18n:attributes="value title"). Please use'
- ' semicolon to separate attributes'
- ' (i18n:attributes="value; title").'
- '\nFile %s at row, column %s\nAttributes %s')
-
-def test():
- t = TALGenerator()
- t.pushProgram()
- t.emit("bar")
- p = t.popProgram()
- t.emit("foo", p)
-
-if __name__ == "__main__":
- test()
+BBB 2005/05/01 -- to be removed after 12 months
+"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.talgenerator', '2.12')
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALInterpreter.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALInterpreter.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALInterpreter.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,828 +13,16 @@
##############################################################################
"""Interpreter for a pre-compiled TAL program.
+BBB 2005/05/01 -- to be removed after 12 months
+
$Id$
"""
-import cgi
-import sys
-import re
+import zope.deprecation
+zope.deprecation.moved('zope.tal.talinterpreter', '2.12')
-# Do not use cStringIO here! It's not unicode aware. :(
-from StringIO import StringIO
-from DocumentTemplate.DT_Util import ustr
-from ZODB.POSException import ConflictError
-
-from zope.i18nmessageid import Message
-
-from TALDefs import attrEscape, TAL_VERSION, METALError
-from TALDefs import isCurrentVersion
-from TALDefs import getProgramVersion, getProgramMode
-from TALGenerator import TALGenerator
-from TranslationContext import TranslationContext
-
-I18nMessageTypes = (Message,)
-
-# TODO: In Python 2.4 we can use frozenset() instead of dict.fromkeys()
-BOOLEAN_HTML_ATTRS = dict.fromkeys([
- # 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)
- # TODO: 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"
-])
-
-_nulljoin = ''.join
-_spacejoin = ' '.join
-
-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 _spacejoin(text.split())
-
-
-NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
-_interp_regex = re.compile(r'(?<!\$)(\$(?:%(n)s|{%(n)s}))' %({'n': NAME_RE}))
-_get_var_regex = re.compile(r'%(n)s' %({'n': NAME_RE}))
-
-def interpolate(text, mapping):
- """Interpolate ${keyword} substitutions.
-
- This is called when no translation is provided by the translation
- service.
- """
- if not mapping:
- return text
- # Find all the spots we want to substitute.
- to_replace = _interp_regex.findall(text)
- # Now substitute with the variables in mapping.
- for string in to_replace:
- var = _get_var_regex.findall(string)[0]
- if mapping.has_key(var):
- # Call ustr because we may have an integer for instance.
- subst = ustr(mapping[var])
- try:
- text = text.replace(string, subst)
- except UnicodeError:
- # subst contains high-bit chars...
- # As we have no way of knowing the correct encoding,
- # substitue something instead of raising an exception.
- subst = `subst`[1:-1]
- text = text.replace(string, subst)
- return text
-
-
-class AltTALGenerator(TALGenerator):
-
- def __init__(self, repldict, expressionCompiler=None, xml=0):
- self.repldict = repldict
- self.enabled = 1
- TALGenerator.__init__(self, expressionCompiler, xml)
-
- def enable(self, enabled):
- self.enabled = enabled
-
- def emit(self, *args):
- if self.enabled:
- TALGenerator.emit(self, *args)
-
- def emitStartElement(self, name, attrlist, taldict, metaldict, i18ndict,
- position=(None, None), isend=0):
- metaldict = {}
- taldict = {}
- i18ndict = {}
- if self.enabled and self.repldict:
- taldict["attributes"] = "x x"
- TALGenerator.emitStartElement(self, name, attrlist,
- taldict, metaldict, i18ndict,
- position, isend)
-
- def replaceAttrs(self, attrlist, repldict):
- if self.enabled and self.repldict:
- repldict = self.repldict
- self.repldict = None
- return TALGenerator.replaceAttrs(self, attrlist, repldict)
-
-
-class TALInterpreter:
- """TAL interpreter.
- """
-
- def __init__(self, program, macros, engine, stream=None,
- debug=0, wrap=60, metal=1, tal=1, showtal=-1,
- strictinsert=1, stackLimit=100, i18nInterpolate=1):
- """Create a TAL interpreter.
-
- Optional arguments:
-
- stream -- output stream (defaults to sys.stdout).
-
- debug -- enable debugging output to sys.stderr (off by default).
-
- wrap -- try to wrap attributes on opening tags to this number of
- column (default: 60).
-
- metal -- enable METAL macro processing (on by default).
-
- tal -- enable TAL processing (on by default).
-
- showtal -- do not strip away TAL directives. A special value of
- -1 (which is the default setting) enables showtal when TAL
- processing is disabled, and disables showtal when TAL processing is
- enabled. Note that you must use 0, 1, or -1; true boolean values
- are not supported (TODO: why?).
-
- strictinsert -- enable TAL processing and stricter HTML/XML
- checking on text produced by structure inserts (on by default).
- Note that Zope turns this value off by default.
-
- stackLimit -- set macro nesting limit (default: 100).
-
- i18nInterpolate -- enable i18n translations (default: on).
-
- """
- self.program = program
- self.macros = macros
- self.engine = engine # Execution engine (aka context)
- self.Default = engine.getDefault()
- self._currentTag = ""
- self._stream_stack = [stream or sys.stdout]
- self.popStream()
- self.debug = debug
- self.wrap = wrap
- self.metal = metal
- self.tal = tal
- if tal:
- self.dispatch = self.bytecode_handlers_tal
- else:
- self.dispatch = self.bytecode_handlers
- assert showtal in (-1, 0, 1)
- if showtal == -1:
- showtal = (not tal)
- self.showtal = showtal
- self.strictinsert = strictinsert
- self.stackLimit = stackLimit
- self.html = 0
- self.endsep = "/>"
- self.endlen = len(self.endsep)
- self.macroStack = []
- self.position = None, None # (lineno, offset)
- self.col = 0
- self.level = 0
- self.scopeLevel = 0
- self.sourceFile = None
- self.i18nStack = []
- self.i18nInterpolate = i18nInterpolate
- self.i18nContext = TranslationContext()
-
- def StringIO(self):
- # Third-party products wishing to provide a full Unicode-aware
- # StringIO can do so by monkey-patching this method.
- return FasterStringIO()
-
- def saveState(self):
- return (self.position, self.col, self.stream, self._stream_stack,
- self.scopeLevel, self.level, self.i18nContext)
-
- def restoreState(self, state):
- (self.position, self.col, self.stream,
- self._stream_stack, scopeLevel, level, i18n) = state
- self._stream_write = self.stream.write
- assert self.level == level
- while self.scopeLevel > scopeLevel:
- self.engine.endScope()
- self.scopeLevel = self.scopeLevel - 1
- self.engine.setPosition(self.position)
- self.i18nContext = i18n
-
- def restoreOutputState(self, state):
- (dummy, self.col, self.stream,
- self._stream_stack, scopeLevel, level, i18n) = state
- self._stream_write = self.stream.write
- assert self.level == level
- assert self.scopeLevel == scopeLevel
-
- def pushMacro(self, macroName, slots, entering=1):
- if len(self.macroStack) >= self.stackLimit:
- raise METALError("macro nesting limit (%d) exceeded "
- "by %s" % (self.stackLimit, `macroName`))
- self.macroStack.append([macroName, slots, entering, self.i18nContext])
-
- def popMacro(self):
- return self.macroStack.pop()
-
- def __call__(self):
- assert self.level == 0
- assert self.scopeLevel == 0
- assert self.i18nContext.parent is None
- self.interpret(self.program)
- assert self.level == 0
- assert self.scopeLevel == 0
- assert self.i18nContext.parent is None
- if self.col > 0:
- self._stream_write("\n")
- self.col = 0
-
- def pushStream(self, newstream):
- self._stream_stack.append(self.stream)
- self.stream = newstream
- self._stream_write = self.stream.write
-
- def popStream(self):
- self.stream = self._stream_stack.pop()
- self._stream_write = self.stream.write
-
- def stream_write(self, s,
- len=len):
- self._stream_write(s)
- i = s.rfind('\n')
- if i < 0:
- self.col = self.col + len(s)
- else:
- self.col = len(s) - (i + 1)
-
- bytecode_handlers = {}
-
- def interpret(self, program):
- oldlevel = self.level
- self.level = oldlevel + 1
- handlers = self.dispatch
- try:
- if self.debug:
- for (opcode, args) in program:
- s = "%sdo_%s(%s)\n" % (" "*self.level, opcode,
- repr(args))
- if len(s) > 80:
- s = s[:76] + "...\n"
- sys.stderr.write(s)
- handlers[opcode](self, args)
- else:
- for (opcode, args) in program:
- handlers[opcode](self, args)
- finally:
- self.level = oldlevel
-
- def do_version(self, version):
- assert version == TAL_VERSION
- bytecode_handlers["version"] = do_version
-
- def do_mode(self, mode):
- assert mode in ("html", "xml")
- self.html = (mode == "html")
- if self.html:
- self.endsep = " />"
- else:
- self.endsep = "/>"
- self.endlen = len(self.endsep)
- bytecode_handlers["mode"] = do_mode
-
- def do_setSourceFile(self, source_file):
- self.sourceFile = source_file
- self.engine.setSourceFile(source_file)
- bytecode_handlers["setSourceFile"] = do_setSourceFile
-
- def do_setPosition(self, position):
- self.position = position
- self.engine.setPosition(position)
- bytecode_handlers["setPosition"] = do_setPosition
-
- def do_startEndTag(self, stuff):
- self.do_startTag(stuff, self.endsep, self.endlen)
- bytecode_handlers["startEndTag"] = do_startEndTag
-
- def do_startTag(self, (name, attrList),
- end=">", endlen=1, _len=len):
- # The bytecode generator does not cause calls to this method
- # for start tags with no attributes; those are optimized down
- # to rawtext events. Hence, there is no special "fast path"
- # for that case.
- self._currentTag = name
- L = ["<", name]
- append = L.append
- col = self.col + _len(name) + 1
- wrap = self.wrap
- align = col + 1
- if align >= wrap/2:
- align = 4 # Avoid a narrow column far to the right
- attrAction = self.dispatch["<attrAction>"]
- try:
- for item in attrList:
- if _len(item) == 2:
- name, s = item
- else:
- # item[2] is the 'action' field:
- if item[2] in ('metal', 'tal', 'xmlns', 'i18n'):
- if not self.showtal:
- continue
- ok, name, s = self.attrAction(item)
- else:
- ok, name, s = attrAction(self, item)
- if not ok:
- continue
- slen = _len(s)
- if (wrap and
- col >= align and
- col + 1 + slen > wrap):
- append("\n")
- append(" "*align)
- col = align + slen
- else:
- append(" ")
- col = col + 1 + slen
- append(s)
- append(end)
- col = col + endlen
- finally:
- self._stream_write(_nulljoin(L))
- self.col = col
- bytecode_handlers["startTag"] = do_startTag
-
- def attrAction(self, item):
- name, value, action = item[:3]
- if action == 'insert':
- return 0, name, value
- macs = self.macroStack
- if action == 'metal' and self.metal and macs:
- if len(macs) > 1 or not macs[-1][2]:
- # Drop all METAL attributes at a use-depth above one.
- return 0, name, value
- # Clear 'entering' flag
- macs[-1][2] = 0
- # Convert or drop depth-one METAL attributes.
- i = name.rfind(":") + 1
- prefix, suffix = name[:i], name[i:]
- if suffix == "define-macro":
- # Convert define-macro as we enter depth one.
- name = prefix + "use-macro"
- value = macs[-1][0] # Macro name
- elif suffix == "define-slot":
- name = prefix + "fill-slot"
- elif suffix == "fill-slot":
- pass
- else:
- return 0, name, value
-
- if value is None:
- value = name
- else:
- value = '%s="%s"' % (name, attrEscape(value))
- return 1, name, value
-
- def attrAction_tal(self, item):
- name, value, action = item[:3]
- ok = 1
- expr, xlat, msgid = item[3:]
- if self.html and name.lower() in BOOLEAN_HTML_ATTRS:
- evalue = self.engine.evaluateBoolean(item[3])
- if evalue is self.Default:
- if action == 'insert': # Cancelled insert
- ok = 0
- elif evalue:
- value = None
- else:
- ok = 0
- elif expr is not None:
- evalue = self.engine.evaluateText(item[3])
- if evalue is self.Default:
- if action == 'insert': # Cancelled insert
- ok = 0
- else:
- if evalue is None:
- ok = 0
- value = evalue
- else:
- evalue = None
-
- if ok:
- if xlat:
- translated = self.translate(msgid or value, value, {})
- if translated is not None:
- value = translated
- if value is None:
- value = name
- elif evalue is self.Default:
- value = attrEscape(value)
- else:
- value = cgi.escape(value, quote=1)
- value = '%s="%s"' % (name, value)
- return ok, name, value
- bytecode_handlers["<attrAction>"] = attrAction
-
- def no_tag(self, start, program):
- state = self.saveState()
- self.stream = stream = self.StringIO()
- self._stream_write = stream.write
- self.interpret(start)
- self.restoreOutputState(state)
- self.interpret(program)
-
- def do_optTag(self, (name, cexpr, tag_ns, isend, start, program),
- omit=0):
- if tag_ns and not self.showtal:
- return self.no_tag(start, program)
-
- self.interpret(start)
- if not isend:
- self.interpret(program)
- s = '</%s>' % name
- self._stream_write(s)
- self.col = self.col + len(s)
-
- def do_optTag_tal(self, stuff):
- cexpr = stuff[1]
- if cexpr is not None and (cexpr == '' or
- self.engine.evaluateBoolean(cexpr)):
- self.no_tag(stuff[-2], stuff[-1])
- else:
- self.do_optTag(stuff)
- bytecode_handlers["optTag"] = do_optTag
-
- def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)):
- self._stream_write(s)
- self.col = col
- self.do_setPosition(position)
- if closeprev:
- engine = self.engine
- engine.endScope()
- engine.beginScope()
- else:
- self.engine.beginScope()
- self.scopeLevel = self.scopeLevel + 1
-
- def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)):
- self._stream_write(s)
- self.col = col
- engine = self.engine
- self.position = position
- engine.setPosition(position)
- if closeprev:
- engine.endScope()
- engine.beginScope()
- else:
- engine.beginScope()
- self.scopeLevel = self.scopeLevel + 1
- engine.setLocal("attrs", dict)
- bytecode_handlers["rawtextBeginScope"] = do_rawtextBeginScope
-
- def do_beginScope(self, dict):
- self.engine.beginScope()
- self.scopeLevel = self.scopeLevel + 1
-
- def do_beginScope_tal(self, dict):
- engine = self.engine
- engine.beginScope()
- engine.setLocal("attrs", dict)
- self.scopeLevel = self.scopeLevel + 1
- bytecode_handlers["beginScope"] = do_beginScope
-
- def do_endScope(self, notused=None):
- self.engine.endScope()
- self.scopeLevel = self.scopeLevel - 1
- bytecode_handlers["endScope"] = do_endScope
-
- def do_setLocal(self, notused):
- pass
-
- def do_setLocal_tal(self, (name, expr)):
- self.engine.setLocal(name, self.engine.evaluateValue(expr))
- bytecode_handlers["setLocal"] = do_setLocal
-
- def do_setGlobal_tal(self, (name, expr)):
- self.engine.setGlobal(name, self.engine.evaluateValue(expr))
- bytecode_handlers["setGlobal"] = do_setLocal
-
- def do_beginI18nContext(self, settings):
- get = settings.get
- self.i18nContext = TranslationContext(self.i18nContext,
- domain=get("domain"),
- source=get("source"),
- target=get("target"))
- bytecode_handlers["beginI18nContext"] = do_beginI18nContext
-
- def do_endI18nContext(self, notused=None):
- self.i18nContext = self.i18nContext.parent
- assert self.i18nContext is not None
- bytecode_handlers["endI18nContext"] = do_endI18nContext
-
- def do_insertText(self, stuff):
- self.interpret(stuff[1])
-
- def do_insertText_tal(self, stuff):
- text = self.engine.evaluateText(stuff[0])
- if text is None:
- return
- if text is self.Default:
- self.interpret(stuff[1])
- return
- if isinstance(text, I18nMessageTypes):
- # Translate this now.
- text = self.engine.translate(text.domain, text,
- text.mapping, default=text.default)
- s = cgi.escape(text)
- self._stream_write(s)
- i = s.rfind('\n')
- if i < 0:
- self.col = self.col + len(s)
- else:
- self.col = len(s) - (i + 1)
- bytecode_handlers["insertText"] = do_insertText
-
- def do_i18nVariable(self, stuff):
- varname, program, expression, structure = stuff
- if expression is None:
- # The value is implicitly the contents of this tag, so we have to
- # evaluate the mini-program to get the value of the variable.
- state = self.saveState()
- try:
- tmpstream = self.StringIO()
- self.pushStream(tmpstream)
- try:
- self.interpret(program)
- finally:
- self.popStream()
- if self.html and self._currentTag == "pre":
- value = tmpstream.getvalue()
- else:
- value = normalize(tmpstream.getvalue())
- finally:
- self.restoreState(state)
- else:
- # Evaluate the value to be associated with the variable in the
- # i18n interpolation dictionary.
- if structure:
- value = self.engine.evaluateStructure(expression)
- else:
- value = self.engine.evaluate(expression)
-
- # evaluate() does not do any I18n, so we do it here.
- if isinstance(value, I18nMessageTypes):
- # Translate this now.
- # XXX
- value = self.engine.translate(value.domain, value,
- value.mapping, value.default)
-
- if not structure:
- value = cgi.escape(ustr(value))
-
- # Either the i18n:name tag is nested inside an i18n:translate in which
- # case the last item on the stack has the i18n dictionary and string
- # representation, or the i18n:name and i18n:translate attributes are
- # in the same tag, in which case the i18nStack will be empty. In that
- # case we can just output the ${name} to the stream
- i18ndict, srepr = self.i18nStack[-1]
- i18ndict[varname] = value
- placeholder = '${%s}' % varname
- srepr.append(placeholder)
- self._stream_write(placeholder)
- bytecode_handlers['i18nVariable'] = do_i18nVariable
-
- def do_insertTranslation(self, stuff):
- i18ndict = {}
- srepr = []
- obj = None
- self.i18nStack.append((i18ndict, srepr))
- msgid = stuff[0]
- # We need to evaluate the content of the tag because that will give us
- # several useful pieces of information. First, the contents will
- # include an implicit message id, if no explicit one was given.
- # Second, it will evaluate any i18nVariable definitions in the body of
- # the translation (necessary for $varname substitutions).
- #
- # Use a temporary stream to capture the interpretation of the
- # subnodes, which should /not/ go to the output stream.
- currentTag = self._currentTag
- tmpstream = self.StringIO()
- self.pushStream(tmpstream)
- try:
- self.interpret(stuff[1])
- finally:
- self.popStream()
- # We only care about the evaluated contents if we need an implicit
- # message id. All other useful information will be in the i18ndict on
- # the top of the i18nStack.
- default = tmpstream.getvalue()
- if not msgid:
- if self.html and currentTag == "pre":
- msgid = default
- else:
- msgid = normalize(default)
- self.i18nStack.pop()
- # See if there is was an i18n:data for msgid
- if len(stuff) > 2:
- obj = self.engine.evaluate(stuff[2])
- xlated_msgid = self.translate(msgid, default, i18ndict, obj)
- # TODO: I can't decide whether we want to cgi escape the translated
- # string or not. OTOH not doing this could introduce a cross-site
- # scripting vector by allowing translators to sneak JavaScript into
- # translations. OTOH, for implicit interpolation values, we don't
- # want to escape stuff like ${name} <= "<b>Timmy</b>".
- assert xlated_msgid is not None
- self._stream_write(xlated_msgid)
- bytecode_handlers['insertTranslation'] = do_insertTranslation
-
- def do_insertStructure(self, stuff):
- self.interpret(stuff[2])
-
- def do_insertStructure_tal(self, (expr, repldict, block)):
- structure = self.engine.evaluateStructure(expr)
- if structure is None:
- return
- if structure is self.Default:
- self.interpret(block)
- return
- text = ustr(structure)
- if not (repldict or self.strictinsert):
- # Take a shortcut, no error checking
- self.stream_write(text)
- return
- if self.html:
- self.insertHTMLStructure(text, repldict)
- else:
- self.insertXMLStructure(text, repldict)
- bytecode_handlers["insertStructure"] = do_insertStructure
-
- def insertHTMLStructure(self, text, repldict):
- from HTMLTALParser import HTMLTALParser
- gen = AltTALGenerator(repldict, self.engine.getCompiler(), 0)
- p = HTMLTALParser(gen) # Raises an exception if text is invalid
- p.parseString(text)
- program, macros = p.getCode()
- self.interpret(program)
-
- def insertXMLStructure(self, text, repldict):
- from TALParser import TALParser
- gen = AltTALGenerator(repldict, self.engine.getCompiler(), 0)
- p = TALParser(gen)
- gen.enable(0)
- p.parseFragment('<!DOCTYPE foo PUBLIC "foo" "bar"><foo>')
- gen.enable(1)
- p.parseFragment(text) # Raises an exception if text is invalid
- gen.enable(0)
- p.parseFragment('</foo>', 1)
- program, macros = gen.getCode()
- self.interpret(program)
-
- def do_loop(self, (name, expr, block)):
- self.interpret(block)
-
- def do_loop_tal(self, (name, expr, block)):
- iterator = self.engine.setRepeat(name, expr)
- while iterator.next():
- self.interpret(block)
- bytecode_handlers["loop"] = do_loop
-
- def translate(self, msgid, default, i18ndict, obj=None):
- if obj:
- i18ndict.update(obj)
- if not self.i18nInterpolate:
- return msgid
- # TODO: We need to pass in one of context or target_language
- return self.engine.translate(self.i18nContext.domain,
- msgid, i18ndict, default=default)
-
- def do_rawtextColumn(self, (s, col)):
- self._stream_write(s)
- self.col = col
- bytecode_handlers["rawtextColumn"] = do_rawtextColumn
-
- def do_rawtextOffset(self, (s, offset)):
- self._stream_write(s)
- self.col = self.col + offset
- bytecode_handlers["rawtextOffset"] = do_rawtextOffset
-
- def do_condition(self, (condition, block)):
- if not self.tal or self.engine.evaluateBoolean(condition):
- self.interpret(block)
- bytecode_handlers["condition"] = do_condition
-
- def do_defineMacro(self, (macroName, macro)):
- macs = self.macroStack
- if len(macs) == 1:
- entering = macs[-1][2]
- if not entering:
- macs.append(None)
- self.interpret(macro)
- assert macs[-1] is None
- macs.pop()
- return
- self.interpret(macro)
- bytecode_handlers["defineMacro"] = do_defineMacro
-
- def do_useMacro(self, (macroName, macroExpr, compiledSlots, block)):
- if not self.metal:
- self.interpret(block)
- return
- macro = self.engine.evaluateMacro(macroExpr)
- if macro is self.Default:
- macro = block
- else:
- if not isCurrentVersion(macro):
- raise METALError("macro %s has incompatible version %s" %
- (`macroName`, `getProgramVersion(macro)`),
- self.position)
- mode = getProgramMode(macro)
- if mode != (self.html and "html" or "xml"):
- raise METALError("macro %s has incompatible mode %s" %
- (`macroName`, `mode`), self.position)
- self.pushMacro(macroName, compiledSlots)
- prev_source = self.sourceFile
- self.interpret(macro)
- if self.sourceFile != prev_source:
- self.engine.setSourceFile(prev_source)
- self.sourceFile = prev_source
- self.popMacro()
- bytecode_handlers["useMacro"] = do_useMacro
-
- def do_fillSlot(self, (slotName, block)):
- # This is only executed if the enclosing 'use-macro' evaluates
- # to 'default'.
- self.interpret(block)
- bytecode_handlers["fillSlot"] = do_fillSlot
-
- def do_defineSlot(self, (slotName, block)):
- if not self.metal:
- self.interpret(block)
- return
- macs = self.macroStack
- if macs and macs[-1] is not None:
- macroName, slots = self.popMacro()[:2]
- slot = slots.get(slotName)
- if slot is not None:
- prev_source = self.sourceFile
- try:
- self.interpret(slot)
- finally:
- 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 = self.StringIO()
- self._stream_write = stream.write
- try:
- self.interpret(block)
- except ConflictError:
- raise
- 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
-
-
-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 _write_ValueError(s):
- raise ValueError, "I/O operation on closed file"
+import zope.deferredimport
+zope.deferredimport.deprecated(
+ "'interpolate' has moved to zope.i18n. This reference will be gone "
+ "in Zope 2.12.",
+ interpolate = 'zope.i18n:interpolate'
+ )
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALParser.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALParser.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/TALParser.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,131 +13,8 @@
##############################################################################
"""
Parse XML and compile to TALInterpreter intermediate code.
-"""
-from XMLParser import XMLParser
-from TALDefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS
-from 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, value
- if ns == 'metal':
- metaldict[keybase] = value
- item = item + ("metal",)
- elif ns == 'tal':
- taldict[keybase] = value
- item = item + ("tal",)
- elif ns == 'i18n':
- 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 TALInterpreter import TALInterpreter
- from DummyEngine import DummyEngine
- engine = DummyEngine(macros)
- TALInterpreter(program, macros, engine, sys.stdout, wrap=0)()
-
-if __name__ == "__main__":
- test()
+BBB 2005/05/01 -- to be removed after 12 months
+"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.talparser', '2.12')
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/TranslationContext.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/TranslationContext.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/TranslationContext.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,29 +13,9 @@
##############################################################################
"""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.
+BBB 2005/05/01 -- to be removed after 12 months
$Id$
"""
-
-DEFAULT_DOMAIN = "default"
-
-class TranslationContext:
- """Information about the I18N settings of a TAL processor."""
-
- def __init__(self, parent=None, domain=None, target=None, source=None):
- if parent:
- if not domain:
- domain = parent.domain
- if not target:
- target = parent.target
- if not source:
- source = parent.source
- elif domain is None:
- domain = DEFAULT_DOMAIN
-
- self.parent = parent
- self.domain = domain
- self.target = target
- self.source = source
+import zope.deprecation
+zope.deprecation.moved('zope.tal.translationcontext', '2.12')
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/XMLParser.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/XMLParser.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/XMLParser.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -13,75 +13,11 @@
##############################################################################
"""
Generic expat-based XML parser base class.
+
+BBB 2005/05/01 -- to be removed after 12 months
"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.xmlparser', '2.12')
import xml.parsers.expat
-from logging import getLogger
-
-
-LOG = getLogger('TAL')
-
XMLParseError = xml.parsers.expat.ExpatError
-
-
-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:
- LOG.info("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:
- LOG.error("Can't set expat handler %s" % name)
-
- def createParser(self, encoding=None):
- return xml.parsers.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)
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/driver.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/driver.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/driver.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -34,165 +34,11 @@
Leave TAL/METAL attributes in output
-i
Leave I18N substitution strings un-interpolated.
+
+BBB 2005/05/01 -- to be removed after 12 months
"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.driver', '2.12')
-import os
-import sys
-
-import getopt
-
if __name__ == "__main__":
- import setpath # Local hack to tweak sys.path etc.
-
-# Import local classes
-import TALDefs
-from DummyEngine import DummyEngine
-from DummyEngine import DummyTranslationService
-
-FILE = "tests/input/test01.xml"
-
-class TestTranslations(DummyTranslationService):
- def translate(self, domain, msgid, mapping=None, context=None,
- target_language=None, default=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 at dom.ain'
- elif msgid == 'origin':
- return '%(name)s was born in %(country)s' % mapping
- return DummyTranslationService.translate(self, domain, msgid,
- mapping, context,
- target_language,
- default=default)
-
-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 at 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 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 HTMLTALParser import HTMLTALParser
- p = HTMLTALParser()
- else:
- from TALParser import TALParser
- p = TALParser()
- p.parseFile(file)
- return p.getCode()
-
-def showit(it):
- from pprint import pprint
- pprint(it)
-
-if __name__ == "__main__":
main()
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/ndiff.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/ndiff.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/ndiff.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -1,625 +1,8 @@
#! /usr/bin/env python
+# BBB 2005/05/01 -- to be removed after 12 months
+import zope.deprecation
+zope.deprecation.moved('zope.tal.ndiff', '2.12')
-# Module ndiff version 1.6.0
-# Released to the public domain 08-Dec-2000,
-# by Tim Peters (tim.one at 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
-"+ " lines; use ndiff with -r2; or, on Unix, the second file can be
-recovered by piping the output through
-
- sed -n '/^[+ ] /s/^..//p'
-
-See module comments for details and programmatic interface.
-"""
-
-__version__ = 1, 5, 0
-
-# SequenceMatcher tries to compute a "human-friendly diff" between
-# two sequences (chiefly picturing a file as a sequence of lines,
-# and a line as a sequence of characters, here). Unlike e.g. UNIX(tm)
-# diff, the fundamental notion is the longest *contiguous* & junk-free
-# matching subsequence. That's what catches peoples' eyes. The
-# Windows(tm) windiff has another interesting notion, pairing up elements
-# that appear uniquely in each sequence. That, and the method here,
-# appear to yield more intuitive difference reports than does diff. This
-# method appears to be the least vulnerable to synching up on blocks
-# of "junk lines", though (like blank lines in ordinary text files,
-# or maybe "<P>" lines in HTML files). That may be because this is
-# the only method of the 3 that has a *concept* of "junk" <wink>.
-#
-# Note that ndiff makes no claim to produce a *minimal* diff. To the
-# contrary, minimal diffs are often counter-intuitive, because they
-# synch up anywhere possible, sometimes accidental matches 100 pages
-# apart. Restricting synch points to contiguous matches preserves some
-# notion of locality, at the occasional cost of producing a longer diff.
-#
-# With respect to junk, an earlier version of ndiff simply refused to
-# *start* a match with a junk element. The result was cases like this:
-# before: private Thread currentThread;
-# after: private volatile Thread currentThread;
-# If you consider whitespace to be junk, the longest contiguous match
-# not starting with junk is "e Thread currentThread". So ndiff reported
-# that "e volatil" was inserted between the 't' and the 'e' in "private".
-# While an accurate view, to people that's absurd. The current version
-# looks for matching blocks that are entirely junk-free, then extends the
-# longest one of those as far as possible but only with matching junk.
-# So now "currentThread" is matched, then extended to suck up the
-# preceding blank; then "private" is matched, and extended to suck up the
-# following blank; then "Thread" is matched; and finally ndiff reports
-# that "volatile " was inserted before "Thread". The only quibble
-# remaining is that perhaps it was really the case that " volatile"
-# was inserted after "private". I can live with that <wink>.
-#
-# NOTE on junk: the module-level names
-# IS_LINE_JUNK
-# IS_CHARACTER_JUNK
-# can be set to any functions you like. The first one should accept
-# a single string argument, and return true iff the string is junk.
-# The default is whether the regexp r"\s*#?\s*$" matches (i.e., a
-# line without visible characters, except for at most one splat).
-# The second should accept a string of length 1 etc. The default is
-# whether the character is a blank or tab (note: bad idea to include
-# newline in this!).
-#
-# After setting those, you can call fcompare(f1name, f2name) with the
-# names of the files you want to compare. The difference report
-# is sent to stdout. Or you can call main(args), passing what would
-# have been in sys.argv[1:] had the cmd-line form been used.
-
-TRACE = 0
-
-# define what "junk" means
-import re
-
-def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match):
- return pat(line) is not None
-
-def IS_CHARACTER_JUNK(ch, ws=" \t"):
- return ch in ws
-
-del re
-
-class SequenceMatcher:
- def __init__(self, isjunk=None, a='', b=''):
- # Members:
- # a
- # first sequence
- # b
- # second sequence; differences are computed as "what do
- # we need to do to 'a' to change it into 'b'?"
- # b2j
- # for x in b, b2j[x] is a list of the indices (into b)
- # at which x appears; junk elements do not appear
- # b2jhas
- # b2j.has_key
- # fullbcount
- # for x in b, fullbcount[x] == the number of times x
- # appears in b; only materialized if really needed (used
- # only for computing quick_ratio())
- # matching_blocks
- # a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k];
- # ascending & non-overlapping in i and in j; terminated by
- # a dummy (len(a), len(b), 0) sentinel
- # opcodes
- # a list of (tag, i1, i2, j1, j2) tuples, where tag is
- # one of
- # 'replace' a[i1:i2] should be replaced by b[j1:j2]
- # 'delete' a[i1:i2] should be deleted
- # 'insert' b[j1:j2] should be inserted
- # 'equal' a[i1:i2] == b[j1:j2]
- # isjunk
- # a user-supplied function taking a sequence element and
- # returning true iff the element is "junk" -- this has
- # subtle but helpful effects on the algorithm, which I'll
- # get around to writing up someday <0.9 wink>.
- # DON'T USE! Only __chain_b uses this. Use isbjunk.
- # isbjunk
- # for x in b, isbjunk(x) == isjunk(x) but much faster;
- # it's really the has_key method of a hidden dict.
- # DOES NOT WORK for x in a!
-
- self.isjunk = isjunk
- self.a = self.b = None
- self.set_seqs(a, b)
-
- def set_seqs(self, a, b):
- self.set_seq1(a)
- self.set_seq2(b)
-
- def set_seq1(self, a):
- if a is self.a:
- return
- self.a = a
- self.matching_blocks = self.opcodes = None
-
- def set_seq2(self, b):
- if b is self.b:
- return
- self.b = b
- self.matching_blocks = self.opcodes = None
- self.fullbcount = None
- self.__chain_b()
-
- # For each element x in b, set b2j[x] to a list of the indices in
- # b where x appears; the indices are in increasing order; note that
- # the number of times x appears in b is len(b2j[x]) ...
- # when self.isjunk is defined, junk elements don't show up in this
- # map at all, which stops the central find_longest_match method
- # from starting any matching block at a junk element ...
- # also creates the fast isbjunk function ...
- # note that this is only called when b changes; so for cross-product
- # kinds of matches, it's best to call set_seq2 once, then set_seq1
- # repeatedly
-
- def __chain_b(self):
- # Because isjunk is a user-defined (not C) function, and we test
- # for junk a LOT, it's important to minimize the number of calls.
- # Before the tricks described here, __chain_b was by far the most
- # time-consuming routine in the whole module! If anyone sees
- # Jim Roskind, thank him again for profile.py -- I never would
- # have guessed that.
- # The first trick is to build b2j ignoring the possibility
- # of junk. I.e., we don't call isjunk at all yet. Throwing
- # out the junk later is much cheaper than building b2j "right"
- # from the start.
- b = self.b
- self.b2j = b2j = {}
- self.b2jhas = b2jhas = b2j.has_key
- for i in xrange(len(b)):
- elt = b[i]
- if b2jhas(elt):
- b2j[elt].append(i)
- else:
- b2j[elt] = [i]
-
- # Now b2j.keys() contains elements uniquely, and especially when
- # the sequence is a string, that's usually a good deal smaller
- # than len(string). The difference is the number of isjunk calls
- # saved.
- isjunk, junkdict = self.isjunk, {}
- if isjunk:
- for elt in b2j.keys():
- if isjunk(elt):
- junkdict[elt] = 1 # value irrelevant; it's a set
- del b2j[elt]
-
- # Now for x in b, isjunk(x) == junkdict.has_key(x), but the
- # latter is much faster. Note too that while there may be a
- # lot of junk in the sequence, the number of *unique* junk
- # elements is probably small. So the memory burden of keeping
- # this dict alive is likely trivial compared to the size of b2j.
- self.isbjunk = junkdict.has_key
-
- def find_longest_match(self, alo, ahi, blo, bhi):
- """Find longest matching block in a[alo:ahi] and b[blo:bhi].
-
- If isjunk is not defined:
-
- Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
- alo <= i <= i+k <= ahi
- blo <= j <= j+k <= bhi
- and for all (i',j',k') meeting those conditions,
- k >= k'
- i <= i'
- and if i == i', j <= j'
- In other words, of all maximal matching blocks, return one
- that starts earliest in a, and of all those maximal matching
- blocks that start earliest in a, return the one that starts
- earliest in b.
-
- If isjunk is defined, first the longest matching block is
- determined as above, but with the additional restriction that
- no junk element appears in the block. Then that block is
- extended as far as possible by matching (only) junk elements on
- both sides. So the resulting block never matches on junk except
- as identical junk happens to be adjacent to an "interesting"
- match.
-
- If no blocks match, return (alo, blo, 0).
- """
-
- # CAUTION: stripping common prefix or suffix would be incorrect.
- # E.g.,
- # ab
- # acab
- # Longest matching block is "ab", but if common prefix is
- # stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
- # strip, so ends up claiming that ab is changed to acab by
- # inserting "ca" in the middle. That's minimal but unintuitive:
- # "it's obvious" that someone inserted "ac" at the front.
- # Windiff ends up at the same place as diff, but by pairing up
- # the unique 'b's and then matching the first two 'a's.
-
- a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.isbjunk
- besti, bestj, bestsize = alo, blo, 0
- # find longest junk-free match
- # during an iteration of the loop, j2len[j] = length of longest
- # junk-free match ending with a[i-1] and b[j]
- j2len = {}
- nothing = []
- for i in xrange(alo, ahi):
- # look at all instances of a[i] in b; note that because
- # b2j has no junk keys, the loop is skipped if a[i] is junk
- j2lenget = j2len.get
- newj2len = {}
- for j in b2j.get(a[i], nothing):
- # a[i] matches b[j]
- if j < blo:
- continue
- if j >= bhi:
- break
- k = newj2len[j] = j2lenget(j-1, 0) + 1
- if k > bestsize:
- besti, bestj, bestsize = i-k+1, j-k+1, k
- j2len = newj2len
-
- # Now that we have a wholly interesting match (albeit possibly
- # empty!), we may as well suck up the matching junk on each
- # side of it too. Can't think of a good reason not to, and it
- # saves post-processing the (possibly considerable) expense of
- # figuring out what to do with it. In the case of an empty
- # interesting match, this is clearly the right thing to do,
- # because no other kind of match is possible in the regions.
- while besti > alo and bestj > blo and \
- isbjunk(b[bestj-1]) and \
- a[besti-1] == b[bestj-1]:
- besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
- while besti+bestsize < ahi and bestj+bestsize < bhi and \
- isbjunk(b[bestj+bestsize]) and \
- a[besti+bestsize] == b[bestj+bestsize]:
- bestsize = bestsize + 1
-
- if TRACE:
- print "get_matching_blocks", alo, ahi, blo, bhi
- print " returns", besti, bestj, bestsize
- return besti, bestj, bestsize
-
- def get_matching_blocks(self):
- if self.matching_blocks is not None:
- return self.matching_blocks
- self.matching_blocks = []
- la, lb = len(self.a), len(self.b)
- self.__helper(0, la, 0, lb, self.matching_blocks)
- self.matching_blocks.append( (la, lb, 0) )
- if TRACE:
- print '*** matching blocks', self.matching_blocks
- return self.matching_blocks
-
- # builds list of matching blocks covering a[alo:ahi] and
- # b[blo:bhi], appending them in increasing order to answer
-
- def __helper(self, alo, ahi, blo, bhi, answer):
- i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)
- # a[alo:i] vs b[blo:j] unknown
- # a[i:i+k] same as b[j:j+k]
- # a[i+k:ahi] vs b[j+k:bhi] unknown
- if k:
- if alo < i and blo < j:
- self.__helper(alo, i, blo, j, answer)
- answer.append(x)
- if i+k < ahi and j+k < bhi:
- self.__helper(i+k, ahi, j+k, bhi, answer)
-
- def ratio(self):
- """Return a measure of the sequences' similarity (float in [0,1]).
-
- Where T is the total number of elements in both sequences, and
- M is the number of matches, this is 2*M / T.
- Note that this is 1 if the sequences are identical, and 0 if
- they have nothing in common.
- """
-
- matches = reduce(lambda sum, triple: sum + triple[-1],
- self.get_matching_blocks(), 0)
- return 2.0 * matches / (len(self.a) + len(self.b))
-
- def quick_ratio(self):
- """Return an upper bound on ratio() relatively quickly."""
- # viewing a and b as multisets, set matches to the cardinality
- # of their intersection; this counts the number of matches
- # without regard to order, so is clearly an upper bound
- if self.fullbcount is None:
- self.fullbcount = fullbcount = {}
- for elt in self.b:
- fullbcount[elt] = fullbcount.get(elt, 0) + 1
- fullbcount = self.fullbcount
- # avail[x] is the number of times x appears in 'b' less the
- # number of times we've seen it in 'a' so far ... kinda
- avail = {}
- availhas, matches = avail.has_key, 0
- for elt in self.a:
- if availhas(elt):
- numb = avail[elt]
- else:
- numb = fullbcount.get(elt, 0)
- avail[elt] = numb - 1
- if numb > 0:
- matches = matches + 1
- return 2.0 * matches / (len(self.a) + len(self.b))
-
- def real_quick_ratio(self):
- """Return an upper bound on ratio() very quickly"""
- la, lb = len(self.a), len(self.b)
- # can't have more matches than the number of elements in the
- # shorter sequence
- return 2.0 * min(la, lb) / (la + lb)
-
- def get_opcodes(self):
- if self.opcodes is not None:
- return self.opcodes
- i = j = 0
- self.opcodes = answer = []
- for ai, bj, size in self.get_matching_blocks():
- # invariant: we've pumped out correct diffs to change
- # a[:i] into b[:j], and the next matching block is
- # a[ai:ai+size] == b[bj:bj+size]. So we need to pump
- # out a diff to change a[i:ai] into b[j:bj], pump out
- # the matching block, and move (i,j) beyond the match
- tag = ''
- if i < ai and j < bj:
- tag = 'replace'
- elif i < ai:
- tag = 'delete'
- elif j < bj:
- tag = 'insert'
- if tag:
- answer.append( (tag, i, ai, j, bj) )
- i, j = ai+size, bj+size
- # the list of matching blocks is terminated by a
- # sentinel with size 0
- if size:
- answer.append( ('equal', ai, i, bj, j) )
- return answer
-
-# meant for dumping lines
-def dump(tag, x, lo, hi):
- for i in xrange(lo, hi):
- print tag, x[i],
-
-def plain_replace(a, alo, ahi, b, blo, bhi):
- assert alo < ahi and blo < bhi
- # dump the shorter block first -- reduces the burden on short-term
- # memory if the blocks are of very different sizes
- if bhi - blo < ahi - alo:
- dump('+', b, blo, bhi)
- dump('-', a, alo, ahi)
- else:
- dump('-', a, alo, ahi)
- dump('+', b, blo, bhi)
-
-# When replacing one block of lines with another, this guy searches
-# the blocks for *similar* lines; the best-matching pair (if any) is
-# used as a synch point, and intraline difference marking is done on
-# the similar pair. Lots of work, but often worth it.
-
-def fancy_replace(a, alo, ahi, b, blo, bhi):
- if TRACE:
- print '*** fancy_replace', alo, ahi, blo, bhi
- dump('>', a, alo, ahi)
- dump('<', b, blo, bhi)
-
- # don't synch up unless the lines have a similarity score of at
- # least cutoff; best_ratio tracks the best score seen so far
- best_ratio, cutoff = 0.74, 0.75
- cruncher = SequenceMatcher(IS_CHARACTER_JUNK)
- eqi, eqj = None, None # 1st indices of equal lines (if any)
-
- # search for the pair that matches best without being identical
- # (identical lines must be junk lines, & we don't want to synch up
- # on junk -- unless we have to)
- for j in xrange(blo, bhi):
- bj = b[j]
- cruncher.set_seq2(bj)
- for i in xrange(alo, ahi):
- ai = a[i]
- if ai == bj:
- if eqi is None:
- eqi, eqj = i, j
- continue
- cruncher.set_seq1(ai)
- # computing similarity is expensive, so use the quick
- # upper bounds first -- have seen this speed up messy
- # compares by a factor of 3.
- # note that ratio() is only expensive to compute the first
- # time it's called on a sequence pair; the expensive part
- # of the computation is cached by cruncher
- if cruncher.real_quick_ratio() > best_ratio and \
- cruncher.quick_ratio() > best_ratio and \
- cruncher.ratio() > best_ratio:
- best_ratio, best_i, best_j = cruncher.ratio(), i, j
- if best_ratio < cutoff:
- # no non-identical "pretty close" pair
- if eqi is None:
- # no identical pair either -- treat it as a straight replace
- plain_replace(a, alo, ahi, b, blo, bhi)
- return
- # no close pair, but an identical pair -- synch up on that
- best_i, best_j, best_ratio = eqi, eqj, 1.0
- else:
- # there's a close pair, so forget the identical pair (if any)
- eqi = None
-
- # a[best_i] very similar to b[best_j]; eqi is None iff they're not
- # identical
- if TRACE:
- print '*** best_ratio', best_ratio, best_i, best_j
- dump('>', a, best_i, best_i+1)
- dump('<', b, best_j, best_j+1)
-
- # pump out diffs from before the synch point
- fancy_helper(a, alo, best_i, b, blo, best_j)
-
- # do intraline marking on the synch pair
- aelt, belt = a[best_i], b[best_j]
- if eqi is None:
- # pump out a '-', '?', '+', '?' quad for the synched lines
- atags = btags = ""
- cruncher.set_seqs(aelt, belt)
- for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
- la, lb = ai2 - ai1, bj2 - bj1
- if tag == 'replace':
- atags = atags + '^' * la
- btags = btags + '^' * lb
- elif tag == 'delete':
- atags = atags + '-' * la
- elif tag == 'insert':
- btags = btags + '+' * lb
- elif tag == 'equal':
- atags = atags + ' ' * la
- btags = btags + ' ' * lb
- else:
- raise ValueError, 'unknown tag ' + `tag`
- printq(aelt, belt, atags, btags)
- else:
- # the synch pair is identical
- print ' ', aelt,
-
- # pump out diffs from after the synch point
- fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
-
-def fancy_helper(a, alo, ahi, b, blo, bhi):
- if alo < ahi:
- if blo < bhi:
- fancy_replace(a, alo, ahi, b, blo, bhi)
- else:
- dump('-', a, alo, ahi)
- elif blo < bhi:
- dump('+', b, blo, bhi)
-
-# Crap to deal with leading tabs in "?" output. Can hurt, but will
-# probably help most of the time.
-
-def printq(aline, bline, atags, btags):
- common = min(count_leading(aline, "\t"),
- count_leading(bline, "\t"))
- common = min(common, count_leading(atags[:common], " "))
- print "-", aline,
- if count_leading(atags, " ") < len(atags):
- print "?", "\t" * common + atags[common:]
- print "+", bline,
- if count_leading(btags, " ") < len(btags):
- print "?", "\t" * common + btags[common:]
-
-def count_leading(line, ch):
- i, n = 0, len(line)
- while i < n and line[i] == ch:
- i = i + 1
- return i
-
-def fail(msg):
- import sys
- out = sys.stderr.write
- out(msg + "\n\n")
- out(__doc__)
- return 0
-
-# open a file & return the file object; gripe and return 0 if it
-# couldn't be opened
-def fopen(fname):
- try:
- return open(fname, 'r')
- except IOError, detail:
- return fail("couldn't open " + fname + ": " + str(detail))
-
-# open two files & spray the diff to stdout; return false iff a problem
-def fcompare(f1name, f2name):
- f1 = fopen(f1name)
- f2 = fopen(f2name)
- if not f1 or not f2:
- return 0
-
- a = f1.readlines(); f1.close()
- b = f2.readlines(); f2.close()
-
- cruncher = SequenceMatcher(IS_LINE_JUNK, a, b)
- for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
- if tag == 'replace':
- fancy_replace(a, alo, ahi, b, blo, bhi)
- elif tag == 'delete':
- dump('-', a, alo, ahi)
- elif tag == 'insert':
- dump('+', b, blo, bhi)
- elif tag == 'equal':
- dump(' ', a, alo, ahi)
- else:
- raise ValueError, 'unknown tag ' + `tag`
-
- return 1
-
-# crack args (sys.argv[1:] is normal) & compare;
-# return false iff a problem
-
-def main(args):
- import getopt
- 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:]
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/runtest.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/runtest.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/runtest.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -14,138 +14,11 @@
##############################################################################
"""
Driver program to run METAL and TAL regression tests.
+
+BBB 2005/05/01 -- to be removed after 12 months
"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.runtest', '2.12')
-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 driver
-import 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()
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/talgettext.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/talgettext.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/talgettext.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -1,318 +1,6 @@
+# BBB 2005/05/01 -- to be removed after 12 months
+import zope.deprecation
+zope.deprecation.moved('zope.tal.talgettext', '2.12')
-
-
-#!/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.1 (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.
- -o / --output <file>
- Output the translation .po file to <file>.
- -u / --update <file>
- Update the existing translation <file> with any new translation strings
- found.
-"""
-
-import sys
-import time
-import getopt
-import traceback
-
-from TAL.HTMLTALParser import HTMLTALParser
-from TAL.TALInterpreter import TALInterpreter
-from TAL.DummyEngine import DummyEngine
-from ITALES import ITALESEngine
-from TAL.TALDefs import TALESError
-
-__version__ = '$Revision: 1.1.2.1 $'
-
-pot_header = '''\
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR ORGANIZATION
-# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\\n"
-"POT-Creation-Date: %(time)s\\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
-"Last-Translator: FULL NAME <EMAIL at ADDRESS>\\n"
-"Language-Team: LANGUAGE <LL at li.org>\\n"
-"MIME-Version: 1.0\\n"
-"Content-Type: text/plain; charset=CHARSET\\n"
-"Content-Transfer-Encoding: ENCODING\\n"
-"Generated-By: talgettext.py %(version)s\\n"
-'''
-
-NLSTR = '"\n"'
-
-try:
- True
-except NameError:
- True=1
- False=0
-
-def usage(code, msg=''):
- # Python 2.1 required
- print >> sys.stderr, __doc__
- if msg:
- print >> sys.stderr, msg
- sys.exit(code)
-
-
-class POTALInterpreter(TALInterpreter):
- def translate(self, msgid, default, i18ndict=None, obj=None):
- # XXX is this right?
- if i18ndict is None:
- i18ndict = {}
- if obj:
- i18ndict.update(obj)
- # XXX Mmmh, it seems that sometimes the msgid is None; is that really
- # possible?
- if msgid is None:
- return None
- # XXX We need to pass in one of context or target_language
- return self.engine.translate(msgid, self.i18nContext.domain, i18ndict,
- position=self.position, default=default)
-
-
-class POEngine(DummyEngine):
- __implements__ = ITALESEngine
-
- def __init__(self, macros=None):
- self.catalog = {}
- DummyEngine.__init__(self, macros)
-
- def evaluate(*args):
- return '' # who cares
-
- def evaluatePathOrVar(*args):
- return '' # who cares
-
- def evaluateSequence(self, expr):
- return (0,) # dummy
-
- def evaluateBoolean(self, expr):
- return True # dummy
-
- def translate(self, msgid, domain=None, mapping=None, default=None,
- # XXX position is not part of the ITALESEngine
- # interface
- position=None):
-
- if domain not in self.catalog:
- self.catalog[domain] = {}
- domain = self.catalog[domain]
-
- # ---------------------------------------------
- # only non-empty msgids are added to dictionary
- # (changed by heinrichbernd - 2004/09/07)
- # ---------------------------------------------
- if msgid:
- if msgid not in domain:
- domain[msgid] = []
- domain[msgid].append((self.file, position))
- return 'x'
-
-
-class UpdatePOEngine(POEngine):
- """A slightly-less braindead POEngine which supports loading an existing
- .po file first."""
-
- def __init__ (self, macros=None, filename=None):
- POEngine.__init__(self, macros)
-
- self._filename = filename
- self._loadFile()
- self.base = self.catalog
- self.catalog = {}
-
- def __add(self, id, s, fuzzy):
- "Add a non-fuzzy translation to the dictionary."
- if not fuzzy and str:
- # check for multi-line values and munge them appropriately
- if '\n' in s:
- lines = s.rstrip().split('\n')
- s = NLSTR.join(lines)
- self.catalog[id] = s
-
- def _loadFile(self):
- # shamelessly cribbed from Python's Tools/i18n/msgfmt.py
- # 25-Mar-2003 Nathan R. Yergler (nathan at zope.org)
- # 14-Apr-2003 Hacked by Barry Warsaw (barry at zope.com)
-
- ID = 1
- STR = 2
-
- try:
- lines = open(self._filename).readlines()
- except IOError, msg:
- print >> sys.stderr, msg
- sys.exit(1)
-
- section = None
- fuzzy = False
-
- # Parse the catalog
- lno = 0
- for l in lines:
- lno += True
- # If we get a comment line after a msgstr, this is a new entry
- if l[0] == '#' and section == STR:
- self.__add(msgid, msgstr, fuzzy)
- section = None
- fuzzy = False
- # Record a fuzzy mark
- if l[:2] == '#,' and l.find('fuzzy'):
- fuzzy = True
- # Skip comments
- if l[0] == '#':
- continue
- # Now we are in a msgid section, output previous section
- if l.startswith('msgid'):
- if section == STR:
- self.__add(msgid, msgstr, fuzzy)
- section = ID
- l = l[5:]
- msgid = msgstr = ''
- # Now we are in a msgstr section
- elif l.startswith('msgstr'):
- section = STR
- l = l[6:]
- # Skip empty lines
- if not l.strip():
- continue
- # XXX: Does this always follow Python escape semantics?
- l = eval(l)
- if section == ID:
- msgid += l
- elif section == STR:
- msgstr += '%s\n' % l
- else:
- print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
- 'before:'
- print >> sys.stderr, l
- sys.exit(1)
- # Add last entry
- if section == STR:
- self.__add(msgid, msgstr, fuzzy)
-
- def evaluate(self, expression):
- try:
- return POEngine.evaluate(self, expression)
- except TALESError:
- pass
-
- def evaluatePathOrVar(self, expr):
- return 'who cares'
-
- def translate(self, msgid, domain=None, mapping=None, default=None,
- position=None):
- if msgid not in self.base:
- POEngine.translate(self, msgid, domain, mapping, default, position)
- return 'x'
-
-
-def main():
- try:
- opts, args = getopt.getopt(
- sys.argv[1:],
- 'ho:u:',
- ['help', 'output=', 'update='])
- except getopt.error, msg:
- usage(1, msg)
-
- outfile = None
- engine = None
- update_mode = False
- for opt, arg in opts:
- if opt in ('-h', '--help'):
- usage(0)
- elif opt in ('-o', '--output'):
- outfile = arg
- elif opt in ('-u', '--update'):
- update_mode = True
- if outfile is None:
- outfile = arg
- engine = UpdatePOEngine(filename=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
-
- # check if we've already instantiated an engine;
- # if not, use the stupidest one available
- if not engine:
- engine = POEngine()
-
- # process each file specified
- for filename in args:
- try:
- engine.file = filename
- p = HTMLTALParser()
- p.parseFile(filename)
- program, macros = p.getCode()
- POTALInterpreter(program, macros, engine, stream=Devnull(),
- metal=False)()
- except: # Hee hee, I love bare excepts!
- print 'There was an error processing', filename
- traceback.print_exc()
-
- # Now output the keys in the engine. Write them to a file if --output or
- # --update was specified; otherwise use standard out.
- if (outfile is None):
- outfile = sys.stdout
- else:
- outfile = file(outfile, update_mode and "a" or "w")
- catalog = {}
- for domain in engine.catalog.keys():
- catalog.update(engine.catalog[domain])
-
- messages = catalog.copy()
- try:
- messages.update(engine.base)
- except AttributeError:
- pass
- if '' not in messages:
- print >> outfile, pot_header % {'time': time.ctime(),
- 'version': __version__}
-
- msgids = catalog.keys()
- # XXX: You should not sort by msgid, but by filename and position. (SR)
- msgids.sort()
- for msgid in msgids:
- positions = catalog[msgid]
- for filename, position in positions:
- outfile.write('#: %s:%s\n' % (filename, position[0]))
-
- outfile.write('msgid "%s"\n' % msgid)
- outfile.write('msgstr ""\n')
- outfile.write('\n')
-
-
if __name__ == '__main__':
main()
Modified: Zope/branches/ajung-zpt-end-game/lib/python/TAL/timer.py
===================================================================
--- Zope/branches/ajung-zpt-end-game/lib/python/TAL/timer.py 2006-05-28 22:14:02 UTC (rev 68316)
+++ Zope/branches/ajung-zpt-end-game/lib/python/TAL/timer.py 2006-05-28 23:07:07 UTC (rev 68317)
@@ -14,44 +14,11 @@
##############################################################################
"""
Helper program to time compilation and interpretation
+
+BBB 2005/05/01 -- to be removed after 12 months
"""
+import zope.deprecation
+zope.deprecation.moved('zope.tal.timer', '2.12')
-import sys
-import time
-import getopt
-from cPickle import dumps, loads
-from cStringIO import StringIO
-
-from 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 = 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()
More information about the Zope-Checkins
mailing list