[Zope-Checkins] CVS: Zope3/lib/python/Zope/PageTemplate - TALES.py:1.1.2.9
Shane Hathaway
shane@cvs.zope.org
Wed, 13 Mar 2002 22:43:18 -0500
Update of /cvs-repository/Zope3/lib/python/Zope/PageTemplate
In directory cvs.zope.org:/tmp/cvs-serv7754
Modified Files:
Tag: Zope-3x-branch
TALES.py
Log Message:
- Made it so TALESErrors don't mask another exception, which is unfortunately
brittle.
- Provided a registry of base names, such as "module", which are available
in all expressions.
- Added setSourceFile() method to the generic TALES engine.
- Replaced getTALESError() in the TALES engine with createErrorInfo().
"tal:on-error" handlers use ErrorInfo instances.
- Added __traceback_supplement__, which doesn't do anything yet, but will
soon provide the detailed exception information that TALESErrors tried to
provide.
=== Zope3/lib/python/Zope/PageTemplate/TALES.py 1.1.2.8 => 1.1.2.9 ===
import re, sys
+from types import StringType
+
import Zope.ZTUtils
+from SafeMapping import SafeMapping
-from types import StringType
NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
_parse_expr = re.compile(r"(%s):" % NAME_RE).match
_valid_name = re.compile('%s$' % NAME_RE).match
+
class TALESError(Exception):
- __allow_access_to_unprotected_subobjects__ = 1
- def __init__(self, expression, info=(None, None, None),
- position=(None, None)):
- self.type, self.value, self.traceback = info
- self.expression = expression
- self.setPosition(position)
- def setPosition(self, position):
- self.lineno = position[0]
- self.offset = position[1]
- def takeTraceback(self):
- t = self.traceback
- self.traceback = None
- return t
- def __str__(self):
- if self.type is None:
- s = self.expression
- else:
- s = '%s: %s in %s' % (self.type, self.value,
- `self.expression`)
- if self.lineno is not None:
- s = "%s, at line %d" % (s, self.lineno)
- if self.offset is not None:
- s = "%s, column %d" % (s, self.offset + 1)
- return s
- def __nonzero__(self):
- return 0
+ """Error during TALES evaluation"""
class Undefined(TALESError):
'''Exception raised on traversal of an undefined path'''
-class RegistrationError(Exception):
- '''TALES Type Registration Error'''
-
class CompilerError(Exception):
'''TALES Compiler Error'''
+class RegistrationError(Exception):
+ '''Expression type or base name registration Error'''
+
+
class Default:
'''Retain Default'''
def __nonzero__(self):
return 0
-Default = Default()
+default_ = Default()
-_marker = []
+_marker = object()
-from SafeMapping import SafeMapping
class Iterator(Zope.ZTUtils.Iterator):
def __init__(self, name, seq, context):
- Zope.ZTUtils.Iterator.__init__(self, seq) # :
+ Zope.ZTUtils.Iterator.__init__(self, seq)
self.name = name
self._context = context
@@ -80,19 +58,28 @@
return self
def next(self):
- try:
- if Zope.ZTUtils.Iterator.next(self):
- self._context.setLocal(self.name, self.seq[self.index])
- return 1
- except TALESError:
- raise
- except:
- raise TALESError, ('repeat/%s' % self.name,
- sys.exc_info()), sys.exc_info()[2]
+ if Zope.ZTUtils.Iterator.next(self):
+ self._context.setLocal(self.name, self.seq[self.index])
+ return 1
return 0
-class Engine:
+
+class ErrorInfo:
+ """Information about an exception passed to an on-error handler."""
+ 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]
+
+
+
+class ExpressionEngine:
'''Expression Engine
An instance of this class keeps a mutable collection of expression
@@ -100,16 +87,15 @@
these handlers. It can provide an expression Context, which is
capable of holding state and evaluating compiled expressions.
'''
- Iterator = Iterator
-
- def __init__(self, Iterator=None):
+ def __init__(self):
self.types = {}
- if Iterator is not None:
- self.Iterator = Iterator
+ self.base_names = {}
+ self.iteratorFactory = Iterator
def registerType(self, name, handler):
if not _valid_name(name):
- raise RegistrationError, 'Invalid Expression type "%s".' % name
+ raise RegistrationError, (
+ 'Invalid expression type name "%s".' % name)
types = self.types
if types.has_key(name):
raise RegistrationError, (
@@ -120,6 +106,18 @@
def getTypes(self):
return self.types
+ def registerBaseName(self, name, object):
+ if not _valid_name(name):
+ raise RegistrationError, 'Invalid base name "%s".' % name
+ base_names = self.base_names
+ if base_names.has_key(name):
+ raise RegistrationError, (
+ 'Multiple registrations for base name "%s".' % name)
+ base_names[name] = object
+
+ def getBaseNames(self):
+ return self.base_names
+
def compile(self, expression):
m = _parse_expr(expression)
if m:
@@ -146,6 +144,7 @@
def getCompilerError(self):
return CompilerError
+
class Context:
'''Expression Context
@@ -154,16 +153,14 @@
'''
_context_class = SafeMapping
- _nocatch = TALESError
position = (None, None)
+ source_file = None
def __init__(self, engine, contexts):
self._engine = engine
- if hasattr(engine, '_nocatch'):
- self._nocatch = engine._nocatch
self.contexts = contexts
contexts['nothing'] = None
- contexts['default'] = Default
+ contexts['default'] = default_
self.repeat_vars = rv = {}
# Wrap this, as it is visible to restricted code
@@ -205,8 +202,8 @@
def setRepeat(self, name, expr):
expr = self.evaluate(expr)
if not expr:
- return self._engine.Iterator(name, (), self)
- it = self._engine.Iterator(name, expr, self)
+ return self._engine.iteratorFactory(name, (), self)
+ it = self._engine.iteratorFactory(name, expr, self)
old_value = self.repeat_vars.get(name)
self._scope_stack[-1].append((name, old_value))
self.repeat_vars[name] = it
@@ -216,22 +213,10 @@
isinstance=isinstance, StringType=StringType):
if isinstance(expression, StringType):
expression = self._engine.compile(expression)
- try:
- v = expression(self)
- if isinstance(v, Exception):
- if isinstance(v, TALESError):
- raise v, None, v.takeTraceback()
- raise v
- except TALESError, err:
- err.setPosition(self.position)
- raise err, None, sys.exc_info()[2]
- except self._nocatch:
- raise
- except:
- raise TALESError, (`expression`, sys.exc_info(),
- self.position), sys.exc_info()[2]
- else:
- return v
+ __traceback_supplement__ = (
+ TALESTracebackSupplement, self, expression)
+ v = expression(self)
+ return v
evaluateValue = evaluate
@@ -240,7 +225,7 @@
def evaluateText(self, expr, None=None):
text = self.evaluate(expr)
- if text is Default or text is None:
+ if text is default_ or text is None:
return text
return str(text)
@@ -253,14 +238,54 @@
return self.evaluate(expr)
evaluateMacro = evaluate
- def getTALESError(self):
- return TALESError
+ def createErrorInfo(self, err, position):
+ return ErrorInfo(err, position)
def getDefault(self):
- return Default
+ return default_
+
+ def setSourceFile(self, source_file):
+ self.source_file = source_file
def setPosition(self, position):
self.position = position
+
+
+class TALESTracebackSupplement:
+ """Implementation of Zope.Exceptions.ITracebackSupplement"""
+ def __init__(self, context, expression):
+ self.context = context
+ self.expression = repr(expression)
+ self.line = context.position[0]
+ self.column = context.position[1]
+
+## source_file = context.source_file
+## if (isinstance(source_file, StringType) and
+## source_file.startswith('traversal:')):
+## p = source_file[10:]
+## # XXX There should be a better way to find the Zope app root.
+## root = self.context.contexts.get('root', None)
+## if root is not None:
+## try:
+## object = root.unrestrictedTraverse(p)
+## except:
+## # Hmm, couldn't find the script??
+## pass
+## else:
+## self.manageable_object = object
+
+ def getInfo(self, as_html=0):
+ import pprint
+ data = self.context.contexts.copy()
+ s = pprint.pformat(data)
+ if not as_html:
+ return ' - Names:\n %s' % s.replace('\n', '\n ')
+ else:
+ from cgi import escape
+ return '<b>Names:</b><pre>%s</pre>' % (escape(s))
+ return None
+
+
class SimpleExpr:
'''Simple example of an expression type handler'''