From 9a2c39f845c7b3e2a8ad428b381466d20ad0f475 Mon Sep 17 00:00:00 2001
From: Mike Bayer <mike_mp@zzzcomputing.com>
Date: Sun, 19 Nov 2006 02:09:12 +0000
Subject: [PATCH] unit tests, error handling, interface, etc

---
 lib/mako/codegen.py   |  1 +
 lib/mako/parsetree.py |  7 +++--
 lib/mako/template.py  | 71 +++++++++++++++++++++++--------------------
 test/codegen.py       | 45 ---------------------------
 4 files changed, 44 insertions(+), 80 deletions(-)
 delete mode 100644 test/codegen.py

diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index 7aca819..8bfb1b9 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -45,6 +45,7 @@ class Compiler(object):
             render = _GenerateRenderMethod(printer, undeclared, name="render_%s" % node.name, args=node.function_decl.get_argument_expressions())
             for n in node.nodes:
                 n.accept_visitor(render)
+            printer.writeline("return ''")
             printer.writeline(None)
             buf.write("\n\n")
             
diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py
index c4cd88b..40e7807 100644
--- a/lib/mako/parsetree.py
+++ b/lib/mako/parsetree.py
@@ -171,10 +171,13 @@ class ComponentTag(Tag):
     __keyword__ = 'component'
     def __init__(self, keyword, attributes, **kwargs):
         super(ComponentTag, self).__init__(keyword, attributes, **kwargs)
-        self.function_decl = ast.FunctionDecl("def " + attributes['name'] + ":pass", self.lineno, self.pos)
+        name = attributes['name']
+        if re.match(r'^[\w_]+$',name):
+            name = name + "()"
+        self.function_decl = ast.FunctionDecl("def " + name + ":pass", self.lineno, self.pos)
         self.name = self.function_decl.funcname
     def declared_identifiers(self):
-        return [self.function_decl.funcname] + self.function_decl.argnames
+        return [self.function_decl.funcname] + list(self.function_decl.argnames)
     def undeclared_identifiers(self):
         res = []
         for c in self.function_decl.defaults:
diff --git a/lib/mako/template.py b/lib/mako/template.py
index 5266817..9df070f 100644
--- a/lib/mako/template.py
+++ b/lib/mako/template.py
@@ -1,4 +1,5 @@
-"""provides the Template class, a facade for parsing, generating and executing template strings."""
+"""provides the Template class, a facade for parsing, generating and executing template strings,
+as well as template runtime operations."""
 
 from mako.lexer import Lexer
 from mako.codegen import Compiler
@@ -34,38 +35,35 @@ class Template(object):
         """
         self.identifier = identifier or "memory:" + hex(id(self))
         if text is not None:
-            module = _compile_text(text, self.identifier, filename)
+            (code, module) = _compile_text(text, self.identifier, filename)
             _inmemory_templates[module.__name__] = self
+            self._code = code
             self._source = text
         else:
             self._source = None
+            self._code = None
         self.module = module
+        self.callable_ = self.module.render
         self.format_exceptions = format_exceptions
         self.error_handler = error_handler
         _modules[module.__name__] = _ModuleMarker(module)
 
-    def get_source(self):
-        if self._source is not None:
-            return self._source
-        else:
-            filename = self.module._template_filename
-            if not filename:
-                raise exceptions.RuntimeException("Cant get source code or template filename for template: %s" % self.identifier)
-            return file(filename).read()
-            
+    source = property(lambda self:_get_template_source(self.callable_), doc="""return the template source code for this Template.""")
+    code = property(lambda self:_get_module_source(self.callable_), doc="""return the module source code for this Template""")
+        
     def render(self, *args, **data):
         """render the output of this template as a string.
         
         a Context object is created corresponding to the given data.  Arguments that are explictly
         declared by this template's internal rendering method are also pulled from the given *args, **data 
         members."""
-        return _render(self, self.module.render, *args, **data)
+        return _render(self, self.callable_, *args, **data)
     
     def render_context(self, context, *args, **kwargs):
         """render this Template with the given context.  
         
         the data is written to the context's buffer."""
-        _render_context(self, self.module.render, context, *args, **kwargs)
+        _render_context(self, self.callable_, context, *args, **kwargs)
         
     def get_component(self, name):
         """return a component of this template as an individual Template of its own."""
@@ -76,30 +74,18 @@ class ComponentTemplate(Template):
     def __init__(self, parent, callable_):
         self.parent = parent
         self.callable_ = callable_
-    def render(self, *args, **data):
-        """render the output of this template as a string.
-        
-        a Context object is created corresponding to the given data.  Arguments that are explictly
-        declared by this template's internal rendering method are also pulled from the given *args, **data 
-        members."""
-        return _render(self.parent, self.callable_, *args, **data)
-
-    def render_context(self, context, *args, **kwargs):
-        """render this Template with the given context.  
+    def get_component(self, name):
+        return self.parent.get_component(name)
         
-        the data is written to the context's buffer."""
-        _render_context(self.parent, self.callable_, context, *args, **kwargs)
-
 def _compile_text(text, identifier, filename):
     node = Lexer(text).parse()
     source = Compiler(node).render()
-#    print source
     cid = identifier
     module = imp.new_module(cid)
     code = compile(source, filename or cid, 'exec')
     exec code in module.__dict__, module.__dict__
     module._modified_time = time.time()
-    return module
+    return (source, module)
     
 def _render(template, callable_, *args, **data):
     """given a Template and a callable_ from that template, create a Context and return the string output."""
@@ -135,8 +121,14 @@ def _exec_template(callable_, context, args=None, kwargs=None):
             e = sys.exc_info()[0]
             error = e
         if error:
-            source = _get_template_source(callable_)
-            raise error
+            if template.error_handler:
+                result = template.error_handler(context, error)
+                if not result:
+                    raise error
+            else:
+                # TODO
+                source = _get_template_source(callable_)
+                raise error
     else:
         callable_(context, *args, **kwargs)
     
@@ -145,12 +137,25 @@ def _get_template_source(callable_):
     name = callable_.func_globals['__name__']
     try:
         template = _inmemory_templates[name]
-        source = template._source
+        return template._source
     except KeyError:
         module = _modules[name].module
         filename = module._template_filename
         if filename is None:
             if not filename:
-                raise exceptions.RuntimeException("Cant get source code or template filename for template: %s" % self.identifier)
+                raise exceptions.RuntimeException("Cant get source code or template filename for template: %s" % name)
+        return file(filename).read()
+
+def _get_module_source(callable_):
+    name = callable_.func_globals['__name__']
+    try:
+        template = _inmemory_templates[name]
+        return template._code
+    except KeyError:
+        module = _modules[name].module
+        filename = module.__file__
+        if filename is None:
+            if not filename:
+                raise exceptions.RuntimeException("Cant get module source code or module filename for template: %s" % name)
         return file(filename).read()
-            
+                
diff --git a/test/codegen.py b/test/codegen.py
deleted file mode 100644
index 97e16be..0000000
--- a/test/codegen.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import unittest
-
-from mako.codegen import Compiler
-from mako.lexer import Lexer
-from mako import exceptions
-
-class CodeGenTest(unittest.TestCase):
-    def test_gen_1(self):
-        template = """
-<html>
-    <head>
-        <title>${title}</title>
-    </head>
-    <body>
-    <%
-        x = get_data()
-        log.debug("X is " + x)
-    %>
-    % for y in x:
-        <p>${y}</p>
-    % endfor
-
-<%!
-    from foo import get_data
-%>
-</html>        
-    
-"""
-        node = Lexer(template).parse()
-        code = Compiler(node).render()
-        print code
-
-    def test_gen_2(self):
-        template = """
-        <%component name="mycomp(x=5, y=7, *args, **kwargs)">
-            hello world
-        </%component>
- ${mycomp()}
-"""
-        node = Lexer(template).parse()
-        code = Compiler(node).render()
-        print code
-
-if __name__ == '__main__':
-    unittest.main()
-- 
GitLab