From 936a524263b90071d601b09a63f3c86117769b61 Mon Sep 17 00:00:00 2001
From: Mike Bayer <mike_mp@zzzcomputing.com>
Date: Thu, 8 Mar 2012 16:02:05 -0800
Subject: [PATCH] - [bug] Fixed endless recursion bug when   nesting multiple
 def-calls with content.   Thanks to Jeff Dairiki. [ticket:186]

---
 CHANGES           |  4 ++++
 mako/codegen.py   |  8 ++++----
 mako/runtime.py   |  6 +++++-
 test/test_call.py | 11 +++++++++++
 4 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/CHANGES b/CHANGES
index 4783699..2d3dabe 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,10 @@
   to filehandles being implicitly closed.
   [ticket:182]
 
+- [bug] Fixed endless recursion bug when 
+  nesting multiple def-calls with content.
+  Thanks to Jeff Dairiki. [ticket:186]
+
 0.6.2
 - [bug] The ${{"foo":"bar"}} parsing issue is fixed!!
   The legendary Eevee has slain the dragon!
diff --git a/mako/codegen.py b/mako/codegen.py
index 2e15124..704330c 100644
--- a/mako/codegen.py
+++ b/mako/codegen.py
@@ -231,7 +231,8 @@ class _GenerateRenderMethod(object):
  
         self.printer.writelines(
             "def %s(%s):" % (name, ','.join(args)),
-                "context.caller_stack._push_frame()",
+                # push new frame, assign current frame to __M_caller
+                "__M_caller = context.caller_stack._push_frame()",
                 "try:"
         )
         if buffered or filtered or cached:
@@ -516,7 +517,8 @@ class _GenerateRenderMethod(object):
         buffered = eval(node.attributes.get('buffered', 'False'))
         cached = eval(node.attributes.get('cached', 'False'))
         self.printer.writelines(
-            "context.caller_stack._push_frame()",
+            # push new frame, assign current frame to __M_caller
+            "__M_caller = context.caller_stack._push_frame()",
             "try:"
             )
         if buffered or filtered or cached:
@@ -848,8 +850,6 @@ class _GenerateRenderMethod(object):
         )
 
         self.printer.writelines(
-            # get local reference to current caller, if any
-            "__M_caller = context.caller_stack._get_caller()",
             # push on caller for nested call
             "context.caller_stack.nextcaller = "
                 "runtime.Namespace('caller', context, callables=ccall(__M_caller))",
diff --git a/mako/runtime.py b/mako/runtime.py
index 65c03e1..b56fa67 100644
--- a/mako/runtime.py
+++ b/mako/runtime.py
@@ -158,12 +158,16 @@ class CallerStack(list):
     def __nonzero__(self):
         return self._get_caller() and True or False
     def _get_caller(self):
+        # this method can be removed once
+        # codegen MAGIC_NUMBER moves past 7
         return self[-1]
     def __getattr__(self, key):
         return getattr(self._get_caller(), key)
     def _push_frame(self):
-        self.append(self.nextcaller or None)
+        frame = self.nextcaller or None
+        self.append(frame)
         self.nextcaller = None
+        return frame
     def _pop_frame(self):
         self.nextcaller = self.pop()
  
diff --git a/test/test_call.py b/test/test_call.py
index 5f13e95..0bb6079 100644
--- a/test/test_call.py
+++ b/test/test_call.py
@@ -385,6 +385,17 @@ class CallTest(TemplateTest):
 """)
         assert result_lines(t.render()) == ['this is a', 'this is b', 'this is c:', "this is the body in b's call"]
 
+    def test_composed_def(self):
+        t = Template("""
+            <%def name="f()"><f>${caller.body()}</f></%def>
+            <%def name="g()"><g>${caller.body()}</g></%def>
+            <%def name="fg()">
+                <%self:f><%self:g>${caller.body()}</%self:g></%self:f>
+            </%def>
+            <%self:fg>fgbody</%self:fg>
+            """)
+        assert result_lines(t.render()) == ['<f><g>fgbody</g></f>']
+
     def test_regular_defs(self):
         t = Template("""
         <%!
-- 
GitLab