diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-12-04 00:39:38 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-12-04 00:39:38 +0000 |
commit | 6c1fd9395a569d41953d28b7b90349194480bb1d (patch) | |
tree | f145edf37f17168a0486269807ef69042f31359c | |
parent | 94bd81119d1ae63845ea117a369396b89627f3bb (diff) | |
download | mako-6c1fd9395a569d41953d28b7b90349194480bb1d.tar.gz |
some basic (memory only so far) caching
-rw-r--r-- | lib/mako/cache.py | 31 | ||||
-rw-r--r-- | lib/mako/codegen.py | 73 | ||||
-rw-r--r-- | lib/mako/ext/autohandler.py | 16 | ||||
-rw-r--r-- | lib/mako/parsetree.py | 4 | ||||
-rw-r--r-- | test/alltests.py | 4 | ||||
-rw-r--r-- | test/cache.py | 52 | ||||
-rw-r--r-- | test/call.py | 26 |
7 files changed, 178 insertions, 28 deletions
diff --git a/lib/mako/cache.py b/lib/mako/cache.py new file mode 100644 index 0000000..516c40d --- /dev/null +++ b/lib/mako/cache.py @@ -0,0 +1,31 @@ +import myghtyutils.container as container +try: + import myghtyutils.ext.memcached as memcached + clsmap = { + 'memory':container.MemoryContainer, + 'dbm':container.DBMContainer, + 'file':container.FileContainer, + 'memcached':memcached.MemcachedContainer + } +except ImportError: + clsmap = { + 'memory':container.MemoryContainer, + 'dbm':container.DBMContainer, + 'file':container.FileContainer, + } + +class Cache(object): + def __init__(self, id): + self.id = id + self.context = container.ContainerContext() + self._containers = {} + def put(self, key, value, type='memory', **kwargs): + self._get_container(key, type, **kwargs).set_value(value) + def get(self, key, type='memory', **kwargs): + return self._get_container(key, type, **kwargs).get_value() + def _get_container(self, key, type, **kwargs): + try: + return self._containers[key] + except KeyError: + return self._containers.setdefault(key, clsmap[type](key, self.context, self.id, **kwargs)) +
\ No newline at end of file diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py index b20f939..b895fe0 100644 --- a/lib/mako/codegen.py +++ b/lib/mako/codegen.py @@ -29,30 +29,29 @@ class _GenerateRenderMethod(object): self.last_source_line = -1 self.compiler = compiler self.node = node - - if isinstance(node, parsetree.DefTag): + + self.in_def = isinstance(node, parsetree.DefTag) + + if self.in_def: name = "render_" + node.name args = node.function_decl.get_argument_expressions() - self.in_def = True filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) + cached = eval(node.attributes.get('cached', 'False')) + defs = None else: + (pagetag, defs) = self.write_toplevel() name = "render" args = None - self.in_def = False buffered = filtered = False - + cached = pagetag is not None and eval(pagetag.attributes.get('cached', 'False')) if args is None: args = ['context', '**kwargs'] else: args = [a for a in ['context'] + args + ['**kwargs']] - if not self.in_def: - defs = self.write_toplevel() - else: - defs = None - self.write_render_callable(name, args, buffered, filtered) + self.write_render_callable(name, args, buffered, filtered, cached) if defs is not None: for node in defs: @@ -62,11 +61,14 @@ class _GenerateRenderMethod(object): inherit = [] namespaces = {} module_code = [] + pagetag = [None] class FindTopLevel(object): def visitInheritTag(s, node): inherit.append(node) def visitNamespaceTag(self, node): namespaces[node.name] = node + def visitPageTag(self, node): + pagetag[0] = node def visitCode(self, node): if node.ismodule: module_code.append(node) @@ -84,12 +86,13 @@ class _GenerateRenderMethod(object): module_identifiers.declared = module_ident # module-level names, python code - self.printer.writeline("from mako import runtime, filters") + self.printer.writeline("from mako import runtime, filters, cache") self.printer.writeline("UNDEFINED = runtime.UNDEFINED") self.printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER)) self.printer.writeline("_modified_time = %s" % repr(time.time())) self.printer.writeline("_template_filename=%s" % repr(self.compiler.filename)) - + self.printer.writeline("_template_cache=cache.Cache(__name__)") + main_identifiers = module_identifiers.branch(self.node) module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs) [module_identifiers.declared.add(x) for x in ["UNDEFINED"]] @@ -106,11 +109,11 @@ class _GenerateRenderMethod(object): elif len(namespaces): self.write_namespaces(namespaces) - return main_identifiers.topleveldefs + return (pagetag[0], main_identifiers.topleveldefs) - def write_render_callable(self, name, args, buffered, filtered): + def write_render_callable(self, name, args, buffered, filtered, cached): self.printer.writeline("def %s(%s):" % (name, ','.join(args))) - if buffered or filtered: + if buffered or filtered or cached: self.printer.writeline("context.push_buffer()") self.printer.writeline("try:") @@ -123,10 +126,11 @@ class _GenerateRenderMethod(object): for n in self.node.nodes: n.accept_visitor(self) - self.write_def_finish(self.node, buffered, filtered) + self.write_def_finish(self.node, buffered, filtered, cached) self.printer.writeline(None) self.printer.write("\n\n") - + if cached: + self.write_cache_decorator(name, buffered) def write_module_code(self, module_code): for n in module_code: @@ -257,7 +261,8 @@ class _GenerateRenderMethod(object): self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls))) filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) - if buffered or filtered: + cached = eval(node.attributes.get('cached', 'False')) + if buffered or filtered or cached: printer.writelines( "context.push_buffer()", "try:" @@ -269,24 +274,42 @@ class _GenerateRenderMethod(object): for n in node.nodes: n.accept_visitor(self) - self.write_def_finish(node, buffered, filtered) + self.write_def_finish(node, buffered, filtered, cached) self.printer.writeline(None) + if cached: + self.write_cache_decorator(node.name, False) - def write_def_finish(self, node, buffered, filtered): - if not buffered: + def write_def_finish(self, node, buffered, filtered, cached): + if not buffered and not cached and not filtered: self.printer.writeline("return ''") - if buffered or filtered: + if buffered or filtered or cached: self.printer.writeline("finally:") self.printer.writeline("_buf = context.pop_buffer()") s = "_buf.getvalue()" if filtered: s = self.create_filter_callable(node.filter_args.args, s) - if buffered: + if buffered or cached: self.printer.writeline("return %s" % s) else: self.printer.writeline("context.write(%s)" % s) self.printer.writeline(None) - + + def write_cache_decorator(self, name, buffered): + self.printer.writeline("__%s = %s" % (name, name)) + if buffered: + self.printer.writelines( + "def %s(context, *args, **kwargs):" % name, + "return _template_cache.get(%s, createfunc=lambda:__%s(context, *args, **kwargs))" % (repr(name), name), + None + ) + else: + self.printer.writelines( + "def %s(context, *args, **kwargs):" % name, + "context.write(_template_cache.get(%s, createfunc=lambda:__%s(context, *args, **kwargs)))" % (repr(name), name), + "return ''", + None + ) + def create_filter_callable(self, args, target): d = dict([(k, "filters." + v.func_name) for k, v in filters.DEFAULT_ESCAPES.iteritems()]) for e in args: @@ -372,7 +395,7 @@ class _GenerateRenderMethod(object): self.write_variable_declares(body_identifiers) for n in node.nodes: n.accept_visitor(self) - self.write_def_finish(node, buffered, False) + self.write_def_finish(node, buffered, False, False) self.printer.writelines( None, "return [%s]" % (','.join(export)), diff --git a/lib/mako/ext/autohandler.py b/lib/mako/ext/autohandler.py index 10c11c4..9fb2194 100644 --- a/lib/mako/ext/autohandler.py +++ b/lib/mako/ext/autohandler.py @@ -1,6 +1,22 @@ """adds autohandler functionality to Mako templates. requires that the TemplateLookup class is used with templates. + +usage: + +<%! + from mako.ext.autohandler import autohandler +%> +<%inherit file="${autohandler(template, context)}"/> + + +or with custom autohandler filename: + +<%! + from mako.ext.autohandler import autohandler +%> +<%inherit file="${autohandler(template, context, name='somefilename')}"/> + """ import posixpath, os, re diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py index 476dbb2..5a82736 100644 --- a/lib/mako/parsetree.py +++ b/lib/mako/parsetree.py @@ -238,7 +238,7 @@ class TextTag(Tag): class DefTag(Tag): __keyword__ = 'def' def __init__(self, keyword, attributes, **kwargs): - super(DefTag, self).__init__(keyword, attributes, ('buffered'), ('name','filter'), ('name',), **kwargs) + super(DefTag, self).__init__(keyword, attributes, ('buffered', 'cached'), ('name','filter'), ('name',), **kwargs) name = attributes['name'] if re.match(r'^[\w_]+$',name): name = name + "()" @@ -271,5 +271,5 @@ class InheritTag(Tag): class PageTag(Tag): __keyword__ = 'page' def __init__(self, keyword, attributes, **kwargs): - super(PageTag, self).__init__(keyword, attributes, (), (), (), **kwargs) + super(PageTag, self).__init__(keyword, attributes, ('cached'), (), (), **kwargs)
\ No newline at end of file diff --git a/test/alltests.py b/test/alltests.py index 06ad059..870c451 100644 --- a/test/alltests.py +++ b/test/alltests.py @@ -7,10 +7,12 @@ def suite(): 'pygen', 'lexer', 'template', + 'lookup', 'def', 'namespace', 'inheritance', - 'call' + 'call', + 'cache' ) alltests = unittest.TestSuite() for name in modules_to_test: diff --git a/test/cache.py b/test/cache.py new file mode 100644 index 0000000..ba44be1 --- /dev/null +++ b/test/cache.py @@ -0,0 +1,52 @@ +from mako.template import Template +import unittest +from util import result_lines + +class CacheTest(unittest.TestCase): + def test_component(self): + t = Template(""" + <%! + callcount = [0] + %> + <%def name="foo" cached="True"> + this is foo + <% + callcount[0] += 1 + %> + </%def> + + ${foo()} + ${foo()} + ${foo()} + callcount: ${callcount} +""") + print t.code + print t.render() + assert result_lines(t.render()) == [ + 'this is foo', + 'this is foo', + 'this is foo', + 'callcount: [1]', + ] + + def test_page(self): + t = Template(""" + <%! + callcount = [0] + %> + <%page cached="True"/> + this is foo + <% + callcount[0] += 1 + %> + callcount: ${callcount} +""") + print t.code + print t.render() + print t.render() + print t.render() + + + +if __name__ == '__main__': + unittest.main()
\ No newline at end of file diff --git a/test/call.py b/test/call.py index 15208f7..fce43f4 100644 --- a/test/call.py +++ b/test/call.py @@ -144,6 +144,32 @@ class CallTest(unittest.TestCase): ${a()} """) assert result_lines(t.render()) == ['this is a', 'this is b', 'this is c:', "this is the body in b's call", 'the embedded "d" is:', 'this is d'] + +class SelfCacheTest(unittest.TestCase): + def test_basic(self): + t = Template(""" + <%! + cached = None + %> + <%def name="foo"> + <% + global cached + if cached: + return "cached: " + cached + context.push_buffer() + %> + this is foo + <% + buf = context.pop_buffer() + cached = buf.getvalue() + return cached + %> + </%def> + + ${foo()} + ${foo()} +""") + print t.render() if __name__ == '__main__': unittest.main() |