[ZPT] CVS: Zope/lib/python/Products/PageTemplates - Expressions.py:1.32.2.1 PageTemplate.py:1.21.2.1 TALES.py:1.28.2.1 ZopePageTemplate.py:1.30.2.1
Shane Hathaway
shane@cvs.zope.org
Fri, 1 Mar 2002 17:22:40 -0500
Update of /cvs-repository/Zope/lib/python/Products/PageTemplates
In directory cvs.zope.org:/tmp/cvs-serv11778
Modified Files:
Tag: shane-better-tracebacks-branch
Expressions.py PageTemplate.py TALES.py ZopePageTemplate.py
Log Message:
Rewired exception handling in page templates so that:
- Tracebacks are never lost.
- Exceptions are never converted to other exception types.
- The ErrorReporter product can report line numbers and show URLs in traceback.
I made sure the tests still pass, so if any functionality is now broken,
we need more tests. ;-)
=== Zope/lib/python/Products/PageTemplates/Expressions.py 1.32 => 1.32.2.1 ===
import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
- TALESError, Undefined, Default, _parse_expr
+ Undefined, Default, _parse_expr
from string import strip, split, join, replace, lstrip
from Acquisition import aq_base, aq_inner, aq_parent
@@ -33,7 +33,6 @@
from PathIterator import Iterator
_engine = Engine(Iterator)
installHandlers(_engine)
- _engine._nocatch = (TALESError, 'Redirect')
return _engine
def installHandlers(engine):
@@ -171,7 +170,7 @@
def _eval(self, econtext,
isinstance=isinstance, StringType=type(''), render=render):
for expr in self._subexprs[:-1]:
- # Try all but the last subexpression, skipping undefined ones
+ # Try all but the last subexpression, skipping undefined ones.
try:
ob = expr(econtext)
except Undefs:
@@ -179,12 +178,8 @@
else:
break
else:
- # On the last subexpression allow exceptions through, but
- # wrap ones that indicate that the subexpression was undefined
- try:
- ob = self._subexprs[-1](econtext)
- except Undefs[1:]:
- raise Undefined(self._s, sys.exc_info())
+ # On the last subexpression allow exceptions through.
+ ob = self._subexprs[-1](econtext)
if self._name == 'nocall' or isinstance(ob, StringType):
return ob
@@ -234,8 +229,9 @@
vvals = []
for var in self._vars:
v = var(econtext)
- if isinstance(v, Exception):
- raise v
+ # I hope this isn't in use anymore.
+ ## if isinstance(v, Exception):
+ ## raise v
vvals.append(v)
return self._expr % tuple(vvals)
@@ -341,6 +337,7 @@
try:
o=object[name]
except (AttributeError, TypeError):
+ # Assume the object does not support the item interface.
raise AttributeError, name
if not validate(object, object, name, o):
raise Unauthorized, name
=== Zope/lib/python/Products/PageTemplates/PageTemplate.py 1.21 => 1.21.2.1 ===
__version__='$Revision$'[11:-2]
-import os, sys, traceback, pprint
+import sys, pprint
+from cgi import escape
+
from TAL.TALParser import TALParser
from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALGenerator import TALGenerator
@@ -27,7 +29,6 @@
from cStringIO import StringIO
from ExtensionClass import Base
-Z_DEBUG_MODE = os.environ.get('Z_DEBUG_MODE') == '1'
class MacroCollection(Base):
def __of__(self, parent):
@@ -77,8 +78,8 @@
output = StringIO()
c = self.pt_getContext()
c.update(extra_context)
- if Z_DEBUG_MODE:
- __traceback_info__ = pprint.pformat(c)
+
+ __traceback_supplement__ = (PageTemplateTracebackSupplement, self, c)
TALInterpreter(self._v_program, self._v_macros,
getEngine().getContext(c),
@@ -174,3 +175,31 @@
class PTRuntimeError(RuntimeError):
'''The Page Template has template errors that prevent it from rendering.'''
pass
+
+
+class PageTemplateTracebackSupplement:
+ """Implementation of Products.ErrorReporter.ITracebackSupplement
+
+ Shows the context in which an expression is executed.
+ """
+
+ def __init__(self, pt, context):
+ c = context.copy()
+ if c.has_key('request'):
+ c['request'] = '(not shown)'
+ self.context = c
+ self.pt = pt
+
+ def getInfo(self, as_html=0):
+ s = pprint.pformat(self.context)
+ if not as_html:
+ return ' - Context:\n %s' % s.replace('\n', '\n ')
+ else:
+ return '<b>Context:</b><pre>%s</pre>' % (
+ escape(pprint.pformat(self.context)))
+
+ def getManageableObject(self):
+ # N/A
+ return None
+ getLine = getColumn = getExpression = getManageableObject
+
=== Zope/lib/python/Products/PageTemplates/TALES.py 1.28 => 1.28.2.1 ===
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 on %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 1
+ """Error during TALES expression evaluation"""
class Undefined(TALESError):
'''Exception raised on traversal of an undefined path'''
- def __str__(self):
- if self.type is None:
- s = self.expression
- else:
- s = '%s not found in %s' % (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
class RegistrationError(Exception):
'''TALES Type Registration Error'''
@@ -107,18 +71,27 @@
self._context = context
def next(self):
- try:
- if ZTUtils.Iterator.next(self):
- self._context.setLocal(self.name, self.item)
- return 1
- except TALESError:
- raise
- except:
- raise TALESError, ('repeat/%s' % self.name,
- sys.exc_info()), sys.exc_info()[2]
+ if ZTUtils.Iterator.next(self):
+ self._context.setLocal(self.name, self.item)
+ return 1
return 0
+class ErrorInfo:
+ """Information about an exception passed to an on-error handler."""
+ __allow_access_to_unprotected_subobjects__ = 1
+
+ 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 Engine:
'''Expression Engine
@@ -181,13 +154,10 @@
'''
_context_class = SafeMapping
- _nocatch = TALESError
position = (None, 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
@@ -243,18 +213,10 @@
isinstance=isinstance, StringType=StringType):
if isinstance(expression, StringType):
expression = self._engine.compile(expression)
- try:
- v = expression(self)
- 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.position, expression)
+ v = expression(self)
+ return v
evaluateValue = evaluate
@@ -276,14 +238,38 @@
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
def setPosition(self, position):
self.position = position
+
+
+class TALESTracebackSupplement:
+ """Implementation of Products.ErrorReporter.ITracebackSupplement"""
+ def __init__(self, position, expression):
+ self.position = position
+ self.expression = expression
+
+ def getManageableObject(self):
+ return None
+
+ def getLine(self):
+ return self.position[0]
+
+ def getColumn(self):
+ return self.position[0]
+
+ def getExpression(self):
+ return repr(self.expression)
+
+ def getInfo(self, as_html=0):
+ return None
+
+
class SimpleExpr:
'''Simple example of an expression type handler'''
=== Zope/lib/python/Products/PageTemplates/ZopePageTemplate.py 1.30 => 1.30.2.1 ===
from OFS.PropertyManager import PropertyManager
from PageTemplate import PageTemplate
-from TALES import TALESError
from Expressions import SecureModuleImporter
from PageTemplateFile import PageTemplateFile
@@ -187,7 +186,8 @@
try:
self.REQUEST.RESPONSE.setHeader('content-type',
self.content_type)
- except AttributeError: pass
+ except AttributeError:
+ pass
security=getSecurityManager()
bound_names['user'] = security.getUser()
@@ -206,15 +206,9 @@
# Execute the template in a new security context.
security.addContext(self)
try:
- try:
- result = self.pt_render(extra_context=bound_names)
- except TALESError, err:
- if (err.type == Unauthorized or
- (isinstance(Unauthorized, Exception) and
- isinstance(err.type, Unauthorized))):
- raise err.type, err.value, err.takeTraceback()
- err.takeTraceback()
- raise
+ __traceback_supplement__ = (
+ ZopePageTemplateTracebackSupplement, self)
+ result = self.pt_render(extra_context=bound_names)
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
@@ -278,6 +272,7 @@
def wl_isLocked(self):
return 0
+
class Src(Acquisition.Explicit):
" "
@@ -291,6 +286,24 @@
d = ZopePageTemplate.__dict__
d['source.xml'] = d['source.html'] = Src()
+
+class ZopePageTemplateTracebackSupplement:
+ """Implementation of Products.ErrorReporter.ITracebackSupplement"""
+ def __init__(self, template):
+ self.template = template
+
+ def getManageableObject(self):
+ return self.template
+
+ def getInfo(self, as_html=0):
+ return None
+
+ def getLine(self):
+ # N/A
+ return None
+ getColumn = getExpression = getLine
+
+
# Product registration and Add support
manage_addPageTemplateForm = PageTemplateFile(
'www/ptAdd', globals(), __name__='manage_addPageTemplateForm')
@@ -318,9 +331,13 @@
self._setObject(id, zpt)
- try: u = self.DestinationURL()
- except: u = REQUEST['URL1']
- if submit==" Add and Edit ": u="%s/%s" % (u,quote(id))
+ try:
+ u = self.DestinationURL()
+ except AttributeError:
+ u = REQUEST['URL1']
+
+ if submit == " Add and Edit ":
+ u = "%s/%s" % (u, quote(id))
REQUEST.RESPONSE.redirect(u+'/manage_main')
return ''