Started work on support for transparent proxies for the debug hack
authorArmin Ronacher <armin.ronacher@active-4.com>
Mon, 29 Nov 2010 09:50:34 +0000 (10:50 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Mon, 29 Nov 2010 09:50:34 +0000 (10:50 +0100)
jinja2/debug.py

index eb15456d13e0f743fc9477fafeccf70b36ae234f..e8492cd6711a8ce9779c6eb3efa4aa4152868cd5 100644 (file)
 """
 import sys
 import traceback
+from types import TracebackType
 from jinja2.utils import CodeType, missing, internal_code
 from jinja2.exceptions import TemplateSyntaxError
 
+# on pypy we can take advantage of transparent proxies
+try:
+    from __pypy__ import tproxy
+except ImportError:
+    tproxy = None
+
 
 # how does the raise helper look like?
 try:
@@ -30,6 +37,7 @@ class TracebackFrameProxy(object):
 
     def __init__(self, tb):
         self.tb = tb
+        self._tb_next = None
 
     def _set_tb_next(self, next):
         if tb_set_next is not None:
@@ -50,6 +58,15 @@ class TracebackFrameProxy(object):
         return getattr(self.tb, name)
 
 
+def make_frame_proxy(frame):
+    proxy = TracebackFrameProxy(frame)
+    if tproxy is None:
+        return proxy
+    def operation_handler(operation, *args, **kwargs):
+        return getattr(proxy, operation)(*args, **kwargs)
+    return tproxy(TracebackType, operation_handler)
+
+
 class ProcessedTraceback(object):
     """Holds a Jinja preprocessed traceback for priting or reraising."""
 
@@ -59,8 +76,7 @@ class ProcessedTraceback(object):
         self.exc_value = exc_value
         self.frames = frames
 
-    def chain_frames(self):
-        """Chains the frames.  Requires ctypes or the debugsupport extension."""
+        # newly concatenate the frames (which are proxies)
         prev_tb = None
         for tb in self.frames:
             if prev_tb is not None:
@@ -95,7 +111,12 @@ class ProcessedTraceback(object):
     @property
     def standard_exc_info(self):
         """Standard python exc_info for re-raising"""
-        return self.exc_type, self.exc_value, self.frames[0].tb
+        tb = self.frames[0]
+        # the frame will be an actual traceback (or transparent proxy) if
+        # we are on pypy or a python implementation with support for tproxy
+        if type(tb) is not TracebackType:
+            tb = tb.tb
+        return self.exc_type, self.exc_value, tb
 
 
 def make_traceback(exc_info, source_hint=None):
@@ -152,7 +173,7 @@ def translate_exception(exc_info, initial_skip=0):
             tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
                                lineno)[2]
 
-        frames.append(TracebackFrameProxy(tb))
+        frames.append(make_frame_proxy(tb))
         tb = next
 
     # if we don't have any exceptions in the frames left, we have to
@@ -161,10 +182,7 @@ def translate_exception(exc_info, initial_skip=0):
     if not frames:
         raise exc_info[0], exc_info[1], exc_info[2]
 
-    traceback = ProcessedTraceback(exc_info[0], exc_info[1], frames)
-    if tb_set_next is not None:
-        traceback.chain_frames()
-    return traceback
+    return ProcessedTraceback(exc_info[0], exc_info[1], frames)
 
 
 def fake_exc_info(exc_info, filename, lineno):
@@ -239,7 +257,8 @@ def fake_exc_info(exc_info, filename, lineno):
 def _init_ugly_crap():
     """This function implements a few ugly things so that we can patch the
     traceback objects.  The function returned allows resetting `tb_next` on
-    any python traceback object.
+    any python traceback object.  Do not attempt to use this on non cpython
+    interpreters
     """
     import ctypes
     from types import TracebackType
@@ -297,12 +316,15 @@ def _init_ugly_crap():
     return tb_set_next
 
 
-# try to get a tb_set_next implementation
-try:
-    from jinja2._debugsupport import tb_set_next
-except ImportError:
+# try to get a tb_set_next implementation if we don't have transparent
+# proxies.
+tb_set_next = None
+if tproxy is None:
     try:
-        tb_set_next = _init_ugly_crap()
-    except:
-        tb_set_next = None
-del _init_ugly_crap
+        from jinja2._debugsupport import tb_set_next
+    except ImportError:
+        try:
+            tb_set_next = _init_ugly_crap()
+        except:
+            pass
+    del _init_ugly_crap