[Checkins] SVN: Sandbox/malthe/chameleon.core/ Added error handler which adds debugging information to exceptions.
Malthe Borch
mborch at gmail.com
Wed Nov 5 10:12:53 EST 2008
Log message for revision 92798:
Added error handler which adds debugging information to exceptions.
Changed:
U Sandbox/malthe/chameleon.core/CHANGES.txt
U Sandbox/malthe/chameleon.core/src/chameleon/core/generation.py
U Sandbox/malthe/chameleon.core/src/chameleon/core/template.py
U Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt
U Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py
-=-
Modified: Sandbox/malthe/chameleon.core/CHANGES.txt
===================================================================
--- Sandbox/malthe/chameleon.core/CHANGES.txt 2008-11-05 15:06:25 UTC (rev 92797)
+++ Sandbox/malthe/chameleon.core/CHANGES.txt 2008-11-05 15:12:47 UTC (rev 92798)
@@ -4,6 +4,9 @@
HEAD
~~~~
+- Added error handler which adds debugging information to the
+ exception object's string output (in debug-mode only). [malthe]
+
1.0b4 (released 28/10/2008)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/generation.py
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/generation.py 2008-11-05 15:06:25 UTC (rev 92797)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/generation.py 2008-11-05 15:12:47 UTC (rev 92798)
@@ -69,6 +69,8 @@
t_prefix = '_tmp'
v_prefix = '_tmpv'
+ annotation = None
+
def __init__(self, symbols=None, encoding=None,
indentation=0, indentation_string="\t"):
BufferIO.__init__(self)
@@ -110,7 +112,7 @@
self.indentation -= amount
def annotate(self, item):
- self.annotations[self.l_counter] = item
+ self.annotation = self.annotations[self.l_counter] = item
def out(self, string):
if isinstance(string, unicode) and self.encoding:
@@ -130,11 +132,19 @@
string = string.encode(self.encoding)
self.l_counter += len(string.split('\n'))-1
-
self.cook()
- BufferIO.write(self,
- self.indentation_string * self.indentation + string + '\n')
+
+ indent = self.indentation_string * self.indentation
+ # if a source code annotation is set, write it as a comment
+ # prior to the source code line
+ if self.annotation:
+ BufferIO.write(
+ self, "%s# %s\n" % (indent, self.annotation))
+ self.annotation = None
+
+ BufferIO.write(self, indent + string + '\n')
+
def getvalue(self):
self.cook()
return BufferIO.getvalue(self)
Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/template.py
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/template.py 2008-11-05 15:06:25 UTC (rev 92797)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/template.py 2008-11-05 15:12:47 UTC (rev 92798)
@@ -1,4 +1,7 @@
import os
+import sys
+import utils
+import pprint
import config
import doctypes
import filecache
@@ -60,8 +63,41 @@
def render(self, **kwargs):
template = self.cook_check(parameters=kwargs)
- return template.render(**kwargs)
+ if config.DEBUG_MODE is False:
+ return template.render(**kwargs)
+
+ try:
+ return template.render(**kwargs)
+ except:
+ tb = sys.exc_info()[-1]
+ lineno = tb.tb_next.tb_next.tb_lineno-1
+ del tb
+ # locate source code annotation (these are available from
+ # the template source as comments)
+ source = template.source.split('\n')
+ for i in reversed(range(lineno)):
+ if source[i].lstrip().startswith('#'):
+ annotation = source[i].split('#', 1)[-1].lstrip()
+ break
+ else:
+ annotation = ""
+
+ formatted_arguments = pprint.pformat(kwargs).split('\n')
+
+ # indent consecutive arguments for readability
+ for index, string in enumerate(formatted_arguments[1:]):
+ formatted_arguments[index+1] = " "*15 + string
+
+ utils.reraise(
+ sys.exc_info(),
+ ("Caught exception rendering template."
+ "\n\n"
+ " - Expression: %s\n"
+ " - Instance: %s\n"
+ " - Arguments: %s\n"
+ ) % (annotation, repr(self), "\n".join(formatted_arguments)))
+
def render_macro(self, macro, global_scope=False, parameters={}):
template = self.cook_check(
parameters=parameters, macro=macro, global_scope=global_scope)
Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt 2008-11-05 15:06:25 UTC (rev 92797)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt 2008-11-05 15:12:47 UTC (rev 92798)
@@ -66,3 +66,61 @@
<span>Hello, world!</span>
</div>
</div>
+
+Error handling
+--------------
+
+When an exception is raised during rendering, the exception is
+augmented with information useful for debugging.
+
+Note that the compiler must run in debug-mode to get this
+functionality.
+
+ >>> from chameleon.core import config
+ >>> config.DEBUG_MODE = True
+
+To test the error handler, we'll try to call a symbol `dummy`; for
+technical reasons, we must wrap this in an encoded pickle---this is
+due to our test setup.
+
+ >>> from chameleon.core.types import value
+ >>> from base64 import encodestring
+ >>> from cPickle import dumps
+
+ >>> template = Template("""\
+ ... <div meta:replace="%s" />""" % encodestring(dumps(value('dummy()'))),
+ ... mock_parser)
+
+First, a simple example where a ``NameError`` exception is raised in a
+function which we'll call from inside the template.
+
+ >>> def dummy():
+ ... print i
+
+ >>> print template(dummy=dummy)
+ Traceback (most recent call last):
+ ...
+ print i
+ ...
+ RuntimeError: Caught exception rendering template.
+ ...
+ dummy()
+ ...
+ NameError: ...
+
+Note that the exception is a copy of the original exception, with a
+customized ``__str__`` method.
+
+ >>> class CustomException(Exception):
+ ... def __init__(self, msg):
+ ... self.msg = msg
+
+ >>> def dummy():
+ ... raise CustomException("This is a custom error message.")
+
+ >>> try:
+ ... print template(dummy=dummy)
+ ... except CustomException, exc:
+ ... print exc.msg
+ This is a custom error message.
+
Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py 2008-11-05 15:06:25 UTC (rev 92797)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py 2008-11-05 15:12:47 UTC (rev 92798)
@@ -253,3 +253,23 @@
def py_attr(name):
return "{%s}%s" % (config.PY_NS, name)
+
+def reraise(exc_info=(None, None, None), error_msg=""):
+ """Re-raise the latest exception given by ``exc_info`` tuple (see
+ ``sys.exc_info``) with an additional ``error_msg`` text."""
+
+ cls, exc, tb = exc_info
+
+ __dict__ = exc.__dict__
+ error_string = str(exc)
+
+ if issubclass(cls, Exception):
+ class RuntimeError(cls):
+ def __str__(self):
+ return "%s\n%s: %s" % (
+ error_msg, cls.__name__, error_string)
+
+ exc = RuntimeError.__new__(RuntimeError)
+ exc.__dict__.update(__dict__)
+
+ raise cls, exc, tb
More information about the Checkins
mailing list