Commit ccd4d15e authored by Shane Hathaway's avatar Shane Hathaway

Merged shane-better-tracebacks-branch. The changes are explained in...

Merged shane-better-tracebacks-branch.  The changes are explained in http://dev.zope.org/Wikis/DevSite/Proposals/BetterTracebacks
parent d4302f00
......@@ -17,8 +17,8 @@ Aqueduct database adapters, etc.
This module can also be used as a simple template for implementing new
item types.
$Id: SimpleItem.py,v 1.94 2002/03/27 10:14:03 htrd Exp $'''
__version__='$Revision: 1.94 $'[11:-2]
$Id: SimpleItem.py,v 1.95 2002/04/03 20:43:52 shane Exp $'''
__version__='$Revision: 1.95 $'[11:-2]
import re, sys, Globals, App.Management, Acquisition, App.Undo
import AccessControl.Role, AccessControl.Owned, App.Common
......@@ -29,9 +29,11 @@ from types import InstanceType, StringType
from ComputedAttribute import ComputedAttribute
from AccessControl import getSecurityManager
from Traversable import Traversable
from Acquisition import aq_base
from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
from DocumentTemplate.ustr import ustr
from zExceptions.ExceptionFormatter import format_exception
import time
from zLOG import LOG, ERROR
import marshal
import ZDOM
......@@ -146,19 +148,27 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable,
if error_type is None: error_type =sys.exc_info()[0]
if error_value is None: error_value=sys.exc_info()[1]
# turn error_type into a string
if hasattr(error_type, '__name__'):
error_type=error_type.__name__
# allow for a few different traceback options
if tb is None and error_tb is None:
tb=sys.exc_info()[2]
if type(tb) is not type('') and (error_tb is None):
error_tb=pretty_tb(error_type, error_value, tb)
error_tb = pretty_tb(error_type, error_value, tb)
elif type(tb) is type('') and not error_tb:
error_tb=tb
error_tb = tb
try:
log = aq_acquire(self, '__error_log__', containment=1)
except AttributeError:
pass
else:
log.raising((error_type, error_value, tb))
# turn error_type into a string
if hasattr(error_type, '__name__'):
error_type=error_type.__name__
if hasattr(self, '_v_eek'):
# Stop if there is recursion.
raise error_type, error_value, tb
self._v_eek=1
......@@ -198,7 +208,11 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable,
else:
v = HTML.__call__(s, client, REQUEST, **kwargs)
except:
v = error_value or "Sorry, an error occurred"
LOG('OFS', ERROR, 'Exception while rendering an error message',
error=sys.exc_info())
v = repr(error_value) + (
" (Also, an error occurred while attempting "
"to render the standard error message.)")
raise error_type, v, tb
finally:
if hasattr(self, '_v_eek'): del self._v_eek
......@@ -311,45 +325,19 @@ class Item_w__name__(Item):
'''
path = (self.__name__,)
p = getattr(self,'aq_inner', None)
p = aq_parent(aq_inner(self))
if p is not None:
path = p.aq_parent.getPhysicalPath() + path
path = p.getPhysicalPath() + path
return path
def format_exception(etype,value,tb,limit=None):
import traceback
result=['Traceback (innermost last):']
if limit is None:
if hasattr(sys, 'tracebacklimit'):
limit = sys.tracebacklimit
n = 0
while tb is not None and (limit is None or n < limit):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals=f.f_locals
result.append(' File %s, line %d, in %s'
% (filename,lineno,name))
try: result.append(' (Object: %s)' %
locals[co.co_varnames[0]].__name__)
except: pass
try: result.append(' (Info: %s)' %
str(locals['__traceback_info__']))
except: pass
tb = tb.tb_next
n = n+1
result.append(' '.join(traceback.format_exception_only(etype, value)))
return result
def pretty_tb(t,v,tb):
tb=format_exception(t,v,tb,200)
tb='\n'.join(tb)
def pretty_tb(t, v, tb, as_html=1):
tb = format_exception(t, v, tb, as_html=as_html)
tb = '\n'.join(tb)
return tb
class SimpleItem(Item, Globals.Persistent,
Acquisition.Implicit,
AccessControl.Role.RoleManager,
......@@ -365,3 +353,29 @@ class SimpleItem(Item, Globals.Persistent,
)
__ac_permissions__=(('View', ()),)
def __repr__(self):
"""Show the physical path of the object and its context if available.
"""
try:
path = '/'.join(self.getPhysicalPath())
except:
path = None
context_path = None
context = aq_parent(self)
container = aq_parent(aq_inner(self))
if aq_base(context) is not aq_base(container):
try:
context_path = '/'.join(context.getPhysicalPath())
except:
context_path = None
res = '<%s' % self.__class__.__name__
if path:
res += ' at %s' % path
else:
res += ' at 0x%x' % id(self)
if context_path:
res += ' used for %s' % context_path
res += '>'
return res
......@@ -17,11 +17,11 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths.
"""
__version__='$Revision: 1.33 $'[11:-2]
__version__='$Revision: 1.34 $'[11:-2]
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 @@ def getEngine():
from PathIterator import Iterator
_engine = Engine(Iterator)
installHandlers(_engine)
_engine._nocatch = (TALESError, 'Redirect')
return _engine
def installHandlers(engine):
......@@ -171,7 +170,7 @@ class PathExpr:
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 @@ class PathExpr:
else:
break
else:
# On the last subexpression allow exceptions through, but
# wrap ones that indicate that the subexpression was undefined
try:
# On the last subexpression allow exceptions through.
ob = self._subexprs[-1](econtext)
except Undefs[1:]:
raise Undefined(self._s, sys.exc_info())
if self._name == 'nocall' or isinstance(ob, StringType):
return ob
......@@ -234,8 +229,9 @@ class StringExpr:
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 @@ def restrictedTraverse(self, path, securityManager,
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,10 +335,29 @@ def restrictedTraverse(self, path, securityManager,
if not validate(object, object, name, o):
raise Unauthorized, name
else:
# Try an item.
try:
o=object[name]
except (AttributeError, TypeError):
raise AttributeError, 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
......
......@@ -15,9 +15,10 @@
HTML- and XML-based template objects using TAL, TALES, and METAL.
"""
__version__='$Revision: 1.22 $'[11:-2]
__version__='$Revision: 1.23 $'[11:-2]
import sys
import os, sys, traceback, pprint
from TAL.TALParser import TALParser
from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALGenerator import TALGenerator
......@@ -26,12 +27,8 @@ from Expressions import getEngine
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 @@ class PageTemplate(Base):
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 @@ class PageTemplate(Base):
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 @@ class PageTemplate(Base):
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 @@ class PageTemplate(Base):
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 @@ class PageTemplate(Base):
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 @@ class PageTemplate(Base):
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 @@ class PageTemplate(Base):
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 @@ ModuleImporter = _ModuleImporter()
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
......@@ -15,7 +15,7 @@
Zope object encapsulating a Page Template from the filesystem.
"""
__version__='$Revision: 1.12 $'[11:-2]
__version__='$Revision: 1.13 $'[11:-2]
import os, AccessControl, Acquisition, sys
from Globals import package_home, DevelopmentMode
......@@ -100,6 +100,10 @@ class PageTemplateFile(Script, PageTemplate, Traversable):
self._cook_check()
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 @@ class PageTemplateFile(Script, PageTemplate, Traversable):
__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__)
......@@ -15,7 +15,7 @@
An implementation of a generic TALES engine
"""
__version__='$Revision: 1.28 $'[11:-2]
__version__='$Revision: 1.29 $'[11:-2]
import re, sys, ZTUtils
from MultiMapping import MultiMapping
......@@ -27,46 +27,10 @@ _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 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 @@ class Iterator(ZTUtils.Iterator):
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]
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 @@ class Context:
'''
_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,17 +214,9 @@ class Context:
isinstance=isinstance, StringType=StringType):
if isinstance(expression, StringType):
expression = self._engine.compile(expression)
try:
__traceback_supplement__ = (
TALESTracebackSupplement, self, expression)
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
evaluateValue = evaluate
......@@ -276,15 +239,42 @@ class Context:
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'''
def __init__(self, name, expr, engine):
......
......@@ -15,7 +15,7 @@
Zope object encapsulating a Page Template.
"""
__version__='$Revision: 1.31 $'[11:-2]
__version__='$Revision: 1.32 $'[11:-2]
import os, AccessControl, Acquisition, sys
from Globals import DTMLFile, ImageFile, MessageDialog, package_home
......@@ -35,7 +35,6 @@ from OFS.Cache import Cacheable
from OFS.Traversable import Traversable
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 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
try:
self.REQUEST.RESPONSE.setHeader('content-type',
self.content_type)
except AttributeError: pass
except AttributeError:
pass
security=getSecurityManager()
bound_names['user'] = security.getUser()
......@@ -205,16 +205,8 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
# 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
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
......@@ -264,6 +256,8 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
"""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 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
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 @@ class Src(Acquisition.Explicit):
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 @@ def manage_addPageTemplate(self, id, title=None, text=None,
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 ''
......
......@@ -17,7 +17,7 @@ This product provides support for Script objects containing restricted
Python code.
"""
__version__='$Revision: 1.40 $'[11:-2]
__version__='$Revision: 1.41 $'[11:-2]
import sys, os, traceback, re, marshal
from Globals import DTMLFile, MessageDialog, package_home
......@@ -247,7 +247,7 @@ class PythonScript(Script, Historical, Cacheable):
'_getattr_': guarded_getattr,
'_getitem_': guarded_getitem,
'_write_': full_write_guard,
'_print_': RestrictedPython.PrintCollector
'_print_': RestrictedPython.PrintCollector,
}
l = {}
exec code in g, l
......@@ -286,14 +286,23 @@ class PythonScript(Script, Historical, Cacheable):
# Got a cached value.
return result
__traceback_info__ = bound_names, args, kw, self.func_defaults
#__traceback_info__ = bound_names, args, kw, self.func_defaults
f = self._v_f
if f is None:
__traceback_supplement__ = (
PythonScriptTracebackSupplement, self)
raise RuntimeError, '%s %s has errors.' % (self.meta_type, self.id)
if bound_names is not None:
# Updating func_globals directly *should* be thread-safe.
# XXX This causes the whole acquisition chain
# to be held by self._v_f. I think we really should
# use new.function() instead, similar to
# CMFCore.FSPythonScript. new.function() takes
# about 8 microseconds on a 1 GHz Athlon. - Shane
f.func_globals.update(bound_names)
f.func_globals['__traceback_supplement__'] = (
PythonScriptTracebackSupplement, self, -1)
# Execute the function in a new security context.
security=getSecurityManager()
......@@ -467,6 +476,15 @@ class PythonScript(Script, Historical, Cacheable):
RESPONSE.setHeader('Content-Type', 'text/plain')
return self.read()
class PythonScriptTracebackSupplement:
"""Implementation of ITracebackSupplement"""
def __init__(self, script, line=0):
self.object = script
# If line is set to -1, it means to use tb_lineno.
self.line = line
_first_indent = re.compile('(?m)^ *(?! |$)')
_nonempty_line = re.compile('(?m)^(.*\S.*)$')
......
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Site error log module.
$Id: SiteErrorLog.py,v 1.2 2002/04/03 20:43:55 shane Exp $
"""
import os
import sys
import time
from random import random
from thread import allocate_lock
from types import StringType, UnicodeType
import Globals
from Acquisition import aq_base
from AccessControl import ClassSecurityInfo, getSecurityManager, Unauthorized
from OFS.SimpleItem import SimpleItem
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from zExceptions.ExceptionFormatter import format_exception
from zLOG import LOG, ERROR
use_error_logging = 'Log Site Errors'
log_to_event_log = 'Log to the Event Log'
_www = os.path.join(os.path.dirname(__file__), 'www')
# temp_logs holds the logs.
temp_logs = {} # { oid -> [ traceback string ] }
cleanup_lock = allocate_lock()
class SiteErrorLog (SimpleItem):
"""Site error log class. You can put an error log anywhere in the tree
and exceptions in that area will be posted to the site error log.
"""
meta_type = 'Site Error Log'
id = 'error_log'
keep_entries = 20
copy_to_zlog = 0
security = ClassSecurityInfo()
manage_options = (
{'label': 'Log', 'action': 'manage_main'},
) + SimpleItem.manage_options
security.declareProtected(use_error_logging, 'getProperties')
manage_main = PageTemplateFile('main.pt', _www)
security.declareProtected(use_error_logging, 'showEntry')
showEntry = PageTemplateFile('showEntry.pt', _www)
security.declarePrivate('manage_beforeDelete')
def manage_beforeDelete(self, item, container):
if item is self:
try:
del container.__error_log__
except AttributeError:
pass
security.declarePrivate('manage_afterAdd')
def manage_afterAdd(self, item, container):
if item is self:
container.__error_log__ = aq_base(self)
def _setId(self, id):
if id != self.id:
raise Globals.MessageDialog(
title='Invalid Id',
message='Cannot change the id of a SiteErrorLog',
action ='./manage_main',)
def _getLog(self):
"""Returns the log for this object.
Careful, the log is shared between threads.
"""
log = temp_logs.get(self._p_oid, None)
if log is None:
log = []
temp_logs[self._p_oid] = log
return log
security.declarePrivate('raising')
def raising(self, info):
"""Log an exception.
Called by SimpleItem's exception handler.
"""
try:
now = time.time()
try:
tb_text = None
tb_html = None
if not isinstance(info[2], StringType) and not isinstance(
info[2], UnicodeType):
tb_text = ''.join(
format_exception(*info, **{'as_html': 0}))
tb_html = ''.join(
format_exception(*info, **{'as_html': 1}))
else:
tb_text = info[2]
request = getattr(self, 'REQUEST', None)
url = None
username = None
req_html = None
if request:
url = request['URL']
username = getSecurityManager().getUser().getUserName()
try:
req_html = str(request)
except:
pass
log = self._getLog()
log.append({
'type': str(getattr(info[0], '__name__', info[0])),
'value': str(info[1]),
'time': now,
'id': str(now) + str(random()), # Low chance of collision
'tb_text': tb_text,
'tb_html': tb_html,
'username': username,
'url': url,
'req_html': req_html,
})
cleanup_lock.acquire()
try:
if len(log) >= self.keep_entries:
del log[:-self.keep_entries]
finally:
cleanup_lock.release()
except:
LOG('SiteError', ERROR, 'Error while logging',
error=sys.exc_info())
else:
if self.copy_to_zlog:
LOG('SiteError', ERROR, str(url), error=info)
finally:
info = None
security.declareProtected(use_error_logging, 'getProperties')
def getProperties(self):
return {'keep_entries': self.keep_entries,
'copy_to_zlog': self.copy_to_zlog}
security.declareProtected(log_to_event_log, 'checkEventLogPermission')
def checkEventLogPermission(self):
if not getSecurityManager().checkPermission(log_to_event_log, self):
raise Unauthorized, ('You do not have the "%s" permission.' %
log_to_event_log)
return 1
security.declareProtected(use_error_logging, 'setProperties')
def setProperties(self, keep_entries, copy_to_zlog=0, RESPONSE=None):
"""Sets the properties of this site error log.
"""
copy_to_zlog = not not copy_to_zlog
if copy_to_zlog and not self.copy_to_zlog:
# Before turning on event logging, check the permission.
self.checkEventLogPermission()
self.keep_entries = int(keep_entries)
self.copy_to_zlog = copy_to_zlog
if RESPONSE is not None:
RESPONSE.redirect(
'%s/manage_main?manage_tabs_message=Changed+properties.' %
self.absolute_url())
security.declareProtected(use_error_logging, 'getLogEntries')
def getLogEntries(self):
"""Returns the entries in the log.
Makes a copy to prevent changes.
"""
# List incomprehension ;-)
return [entry.copy() for entry in self._getLog()]
security.declareProtected(use_error_logging, 'getLogEntryById')
def getLogEntryById(self, id):
"""Returns the specified log entry.
Makes a copy to prevent changes. Returns None if not found.
"""
for entry in self._getLog():
if entry['id'] == id:
return entry.copy()
return None
Globals.InitializeClass(SiteErrorLog)
def manage_addErrorLog(dispatcher, RESPONSE=None):
"""Add a site error log to a container."""
log = SiteErrorLog()
dispatcher._setObject(log.id, log)
if RESPONSE is not None:
RESPONSE.redirect(
dispatcher.DestinationURL() +
'/manage_main?manage_tabs_message=Error+Log+Added.' )
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Site error log product.
$Id: __init__.py,v 1.2 2002/04/03 20:43:55 shane Exp $
"""
import SiteErrorLog
def initialize(context):
context.registerClass(SiteErrorLog.SiteErrorLog,
constructors=(SiteErrorLog.manage_addErrorLog,),
permission=SiteErrorLog.use_error_logging,
icon='www/error.gif')
<h1 tal:replace="structure here/manage_page_header">Header</h1>
<h1 tal:replace="structure here/manage_tabs">Tabs</h1>
<p class="form-help">
This page lists the exceptions that have occurred in this site
recently. You can configure how many exceptions should be kept
and whether the exceptions should be copied to Zope's event log
file(s).
</p>
<form action="setProperties" method="post">
<table tal:define="props container/getProperties">
<tr>
<td align="left" valign="top">
<div class="form-label">
Number of exceptions to keep
</div>
</td>
<td align="left" valign="top">
<input type="text" name="keep_entries" size="40"
tal:attributes="value props/keep_entries" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Copy exceptions to the event log
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="copy_to_zlog"
tal:attributes="checked props/copy_to_zlog;
disabled not:container/checkEventLogPermission|nothing" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Save Changes " />
</div>
</td>
</tr>
</table>
<h3>Exception Log</h3>
<div tal:define="entries container/getLogEntries">
<em tal:condition="not:entries">
No exceptions logged.
</em>
<table tal:condition="entries">
<tr>
<th align="left">Time</th>
<th align="left">User</th>
<th align="left">Exception</th>
</tr>
<tr tal:repeat="entry entries">
<td valign="top" nowrap="nowrap">
<span tal:content="python: DateTime(entry['time']).Time()">13:04:41</span>
</td>
<td>
<span tal:content="entry/username">joe</span>
</td>
<td valign="top">
<a href="showEntry" tal:attributes="href string:showEntry?id=${entry/id}"
>
<span tal:content="entry/type">AttributeError</span>:
<span tal:define="value entry/value"
tal:content="python: len(value) < 70 and value or value[:70] + '...'">
Application object has no attribute "zzope"</span>
</a>
</td>
</tr>
</table>
</div>
</form>
<p>
<form action="manage_main" method="GET">
<input type="submit" name="submit" value=" Refresh " />
</form>
</p>
<h1 tal:replace="structure here/manage_page_footer">Footer</h1>
<h1 tal:replace="structure here/manage_page_header">Header</h1>
<h1 tal:replace="structure here/manage_tabs">Tabs</h1>
<h3>Exception traceback</h3>
<div tal:define="entry python:container.getLogEntryById(request.get('id'))">
<em tal:condition="not:entry">
The specified log entry was not found. It may have expired.
</em>
<div tal:condition="entry">
<table>
<tr>
<th align="left" valign="top">Time</th>
<td tal:content="python: DateTime(entry['time'])"></td>
</tr>
<tr>
<th align="left" valign="top">User</th>
<td tal:content="entry/username">joe</td>
</tr>
<tr>
<th align="left" valign="top">Request URL</th>
<td tal:content="entry/url">http://example.com</td>
</tr>
<tr>
<th align="left" valign="top">Exception Type</th>
<td tal:content="entry/type">AttributeError</td>
</tr>
<tr>
<th align="left" valign="top">Exception Value</th>
<td tal:content="entry/value">zzope</td>
</tr>
</table>
<div tal:condition="python: entry['tb_html'] and
not request.get('show_entry_as_text')">
<div tal:content="structure entry/tb_html">
Traceback
</div>
<p tal:condition="entry/tb_text"><a href="" tal:attributes="href
string:showEntry?id=${entry/id}&show_entry_as_text=1">Display
traceback as text</a></p>
</div>
<div tal:condition="python: not entry['tb_html'] or
request.get('show_entry_as_text')">
<pre tal:content="entry/tb_text">
Traceback
</pre>
<p tal:condition="entry/tb_html"><a href="" tal:attributes="href
string:showEntry?id=${entry/id}">Display
traceback as HTML</a></p>
</div>
<div tal:condition="entry/req_html">
<h3>REQUEST</h3>
<div tal:replace="structure entry/req_html"></div>
</div>
</div>
<p>
<form action="manage_main" method="GET">
<input type="submit" name="submit" value=" Return to log " />
</form>
</p>
</div>
<h1 tal:replace="structure here/manage_page_footer">Footer</h1>
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -16,11 +17,10 @@ Dummy TALES engine so that I can test out the TAL implementation.
import re
import sys
from string import rfind, strip
import driver
from TALDefs import NAME_RE, TALError, TALESError
from TALDefs import NAME_RE, TALESError, ErrorInfo
Default = []
......@@ -32,6 +32,7 @@ class CompilerError(Exception):
class DummyEngine:
position = None
source_file = None
def __init__(self, macros=None):
if macros is None:
......@@ -44,6 +45,9 @@ class DummyEngine:
def getCompilerError(self):
return CompilerError
def setSourceFile(self, source_file):
self.source_file = source_file
def setPosition(self, position):
self.position = position
......@@ -51,7 +55,8 @@ class DummyEngine:
return "$%s$" % expr
def uncompile(self, expression):
assert expression[:1] == "$" == expression[-1:], expression
assert (expression.startswith("$") and expression.endswith("$"),
expression)
return expression[1:-1]
def beginScope(self):
......@@ -71,7 +76,8 @@ class DummyEngine:
self.globals[name] = value
def evaluate(self, expression):
assert expression[:1] == "$" == expression[-1:], expression
assert (expression.startswith("$") and expression.endswith("$"),
expression)
expression = expression[1:-1]
m = name_match(expression)
if m:
......@@ -82,7 +88,7 @@ class DummyEngine:
if type in ("string", "str"):
return expr
if type in ("path", "var", "global", "local"):
expr = strip(expr)
expr = expr.strip()
if self.locals.has_key(expr):
return self.locals[expr]
elif self.globals.has_key(expr):
......@@ -97,8 +103,15 @@ class DummyEngine:
try:
return eval(expr, self.globals, self.locals)
except:
raise TALESError("evaluation error in %s" % `expr`,
info=sys.exc_info())
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 evaluateValue(self, expr):
......@@ -122,7 +135,8 @@ class DummyEngine:
return self.evaluate(expr)
def evaluateMacro(self, macroName):
assert macroName[:1] == "$" == macroName[-1:], macroName
assert (macroName.startswith("$") and macroName.endswith("$"),
macroName)
macroName = macroName[1:-1]
file, localName = self.findMacroFile(macroName)
if not file:
......@@ -147,7 +161,7 @@ class DummyEngine:
def findMacroFile(self, macroName):
if not macroName:
raise TALESError("empty macro name")
i = rfind(macroName, '/')
i = macroName.rfind('/')
if i < 0:
# No slash -- must be a locally defined macro
return None, macroName
......@@ -161,8 +175,8 @@ class DummyEngine:
seq = self.evaluateSequence(expr)
return Iterator(name, seq, self)
def getTALESError(self):
return TALESError
def createErrorInfo(self, err, position):
return ErrorInfo(err, position)
def getDefault(self):
return Default
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -15,7 +16,6 @@ Parse HTML and compile to TALInterpreter intermediate code.
"""
import sys
import string
from TALGenerator import TALGenerator
from TALDefs import ZOPE_METAL_NS, ZOPE_TAL_NS, METALError, TALError
......@@ -74,7 +74,7 @@ class NestingError(HTMLParseError):
% (tagstack[0], endtag))
else:
msg = ('Open tags <%s> do not match close tag </%s>'
% (string.join(tagstack, '>, <'), endtag))
% ('>, <'.join(tagstack), endtag))
else:
msg = 'No tags are open to match </%s>' % endtag
HTMLParseError.__init__(self, msg, position)
......@@ -235,7 +235,7 @@ class HTMLTALParser(HTMLParser):
def scan_xmlns(self, attrs):
nsnew = {}
for key, value in attrs:
if key[:6] == "xmlns:":
if key.startswith("xmlns:"):
nsnew[key[6:]] = value
if nsnew:
self.nsstack.append(self.nsdict)
......@@ -249,7 +249,7 @@ class HTMLTALParser(HTMLParser):
def fixname(self, name):
if ':' in name:
prefix, suffix = string.split(name, ':', 1)
prefix, suffix = name.split(':', 1)
if prefix == 'xmlns':
nsuri = self.nsdict.get(suffix)
if nsuri in (ZOPE_TAL_NS, ZOPE_METAL_NS):
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -66,27 +67,22 @@ class METALError(TALError):
pass
class TALESError(TALError):
pass
# This exception can carry around another exception + traceback
def takeTraceback(self):
t = self.info[2]
self.info = self.info[:2] + (None,)
return t
class ErrorInfo:
def __init__(self, msg, position=(None, None), info=(None, None, None)):
t, v, tb = info
if t:
if issubclass(t, Exception) and t.__module__ == "exceptions":
err = t.__name__
def __init__(self, err, position=(None, None)):
if isinstance(err, Exception):
self.type = err.__class__
self.value = err
else:
err = str(t)
v = v is not None and str(v)
if v:
err = "%s: %s" % (err, v)
msg = "%s: %s" % (msg, err)
TALError.__init__(self, msg, position)
self.info = info
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)
......@@ -117,11 +113,10 @@ def parseSubstitution(arg, position=(None, None)):
def splitParts(arg):
# Break in pieces at undoubled semicolons and
# change double semicolons to singles:
import string
arg = string.replace(arg, ";;", "\0")
parts = string.split(arg, ';')
parts = map(lambda s, repl=string.replace: repl(s, "\0", ";"), parts)
if len(parts) > 1 and not string.strip(parts[-1]):
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
......@@ -139,7 +134,7 @@ def getProgramMode(program):
return None
def getProgramVersion(program):
if (isinstance(program, ListType) and len(program) >= 2 and
if (len(program) >= 2 and
isinstance(program[0], TupleType) and len(program[0]) == 2):
opcode, version = program[0]
if opcode == "version":
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -14,7 +15,6 @@
Code generator for TALInterpreter intermediate code.
"""
import string
import re
import cgi
......@@ -24,8 +24,9 @@ class TALGenerator:
inMacroUse = 0
inMacroDef = 0
source_file = None
def __init__(self, expressionCompiler=None, xml=1):
def __init__(self, expressionCompiler=None, xml=1, source_file=None):
if not expressionCompiler:
from DummyEngine import DummyEngine
expressionCompiler = DummyEngine()
......@@ -40,6 +41,9 @@ class TALGenerator:
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)
def getCode(self):
assert not self.stack
......@@ -78,9 +82,9 @@ class TALGenerator:
# instructions to be joined together.
output.append(self.optimizeArgsList(item))
continue
text = string.join(collect, "")
text = "".join(collect)
if text:
i = string.rfind(text, "\n")
i = text.rfind("\n")
if i >= 0:
i = len(text) - (i + 1)
output.append(("rawtextColumn", (text, i)))
......@@ -272,7 +276,7 @@ class TALGenerator:
def emitDefineMacro(self, macroName):
program = self.popProgram()
macroName = string.strip(macroName)
macroName = macroName.strip()
if self.macros.has_key(macroName):
raise METALError("duplicate macro definition: %s" % `macroName`,
self.position)
......@@ -291,7 +295,7 @@ class TALGenerator:
def emitDefineSlot(self, slotName):
program = self.popProgram()
slotName = string.strip(slotName)
slotName = slotName.strip()
if not re.match('%s$' % NAME_RE, slotName):
raise METALError("invalid slot name: %s" % `slotName`,
self.position)
......@@ -299,7 +303,7 @@ class TALGenerator:
def emitFillSlot(self, slotName):
program = self.popProgram()
slotName = string.strip(slotName)
slotName = slotName.strip()
if self.slots.has_key(slotName):
raise METALError("duplicate fill-slot name: %s" % `slotName`,
self.position)
......@@ -330,7 +334,7 @@ class TALGenerator:
self.program[i] = ("rawtext", text[:m.start()])
collect.append(m.group())
collect.reverse()
return string.join(collect, "")
return "".join(collect)
def unEmitNewlineWhitespace(self):
collect = []
......@@ -349,7 +353,7 @@ class TALGenerator:
break
text, rest = m.group(1, 2)
collect.reverse()
rest = rest + string.join(collect, "")
rest = rest + "".join(collect)
del self.program[i:]
if text:
self.emit("rawtext", text)
......@@ -427,6 +431,8 @@ class TALGenerator:
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:
......@@ -438,6 +444,8 @@ class TALGenerator:
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:
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -85,7 +86,6 @@ class TALInterpreter:
self.program = program
self.macros = macros
self.engine = engine
self.TALESError = engine.getTALESError()
self.Default = engine.getDefault()
self.stream = stream or sys.stdout
self._stream_write = self.stream.write
......@@ -112,6 +112,7 @@ class TALInterpreter:
self.col = 0
self.level = 0
self.scopeLevel = 0
self.sourceFile = None
def saveState(self):
return (self.position, self.col, self.stream,
......@@ -201,6 +202,11 @@ class TALInterpreter:
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)
......@@ -515,7 +521,12 @@ class TALInterpreter:
raise METALError("macro %s has incompatible mode %s" %
(`macroName`, `mode`), self.position)
self.pushMacro(macroName, compiledSlots)
saved_source = self.sourceFile
saved_position = self.position # Used by Boa Constructor
self.interpret(macro)
if self.sourceFile != saved_source:
self.engine.setSourceFile(saved_source)
self.sourceFile = saved_source
self.popMacro()
bytecode_handlers["useMacro"] = do_useMacro
......@@ -531,10 +542,15 @@ class TALInterpreter:
return
macs = self.macroStack
if macs and macs[-1] is not None:
saved_source = self.sourceFile
saved_position = self.position # Used by Boa Constructor
macroName, slots = self.popMacro()[:2]
slot = slots.get(slotName)
if slot is not None:
self.interpret(slot)
if self.sourceFile != saved_source:
self.engine.setSourceFile(saved_source)
self.sourceFile = saved_source
self.pushMacro(macroName, slots, entering=0)
return
self.pushMacro(macroName, slots)
......@@ -553,16 +569,16 @@ class TALInterpreter:
self._stream_write = stream.write
try:
self.interpret(block)
except self.TALESError, err:
except:
exc = sys.exc_info()[1]
self.restoreState(state)
engine = self.engine
engine.beginScope()
err.lineno, err.offset = self.position
engine.setLocal('error', err)
error = engine.createErrorInfo(exc, self.position)
engine.setLocal('error', error)
try:
self.interpret(handler)
finally:
err.takeTraceback()
engine.endScope()
else:
self.restoreOutputState(state)
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -14,7 +15,6 @@
Parse XML and compile to TALInterpreter intermediate code.
"""
import string
from XMLParser import XMLParser
from TALDefs import *
from TALGenerator import TALGenerator
......@@ -99,7 +99,7 @@ class TALParser(XMLParser):
def fixname(self, name):
if ' ' in name:
uri, name = string.split(name, ' ')
uri, name = name.split(' ')
prefix = self.nsDict[uri]
prefixed = name
if prefix:
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......
"""Empty file to make this directory a Python package."""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
#
##############################################################################
""" Template Attribute Language package """
#! /usr/bin/env python1.5
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -17,7 +18,6 @@ Driver program to test METAL and TAL implementation.
import os
import sys
import string
import getopt
......@@ -93,7 +93,7 @@ def compilefile(file, mode=None):
assert mode in ("html", "xml", None)
if mode is None:
ext = os.path.splitext(file)[1]
if string.lower(ext) in (".html", ".htm"):
if ext.lower() in (".html", ".htm"):
mode = "html"
else:
mode = "xml"
......
#! /usr/bin/env python1.5
#! /usr/bin/env python
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.
'''Run benchmarks of TAL vs. DTML'''
......
......@@ -97,7 +97,6 @@ __version__ = 1, 5, 0
# 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.
import string
TRACE = 0
# define what "junk" means
......
#! /usr/bin/env python1.5
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......@@ -17,7 +18,6 @@ Driver program to run METAL and TAL regression tests.
import sys
import os
import string
from cStringIO import StringIO
import glob
import traceback
......@@ -57,7 +57,7 @@ def main():
if args and args[0] == "-Q":
unittesting = 1
del args[0]
while args and args[0][:1] == '-':
while args and args[0].startswith('-'):
opts.append(args[0])
del args[0]
if not args:
......@@ -76,12 +76,12 @@ def main():
errors = 0
for arg in args:
locopts = []
if string.find(arg, "metal") >= 0 and "-m" not in opts:
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[-4:] == ".xml":
if tests.utils.skipxml and arg.endswith(".xml"):
print "SKIPPED (XML parser not available)"
continue
save = sys.stdout, sys.argv
......@@ -109,7 +109,7 @@ def main():
continue
head, tail = os.path.split(arg)
outfile = os.path.join(
string.replace(head, "input", "output"),
head.replace("input", "output"),
tail)
try:
f = open(outfile)
......
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.
"""
Read a module search path from .path.
"""
import os
import sys
import string
dir = os.path.dirname(__file__)
path = os.path.join(dir, ".path")
......@@ -13,7 +19,7 @@ except IOError:
raise IOError, "Please edit .path to point to <Zope2/lib/python>"
else:
for line in f.readlines():
line = string.strip(line)
line = line.strip()
if line and line[0] != '#':
for dir in string.split(line, os.pathsep):
dir = os.path.expanduser(os.path.expandvars(dir))
......
......@@ -8,6 +8,7 @@ import test_htmlparser
import test_htmltalparser
import test_talinterpreter
import test_files
import test_sourcepos
def test_suite():
suite = unittest.TestSuite()
......@@ -18,6 +19,7 @@ def test_suite():
suite.addTest(test_xmlparser.test_suite())
suite.addTest(test_talinterpreter.test_suite())
suite.addTest(test_files.test_suite())
suite.addTest(test_sourcepos.test_suite())
return suite
def main():
......
#! /usr/bin/env python
"""Tests for TALInterpreter."""
import sys
import unittest
from StringIO import StringIO
from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALInterpreter import TALInterpreter
from TAL.TALGenerator import TALGenerator
from TAL.DummyEngine import DummyEngine
page1 = '''<html metal:use-macro="main"><body>
<div metal:fill-slot="body">
page1=<span tal:replace="position:" />
</div>
</body></html>'''
main_template = '''<html metal:define-macro="main"><body>
main_template=<span tal:replace="position:" />
<div metal:define-slot="body" />
main_template=<span tal:replace="position:" />
<div metal:use-macro="foot" />
main_template=<span tal:replace="position:" />
</body></html>'''
footer = '''<div metal:define-macro="foot">
footer=<span tal:replace="position:" />
</div>'''
expected = '''<html><body>
main_template=main_template (2,14)
<div>
page1=page1 (3,6)
</div>
main_template=main_template (4,14)
<div>
footer=footer (2,7)
</div>
main_template=main_template (6,14)
</body></html>'''
class Tests(unittest.TestCase):
def parse(self, eng, s, fn):
gen = TALGenerator(expressionCompiler=eng, xml=0, source_file=fn)
parser = HTMLTALParser(gen)
parser.parseString(s)
program, macros = parser.getCode()
return program, macros
def testSourcePositions(self):
"""Ensure source file and position are set correctly by TAL"""
macros = {}
eng = DummyEngine(macros)
page1_program, page1_macros = self.parse(eng, page1, 'page1')
main_template_program, main_template_macros = self.parse(
eng, main_template, 'main_template')
footer_program, footer_macros = self.parse(eng, footer, 'footer')
macros['main'] = main_template_macros['main']
macros['foot'] = footer_macros['foot']
stream = StringIO()
interp = TALInterpreter(page1_program, macros, eng, stream)
interp()
self.assertEqual(stream.getvalue().strip(), expected.strip(),
stream.getvalue())
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(Tests))
return suite
if __name__ == "__main__":
unittest.main()
#! /usr/bin/env python1.5
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
......
......@@ -92,9 +92,9 @@ def stupid_log_write(subsystem, severity, summary, detail, error):
if error:
try:
_stupid_dest.write(format_exception(
_stupid_dest.write(''.join(format_exception(
error[0], error[1], error[2],
trailer='\n', limit=100))
limit=100)))
except:
_stupid_dest.write("%s: %s\n" % error[:2])
_stupid_dest.flush()
......
......@@ -11,7 +11,7 @@
#
##############################################################################
__version__='$Revision: 1.63 $'[11:-2]
__version__='$Revision: 1.64 $'[11:-2]
import re, sys, os, urllib, time, whrandom, cgi, codecs
from BaseRequest import BaseRequest
......@@ -934,7 +934,8 @@ class HTTPRequest(BaseRequest):
result=result + row % (escape(k), escape(repr(v)))
return result+"</table>"
__repr__=__str__
def __repr__(self):
return "<%s, URL=%s>" % (self.__class__.__name__, self['URL'])
def text(self):
result="FORM\n\n"
......
......@@ -12,17 +12,24 @@
##############################################################################
'''CGI Response Output formatter
$Id: HTTPResponse.py,v 1.55 2002/03/27 10:14:04 htrd Exp $'''
__version__='$Revision: 1.55 $'[11:-2]
$Id: HTTPResponse.py,v 1.56 2002/04/03 20:43:59 shane Exp $'''
__version__='$Revision: 1.56 $'[11:-2]
import types, os, sys, re
from string import translate, maketrans
from types import StringType, InstanceType, LongType, UnicodeType
from BaseResponse import BaseResponse
from zExceptions import Unauthorized
from zExceptions.ExceptionFormatter import format_exception
nl2sp=maketrans('\n',' ')
# Enable APPEND_TRACEBACKS to make Zope append tracebacks like it used to,
# but a better solution is to make standard_error_message display error_tb.
APPEND_TRACEBACKS = 0
status_reasons={
100: 'Continue',
101: 'Switching Protocols',
......@@ -92,16 +99,6 @@ start_of_header_search=re.compile('(<head[^>]*>)', re.IGNORECASE).search
accumulate_header={'set-cookie': 1}.has_key
tb_style = os.environ.get('HTTP_TRACEBACK_STYLE', '').lower()
if tb_style == 'none':
tb_delims = None, None
elif tb_style == 'js':
tb_delims = ('''<pre onclick="this.firstChild.data=this.lastChild.data">
&sect;<!--''', '--></pre>')
elif tb_style == 'plain':
tb_delims = '<pre>', '</pre>'
else:
tb_delims = '<!--', '-->'
class HTTPResponse(BaseResponse):
"""\
......@@ -384,8 +381,15 @@ class HTTPResponse(BaseResponse):
else: h=value
self.setHeader(name,h)
def isHTML(self,str):
return str.strip().lower()[:6] == '<html>' or str.find('</') > 0
def isHTML(self, s):
s = s.lstrip()
# Note that the string can be big, so s.lower().startswith() is more
# expensive than s[:n].lower().
if (s[:6].lower() == '<html>' or s[:14].lower() == '<!doctype html'):
return 1
if s.find('</') > 0:
return 1
return 0
def quoteHTML(self,text,
subs={'&':'&amp;', "<":'&lt;', ">":'&gt;', '\"':'&quot;'}
......@@ -397,42 +401,9 @@ class HTTPResponse(BaseResponse):
return text
def format_exception(self, etype, value, tb, limit=None):
import traceback
result=['Traceback (innermost last):']
if limit is None:
if hasattr(sys, 'tracebacklimit'):
limit = sys.tracebacklimit
n = 0
while tb is not None and (limit is None or n < limit):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals=f.f_locals
result.append(' File %s, line %d, in %s'
% (filename,lineno,name))
try: result.append(' (Object: %s)' %
locals[co.co_varnames[0]].__name__)
except: pass
try: result.append(' (Info: %s)' %
str(locals['__traceback_info__']))
except: pass
tb = tb.tb_next
n = n + 1
result.append(' '.join(traceback.format_exception_only(etype, value)))
return result
def _traceback(self, t, v, tb):
tb = self.format_exception(t, v, tb, 200)
tb = '\n'.join(tb)
tb = self.quoteHTML(tb)
if self.debug_mode: _tbopen, _tbclose = '<PRE>', '</PRE>'
else: _tbopen, _tbclose = tb_delims
if _tbopen is None:
return ''
return "\n%s\n%s\n%s" % (_tbopen, tb, _tbclose)
def _traceback(self, t, v, tb, as_html=1):
tb = format_exception(t, v, tb, as_html=as_html)
return '\n'.join(tb)
def redirect(self, location, status=302, lock=0):
"""Cause a redirection without raising an error"""
......@@ -624,14 +595,16 @@ class HTTPResponse(BaseResponse):
'Sorry, a site error occurred.<p>'
+ self._traceback(t, v, tb)),
is_error=1)
elif b.strip().lower()[:6]=='<html>' or \
b.strip().lower()[:14]=='<!doctype html':
elif self.isHTML(b):
# error is an HTML document, not just a snippet of html
body = self.setBody(b + self._traceback(t, '(see above)', tb),
is_error=1)
if APPEND_TRACEBACKS:
body = self.setBody(b + self._traceback(
t, '(see above)', tb), is_error=1)
else:
body = self.setBody(b, is_error=1)
else:
body = self.setBody((str(t),
b + self._traceback(t,'(see above)', tb)),
body = self.setBody(
(str(t), b + self._traceback(t,'(see above)', tb, 0)),
is_error=1)
del tb
return body
......
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""An exception formatter that shows traceback supplements and traceback info,
optionally in HTML.
$Id: ExceptionFormatter.py,v 1.2 2002/04/03 20:43:59 shane Exp $
"""
import sys
import cgi
DEBUG_EXCEPTION_FORMATTER = 1
class TextExceptionFormatter:
line_sep = '\n'
show_revisions = 0
def __init__(self, limit=None):
self.limit = limit
def escape(self, s):
return s
def getPrefix(self):
return 'Traceback (innermost last):'
def getLimit(self):
limit = self.limit
if limit is None:
limit = getattr(sys, 'tracebacklimit', None)
return limit
def getRevision(self, globals):
if not self.show_revisions:
return None
revision = globals.get('__revision__', None)
if revision is None:
# Incorrect but commonly used spelling
revision = globals.get('__version__', None)
if revision is not None:
try:
revision = str(revision).strip()
except:
revision = '???'
return revision
def formatSupplementLine(self, line):
return ' - %s' % line
def formatObject(self, object):
return [self.formatSupplementLine(repr(object))]
def formatSourceURL(self, url):
return [self.formatSupplementLine('URL: %s' % url)]
def formatSupplement(self, supplement, tb):
result = []
fmtLine = self.formatSupplementLine
object = getattr(supplement, 'object', None)
if object is not None:
result.extend(self.formatObject(object))
url = getattr(supplement, 'source_url', None)
if url is not None:
result.extend(self.formatSourceURL(url))
line = getattr(supplement, 'line', 0)
if line == -1:
line = tb.tb_lineno
col = getattr(supplement, 'column', -1)
if line:
if col is not None and col >= 0:
result.append(fmtLine('Line %s, Column %s' % (
line, col)))
else:
result.append(fmtLine('Line %s' % line))
elif col is not None and col >= 0:
result.append(fmtLine('Column %s' % col))
expr = getattr(supplement, 'expression', None)
if expr:
result.append(fmtLine('Expression: %s' % expr))
warnings = getattr(supplement, 'warnings', None)
if warnings:
for warning in warnings:
result.append(fmtLine('Warning: %s' % warning))
extra = self.formatExtraInfo(supplement)
if extra:
result.append(extra)
return result
def formatExtraInfo(self, supplement):
getInfo = getattr(supplement, 'getInfo', None)
if getInfo is not None:
extra = getInfo()
if extra:
return extra
return None
def formatTracebackInfo(self, tbi):
return self.formatSupplementLine('__traceback_info__: %s' % tbi)
def formatLine(self, tb):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals = f.f_locals
globals = f.f_globals
modname = globals.get('__name__', filename)
s = ' Module %s, line %d' % (modname, lineno)
revision = self.getRevision(globals)
if revision:
s = s + ', rev. %s' % revision
s = s + ', in %s' % name
result = []
result.append(self.escape(s))
# Output a traceback supplement, if any.
if locals.has_key('__traceback_supplement__'):
# Use the supplement defined in the function.
tbs = locals['__traceback_supplement__']
elif globals.has_key('__traceback_supplement__'):
# Use the supplement defined in the module.
# This is used by Scripts (Python).
tbs = globals['__traceback_supplement__']
else:
tbs = None
if tbs is not None:
factory = tbs[0]
args = tbs[1:]
try:
supp = factory(*args)
result.extend(self.formatSupplement(supp, tb))
except:
if DEBUG_EXCEPTION_FORMATTER:
import traceback
traceback.print_exc()
# else just swallow the exception.
try:
tbi = locals.get('__traceback_info__', None)
if tbi is not None:
result.append(self.formatTracebackInfo(tbi))
except:
pass
return self.line_sep.join(result)
def formatExceptionOnly(self, etype, value):
import traceback
return self.line_sep.join(
traceback.format_exception_only(etype, value))
def formatLastLine(self, exc_line):
return self.escape(exc_line)
def formatException(self, etype, value, tb, limit=None):
# The next line provides a way to detect recursion.
__exception_formatter__ = 1
result = [self.getPrefix() + '\n']
if limit is None:
limit = self.getLimit()
n = 0
while tb is not None and (limit is None or n < limit):
if tb.tb_frame.f_locals.get('__exception_formatter__'):
# Stop recursion.
result.append('(Recursive formatException() stopped)\n')
break
line = self.formatLine(tb)
result.append(line + '\n')
tb = tb.tb_next
n = n + 1
exc_line = self.formatExceptionOnly(etype, value)
result.append(self.formatLastLine(exc_line))
return result
class HTMLExceptionFormatter (TextExceptionFormatter):
line_sep = '<br />\r\n'
def escape(self, s):
return cgi.escape(s)
def getPrefix(self):
return '<p>Traceback (innermost last):\r\n<ul>'
def formatSupplementLine(self, line):
return '<b>%s</b>' % self.escape(str(line))
def formatTracebackInfo(self, tbi):
s = self.escape(str(tbi))
s = s.replace('\n', self.line_sep)
return '__traceback_info__: %s' % s
def formatLine(self, tb):
line = TextExceptionFormatter.formatLine(self, tb)
return '<li>%s</li>' % line
def formatLastLine(self, exc_line):
return '</ul>%s</p>' % self.escape(exc_line)
def formatExtraInfo(self, supplement):
getInfo = getattr(supplement, 'getInfo', None)
if getInfo is not None:
extra = getInfo(1)
if extra:
return extra
return None
limit = 200
if hasattr(sys, 'tracebacklimit'):
limit = min(limit, sys.tracebacklimit)
text_formatter = TextExceptionFormatter(limit)
html_formatter = HTMLExceptionFormatter(limit)
def format_exception(t, v, tb, limit=None, as_html=0):
if as_html:
fmt = html_formatter
else:
fmt = text_formatter
return fmt.formatException(t, v, tb, limit=limit)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""ITracebackSupplement interface definition.
$Id: ITracebackSupplement.py,v 1.2 2002/04/03 20:44:00 shane Exp $
"""
from Interface import Interface
from Interface.Attribute import Attribute
class ITracebackSupplement(Interface):
"""Provides valuable information to supplement an exception traceback.
The interface is geared toward providing meaningful feedback when
exceptions occur in user code written in mini-languages like
Zope page templates and restricted Python scripts.
"""
source_url = Attribute(
'source_url',
"""Optional. Set to URL of the script where the exception occurred.
Normally this generates a URL in the traceback that the user
can visit to manage the object. Set to None if unknown or
not available.
"""
)
object = Attribute(
'object',
"""Optional. Set to the script or template where the exception
occurred.
Set to None if unknown or not available.
"""
)
line = Attribute(
'line',
"""Optional. Set to the line number (>=1) where the exception
occurred.
Set to 0 or None if the line number is unknown.
"""
)
column = Attribute(
'column',
"""Optional. Set to the column offset (>=0) where the exception
occurred.
Set to None if the column number is unknown.
"""
)
expression = Attribute(
'expression',
"""Optional. Set to the expression that was being evaluated.
Set to None if not available or not applicable.
"""
)
warnings = Attribute(
'warnings',
"""Optional. Set to a sequence of warning messages.
Set to None if not available, not applicable, or if the exception
itself provides enough information.
"""
)
def getInfo(as_html=0):
"""Optional. Returns a string containing any other useful info.
If as_html is set, the implementation must HTML-quote the result
(normally using cgi.escape()). Returns None to provide no
extra info.
"""
"""Package for zExceptions tests"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""
ExceptionFormatter tests.
Revision information:
$Id: testExceptionFormatter.py,v 1.2 2002/04/03 20:44:00 shane Exp $
"""
from __future__ import nested_scopes
from unittest import TestCase, TestSuite, main, makeSuite
try:
from Zope.Testing.CleanUp import CleanUp # Base class w registry cleanup
except ImportError:
class CleanUp:
pass
import sys
from zExceptions.ExceptionFormatter import format_exception
def tb(as_html=0):
t, v, b = sys.exc_info()
try:
return ''.join(format_exception(t, v, b, as_html=as_html))
finally:
del b
class ExceptionForTesting (Exception):
pass
class TestingTracebackSupplement:
source_url = '/somepath'
line = 634
column = 57
warnings = ['Repent, for the end is nigh']
def __init__(self, expression):
self.expression = expression
class Test(CleanUp, TestCase):
def testBasicNamesText(self, as_html=0):
try:
raise ExceptionForTesting
except ExceptionForTesting:
s = tb(as_html)
# The traceback should include the name of this function.
self.assert_(s.find('testBasicNamesText') >= 0)
# The traceback should include the name of the exception.
self.assert_(s.find('ExceptionForTesting') >= 0)
else:
self.fail('no exception occurred')
def testBasicNamesHTML(self):
self.testBasicNamesText(1)
def testSupplement(self, as_html=0):
try:
__traceback_supplement__ = (TestingTracebackSupplement,
"You're one in a million")
raise ExceptionForTesting
except ExceptionForTesting:
s = tb(as_html)
# The source URL
self.assert_(s.find('/somepath') >= 0, s)
# The line number
self.assert_(s.find('634') >= 0, s)
# The column number
self.assert_(s.find('57') >= 0, s)
# The expression
self.assert_(s.find("You're one in a million") >= 0, s)
# The warning
self.assert_(s.find("Repent, for the end is nigh") >= 0, s)
else:
self.fail('no exception occurred')
def testSupplementHTML(self):
self.testSupplement(1)
def testTracebackInfo(self, as_html=0):
try:
__traceback_info__ = "Adam & Eve"
raise ExceptionForTesting
except ExceptionForTesting:
s = tb(as_html)
if as_html:
# Be sure quoting is happening.
self.assert_(s.find('Adam &amp; Eve') >= 0, s)
else:
self.assert_(s.find('Adam & Eve') >= 0, s)
else:
self.fail('no exception occurred')
def testTracebackInfoHTML(self):
self.testTracebackInfo(1)
def testMultipleLevels(self):
# Makes sure many levels are shown in a traceback.
def f(n):
"""Produces a (n + 1)-level traceback."""
__traceback_info__ = 'level%d' % n
if n > 0:
f(n - 1)
else:
raise ExceptionForTesting
try:
f(10)
except ExceptionForTesting:
s = tb()
for n in range(11):
self.assert_(s.find('level%d' % n) >= 0, s)
else:
self.fail('no exception occurred')
def testQuoteLastLine(self):
class C: pass
try: raise TypeError, C()
except:
s = tb(1)
else:
self.fail('no exception occurred')
self.assert_(s.find('&lt;') >= 0, s)
self.assert_(s.find('&gt;') >= 0, s)
def test_suite():
return TestSuite((
makeSuite(Test),
))
if __name__=='__main__':
main(defaultTest='test_suite')
......@@ -11,43 +11,11 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__version__='$Revision: 1.5 $'[11:-2]
__version__='$Revision: 1.6 $'[11:-2]
import sys
format_exception_only = None
def format_exception(etype, value, tb, limit=None, delimiter='\n',
header='', trailer=''):
global format_exception_only
if format_exception_only is None:
import traceback
format_exception_only = traceback.format_exception_only
result=['Traceback (innermost last):']
if header: result.insert(0, header)
if limit is None:
if hasattr(sys, 'tracebacklimit'):
limit = sys.tracebacklimit
n = 0
while tb is not None and (limit is None or n < limit):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals = f.f_locals
result.append(' File %s, line %d, in %s'
% (filename, lineno, name))
try: result.append(' (Object: %s)' %
locals[co.co_varnames[0]].__name__)
except: pass
try: result.append(' (Info: %s)' %
str(locals['__traceback_info__']))
except: pass
tb = tb.tb_next
n = n+1
result.append(' '.join(format_exception_only(etype, value)))
if trailer: result.append(trailer)
return delimiter.join(result)
try:
# Use the exception formatter in zExceptions
from zExceptions.ExceptionFormatter import format_exception
except ImportError:
# Not available. Use the basic formatter.
from traceback import format_exception
......@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__version__='$Revision: 1.7 $'[11:-2]
__version__='$Revision: 1.8 $'[11:-2]
import os, sys, time
......@@ -87,8 +87,8 @@ class stupid_log_write:
if error:
try:
lines = format_exception(error[0], error[1], error[2],
trailer="\n", limit=100)
print >> _log_dest, lines
limit=100)
print >> _log_dest, ''.join(lines)
except:
print >> _log_dest, "%s: %s" % error[:2]
_log_dest.flush()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment