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