looking for a jinja memleak
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 10 Nov 2007 12:47:56 +0000 (13:47 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 10 Nov 2007 12:47:56 +0000 (13:47 +0100)
--HG--
branch : trunk

jdebug.py
jinja/parser.py
jinja/translators/python.py

index 5c4f7818ffd730341ea0110c85e1f7228e3f7440..219c876c65e4e5d55025ba48b6c201944d62bffe 100644 (file)
--- a/jdebug.py
+++ b/jdebug.py
@@ -19,21 +19,12 @@ from jinja.translators.python import PythonTranslator
 
 __all__ = ['e', 't', 'p', 'l', 'm']
 
+
+_global_frame = sys._getframe()
 e = Environment()
 t = e.from_string
 
 
-if os.environ.get('JDEBUG_SOURCEPRINT'):
-    original_translate = PythonTranslator.translate
-
-    def debug_translate(self):
-        rv = original_translate(self)
-        sys.stderr.write('## GENERATED SOURCE:\n%s\n' % rv)
-        return rv
-
-    PythonTranslator.translate = debug_translate
-
-
 def p(x=None, f=None):
     if x is None and f is not None:
         x = e.loader.get_source(f)
@@ -53,17 +44,15 @@ class MemoryGuard(object):
 
     def freeze(self):
         self.clear()
-        self.update()
-
-    def update(self):
-        self.guarded_objects.clear()
         for obj in gc.get_objects():
             self.guarded_objects[id(obj)] = True
 
     def get_delta(self):
+        frm = sys._getframe()
         result = []
         for obj in gc.get_objects():
-            if id(obj) not in self.guarded_objects:
+            if id(obj) not in self.guarded_objects and \
+               obj is not frm and obj is not result:
                 result.append(obj)
         return result
 
index 44ea95401fc46b5b8a0c027ebab9425b51f141c5..40dccb29dba388f8317c91b89adeaed82bb99a97 100644 (file)
@@ -1117,6 +1117,35 @@ class Parser(object):
 
         return assemble_list()
 
+    def sanitize_tree(self, body, extends):
+        self._sanitize_tree([body], [body], extends, body)
+        return body
+
+    def _sanitize_tree(self, nodelist, stack, extends, body):
+        """
+        This is not a closure because python leaks memory if it is.  It's used
+        by `parse()` to make sure blocks do not trigger unexpected behavior.
+        """
+        for node in nodelist:
+            if extends is not None and \
+                 node.__class__ is nodes.Block and \
+                 stack[-1] is not body:
+                for n in stack:
+                    if n.__class__ is nodes.Block:
+                        break
+                else:
+                    raise TemplateSyntaxError('misplaced block %r, '
+                                              'blocks in child '
+                                              'templates must be '
+                                              'either top level or '
+                                              'located in a block '
+                                              'tag.' % node.name,
+                                              node.lineno,
+                                              self.filename)
+            stack.append(node)
+            self._sanitize_tree(node.get_child_nodes(), stack, extends, body)
+            stack.pop()
+
     def parse(self):
         """
         Parse the template and return a Template node. This also does some
@@ -1142,28 +1171,7 @@ class Parser(object):
                 if leading_whitespace:
                     self.stream.shift(leading_whitespace)
 
-            body = self.subparse(None)
-            def walk(nodelist, stack):
-                for node in nodelist:
-                    if extends is not None and \
-                         node.__class__ is nodes.Block and \
-                         stack[-1] is not body:
-                        for n in stack:
-                            if n.__class__ is nodes.Block:
-                                break
-                        else:
-                            raise TemplateSyntaxError('misplaced block %r, '
-                                                      'blocks in child '
-                                                      'templates must be '
-                                                      'either top level or '
-                                                      'located in a block '
-                                                      'tag.' % node.name,
-                                                      node.lineno,
-                                                      self.filename)
-                    stack.append(node)
-                    walk(node.get_child_nodes(), stack)
-                    stack.pop()
-            walk([body], [body])
+            body = self.sanitize_tree(self.subparse(None), extends)
             return nodes.Template(extends, body, 1, self.filename)
         finally:
             self.close()
index f07fdc36e12adfa60cba84619f82e0c484c1be32..1f48a1114871a007ef72584c4f69c484d9eda516 100644 (file)
@@ -269,10 +269,9 @@ class PythonTranslator(Translator):
         if self.closed:
             raise RuntimeError('translator is closed')
         if node.__class__ in self.handlers:
-            out = self.handlers[node.__class__](node)
+            return self.handlers[node.__class__](node)
         else:
             raise AssertionError('unhandled node %r' % node.__class__)
-        return out
 
     def close(self):
         """