summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-12-04 00:39:38 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-12-04 00:39:38 +0000
commit6c1fd9395a569d41953d28b7b90349194480bb1d (patch)
treef145edf37f17168a0486269807ef69042f31359c
parent94bd81119d1ae63845ea117a369396b89627f3bb (diff)
downloadmako-6c1fd9395a569d41953d28b7b90349194480bb1d.tar.gz
some basic (memory only so far) caching
-rw-r--r--lib/mako/cache.py31
-rw-r--r--lib/mako/codegen.py73
-rw-r--r--lib/mako/ext/autohandler.py16
-rw-r--r--lib/mako/parsetree.py4
-rw-r--r--test/alltests.py4
-rw-r--r--test/cache.py52
-rw-r--r--test/call.py26
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()