provide more context on compiler crashes during transforms
authorStefan Behnel <scoder@users.berlios.de>
Sat, 10 Jan 2009 14:32:22 +0000 (15:32 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Sat, 10 Jan 2009 14:32:22 +0000 (15:32 +0100)
Cython/Compiler/Errors.py
Cython/Compiler/Visitor.py

index c7c6cae99b2e83fee7e8141d05f5648f24c1f5b2..821ef01fc4260d83300452dafafa00245099f225 100644 (file)
@@ -63,7 +63,28 @@ class InternalError(Exception):
     def __init__(self, message):
         Exception.__init__(self, "Internal compiler error: %s"
             % message)
-            
+
+
+class CompilerCrash(CompileError):
+    # raised when an unexpected exception occurs in a transform
+    def __init__(self, pos, context, message, cause, stacktrace=None):
+        if message:
+            message = u'\n' + message
+        else:
+            message = u'\n'
+        if context:
+            message = "Compiler crash in " + context + message
+        if stacktrace:
+            import traceback, sys
+            message += (
+                u'\n\nCompiler crash traceback up to this point:\n' +
+                u''.join(traceback.format_tb(stacktrace)))
+        if cause:
+            if not stacktrace:
+                message += u'\n'
+            message += u'%s: %s' % (cause.__class__.__name__, cause)
+        CompileError.__init__(self, pos, message)
+
 
 listing_file = None
 num_errors = 0
index ad5b33ab93df0846dc315da11429d53b7bf3ff2e..72702026552d8dbb6e6a5cdb5116098713da01fc 100644 (file)
@@ -6,6 +6,7 @@ import inspect
 import Nodes
 import ExprNodes
 import Naming
+import Errors
 from StringEncoding import EncodedString
 
 class BasicVisitor(object):
@@ -91,9 +92,58 @@ class TreeVisitor(BasicVisitor):
         super(TreeVisitor, self).__init__()
         self.access_path = []
 
+    def dump_node(self, node, indent=0):
+        ignored = list(node.child_attrs) + [u'child_attrs', u'pos']
+        values = []
+        pos = node.pos
+        if pos:
+            source = pos[0]
+            if source:
+                import os.path
+                source = os.path.basename(source.get_description())
+            values.append(u'%s:%s:%s' % (source, pos[1], pos[2]))
+        attribute_names = dir(node)
+        attribute_names.sort()
+        for attr in attribute_names:
+            if attr in ignored:
+                continue
+            if attr.startswith(u'_') or attr.endswith(u'_'):
+                continue
+            value = getattr(node, attr, None)
+            if value is None:
+                continue
+            elif isinstance(value, list):
+                value = u'[...]'
+            elif not isinstance(value, (str, unicode, long, int, float)):
+                continue
+            else:
+                value = repr(value)
+            values.append(u'%s = %s' % (attr, value))
+        return u'%s(%s)' % (node.__class__.__name__,
+                           u',\n    '.join(values))
+
     def visitchild(self, child, parent, attrname, idx):
         self.access_path.append((parent, attrname, idx))
-        result = self.visit(child)
+        try:
+            result = self.visit(child)
+        except Errors.CompilerCrash:
+            raise
+        except Exception, e:
+            import sys
+            trace = ['']
+            for parent, attribute, index in self.access_path:
+                node = getattr(parent, attribute)
+                if index is None:
+                    index = ''
+                else:
+                    node = node[index]
+                    index = u'[%d]' % index
+                trace.append(u'%s.%s%s = %s' % (
+                    parent.__class__.__name__, attribute, index,
+                    self.dump_node(node)))
+            raise Errors.CompilerCrash(
+                node.pos, self.__class__.__name__,
+                u'\n'.join(trace), e, sys.exc_info()[2])
         self.access_path.pop()
         return result