[ZPT] CVS: Zope/lib/python/Products/PageTemplates - Expressions.py:1.34 PageTemplate.py:1.23 PageTemplateFile.py:1.13 TALES.py:1.29 ZopePageTemplate.py:1.32
Shane Hathaway
shane@cvs.zope.org
Wed, 3 Apr 2002 15:44:25 -0500
Update of /cvs-repository/Zope/lib/python/Products/PageTemplates
In directory cvs.zope.org:/tmp/cvs-serv3170/lib/python/Products/PageTemplates
Modified Files:
Expressions.py PageTemplate.py PageTemplateFile.py TALES.py
ZopePageTemplate.py
Log Message:
Merged shane-better-tracebacks-branch. The changes are explained in http://dev.zope.org/Wikis/DevSite/Proposals/BetterTracebacks
=== Zope/lib/python/Products/PageTemplates/Expressions.py 1.33 => 1.34 ===
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)
@@ -328,9 +324,10 @@
if not validate(object, container, name, o):
raise Unauthorized, name
else:
- o=get(object, name, M)
+ # Try an attribute.
+ o = get(object, name, M)
if o is not M:
- # Check security.
+ # Check access to the attribute.
if has(object, 'aq_acquire'):
object.aq_acquire(
name, validate2, validate)
@@ -338,12 +335,31 @@
if not validate(object, object, name, o):
raise Unauthorized, name
else:
+ # Try an item.
try:
- o=object[name]
- except (AttributeError, TypeError):
- raise AttributeError, name
- if not validate(object, object, name, o):
- raise Unauthorized, name
+ # XXX maybe in Python 2.2 we can just check whether
+ # the object has the attribute "__getitem__"
+ # instead of blindly catching exceptions.
+ o = object[name]
+ except AttributeError, exc:
+ if str(exc).find('__getitem__') >= 0:
+ # The object does not support the item interface.
+ # Try to re-raise the original attribute error.
+ # XXX I think this only happens with
+ # ExtensionClass instances.
+ get(object, name)
+ raise
+ except TypeError, exc:
+ if str(exc).find('unsubscriptable') >= 0:
+ # The object does not support the item interface.
+ # Try to re-raise the original attribute error.
+ # XXX This is sooooo ugly.
+ get(object, name)
+ raise
+ else:
+ # Check access to the item.
+ if not validate(object, object, name, o):
+ raise Unauthorized, name
object = o
return object
=== Zope/lib/python/Products/PageTemplates/PageTemplate.py 1.22 => 1.23 ===
__version__='$Revision$'[11:-2]
-import os, sys, traceback, pprint
+import sys
+
from TAL.TALParser import TALParser
from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALGenerator import TALGenerator
@@ -26,12 +27,8 @@
from string import join, strip, rstrip, split, replace, lower, find
from cStringIO import StringIO
from ExtensionClass import Base
+from ComputedAttribute import ComputedAttribute
-Z_DEBUG_MODE = os.environ.get('Z_DEBUG_MODE') == '1'
-
-class MacroCollection(Base):
- def __of__(self, parent):
- return parent.pt_macros()
class PageTemplate(Base):
"Page Templates using TAL, TALES, and METAL"
@@ -40,11 +37,16 @@
expand = 0
_v_errors = ()
_v_warnings = ()
+ _v_program = None
+ _v_macros = None
+ _v_cooked = 0
id = '(unknown)'
_text = ''
_error_start = '<!-- Page Template Diagnostics'
- macros = MacroCollection()
+ def macros(self):
+ return self.pt_macros()
+ macros = ComputedAttribute(macros, 1)
def pt_edit(self, text, content_type):
if content_type:
@@ -72,13 +74,16 @@
def pt_render(self, source=0, extra_context={}):
"""Render this Page Template"""
+ if not self._v_cooked:
+ self._cook()
+
+ __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
+
if self._v_errors:
raise PTRuntimeError, 'Page Template %s has errors.' % self.id
output = StringIO()
c = self.pt_getContext()
c.update(extra_context)
- if Z_DEBUG_MODE:
- __traceback_info__ = pprint.pformat(c)
TALInterpreter(self._v_program, self._v_macros,
getEngine().getContext(c),
@@ -92,6 +97,8 @@
return self.pt_render(extra_context={'options': kwargs})
def pt_errors(self):
+ if not self._v_cooked:
+ self._cook()
err = self._v_errors
if err:
return err
@@ -102,13 +109,21 @@
return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
def pt_warnings(self):
+ if not self._v_cooked:
+ self._cook()
return self._v_warnings
def pt_macros(self):
+ if not self._v_cooked:
+ self._cook()
if self._v_errors:
+ __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
raise PTRuntimeError, 'Page Template %s has errors.' % self.id
return self._v_macros
+ def pt_source_file(self):
+ return None # Unknown.
+
def write(self, text):
assert type(text) is type('')
if text[:len(self._error_start)] == self._error_start:
@@ -120,6 +135,8 @@
self._cook()
def read(self):
+ if not self._v_cooked:
+ self._cook()
if not self._v_errors:
if not self.expand:
return self._text
@@ -137,14 +154,14 @@
def _cook(self):
"""Compile the TAL and METAL statments.
- A Page Template must always be cooked, and cooking must not
- fail due to user input.
+ Cooking must not fail due to compilation errors in templates.
"""
+ source_file = self.pt_source_file()
if self.html():
- gen = TALGenerator(getEngine(), xml=0)
+ gen = TALGenerator(getEngine(), xml=0, source_file=source_file)
parser = HTMLTALParser(gen)
else:
- gen = TALGenerator(getEngine())
+ gen = TALGenerator(getEngine(), source_file=source_file)
parser = TALParser(gen)
self._v_errors = ()
@@ -155,6 +172,7 @@
self._v_errors = ["Compilation failed",
"%s: %s" % sys.exc_info()[:2]]
self._v_warnings = parser.getWarnings()
+ self._v_cooked = 1
def html(self):
if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
@@ -174,3 +192,16 @@
class PTRuntimeError(RuntimeError):
'''The Page Template has template errors that prevent it from rendering.'''
pass
+
+
+class PageTemplateTracebackSupplement:
+ #__implements__ = ITracebackSupplement
+
+ def __init__(self, pt):
+ self.object = pt
+ w = pt.pt_warnings()
+ e = pt.pt_errors()
+ if e:
+ w = list(w) + list(e)
+ self.warnings = w
+
=== Zope/lib/python/Products/PageTemplates/PageTemplateFile.py 1.12 => 1.13 ===
return PageTemplate.pt_macros(self)
+ def pt_source_file(self):
+ """Returns a file name to be compiled into the TAL code."""
+ return self.__name__ # Don't reveal filesystem paths
+
def _cook_check(self):
if self._v_last_read and not DevelopmentMode:
return
@@ -132,6 +136,6 @@
__roles__ = ComputedAttribute(_get__roles__, 1)
- def __setstate__(self, state):
+ def __getstate__(self):
raise StorageError, ("Instance of AntiPersistent class %s "
"cannot be stored." % self.__class__.__name__)
=== Zope/lib/python/Products/PageTemplates/TALES.py 1.28 => 1.29 ===
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,11 @@
'''
_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
@@ -243,18 +214,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, expression)
+ v = expression(self)
+ return v
evaluateValue = evaluate
@@ -276,14 +239,41 @@
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 setSourceFile(self, source_file):
+ self.source_file = source_file
+
def setPosition(self, position):
self.position = position
+
+
+
+class TALESTracebackSupplement:
+ """Implementation of ITracebackSupplement"""
+ def __init__(self, context, expression):
+ self.context = context
+ self.source_url = context.source_file
+ self.line = context.position[0]
+ self.column = context.position[1]
+ self.expression = repr(expression)
+
+ 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'''
=== Zope/lib/python/Products/PageTemplates/ZopePageTemplate.py 1.31 => 1.32 ===
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,7 @@
# 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
+ 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)
@@ -264,6 +256,8 @@
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': 'misc_/PageTemplates/zpt.gif',
'alt': self.meta_type, 'title': self.meta_type},)
+ if not self._v_cooked:
+ self._cook()
if self._v_errors:
icons = icons + ({'path': 'misc_/PageTemplates/exclamation.gif',
'alt': 'Error',
@@ -271,13 +265,23 @@
return icons
def __setstate__(self, state):
+ # This is here for backward compatibility. :-(
ZopePageTemplate.inheritedAttribute('__setstate__')(self, state)
- self._cook()
+
+ def pt_source_file(self):
+ """Returns a file name to be compiled into the TAL code."""
+ try:
+ return '/'.join(self.getPhysicalPath())
+ except:
+ # This page template is being compiled without an
+ # acquisition context, so we don't know where it is. :-(
+ return None
if not SUPPORTS_WEBDAV_LOCKS:
def wl_isLocked(self):
return 0
+
class Src(Acquisition.Explicit):
" "
@@ -295,6 +299,7 @@
d = ZopePageTemplate.__dict__
d['source.xml'] = d['source.html'] = Src()
+
# Product registration and Add support
manage_addPageTemplateForm = PageTemplateFile(
'www/ptAdd', globals(), __name__='manage_addPageTemplateForm')
@@ -322,9 +327,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 ''