blob: 387cd465790be78c258304c67b24962e949ef73f [file] [log] [blame]
Armin Ronacher023b5e92008-05-08 11:03:10 +02001from jinja2 import nodes
2from jinja2.ext import Extension
3
4
Armin Ronacher762079c2008-05-08 23:57:56 +02005class FragmentCacheExtension(Extension):
6 # a set of names that trigger the extension.
David Lord04c87872020-01-10 07:46:18 -08007 tags = {"cache"}
Armin Ronacher023b5e92008-05-08 11:03:10 +02008
9 def __init__(self, environment):
Armin Ronacher762079c2008-05-08 23:57:56 +020010 super(FragmentCacheExtension, self).__init__(environment)
Armin Ronacher023b5e92008-05-08 11:03:10 +020011
Armin Ronacher762079c2008-05-08 23:57:56 +020012 # add the defaults to the environment
David Lord04c87872020-01-10 07:46:18 -080013 environment.extend(fragment_cache_prefix="", fragment_cache=None)
Armin Ronacher023b5e92008-05-08 11:03:10 +020014
15 def parse(self, parser):
16 # the first token is the token that started the tag. In our case
17 # we only listen to ``'cache'`` so this will be a name token with
18 # `cache` as value. We get the line number so that we can give
19 # that line number to the nodes we create by hand.
jenisysfa3a3022015-05-09 18:57:52 +020020 lineno = next(parser.stream).lineno
Armin Ronacher023b5e92008-05-08 11:03:10 +020021
22 # now we parse a single expression that is used as cache key.
23 args = [parser.parse_expression()]
24
Armin Ronacher762079c2008-05-08 23:57:56 +020025 # if there is a comma, the user provided a timeout. If not use
26 # None as second parameter.
David Lord04c87872020-01-10 07:46:18 -080027 if parser.stream.skip_if("comma"):
Armin Ronacher023b5e92008-05-08 11:03:10 +020028 args.append(parser.parse_expression())
Armin Ronacher023b5e92008-05-08 11:03:10 +020029 else:
30 args.append(nodes.Const(None))
31
32 # now we parse the body of the cache block up to `endcache` and
33 # drop the needle (which would always be `endcache` in that case)
David Lord04c87872020-01-10 07:46:18 -080034 body = parser.parse_statements(["name:endcache"], drop_needle=True)
Armin Ronacher023b5e92008-05-08 11:03:10 +020035
36 # now return a `CallBlock` node that calls our _cache_support
37 # helper method on this extension.
David Lord04c87872020-01-10 07:46:18 -080038 return nodes.CallBlock(
39 self.call_method("_cache_support", args), [], [], body
40 ).set_lineno(lineno)
Armin Ronacher023b5e92008-05-08 11:03:10 +020041
42 def _cache_support(self, name, timeout, caller):
43 """Helper callback."""
Armin Ronacher762079c2008-05-08 23:57:56 +020044 key = self.environment.fragment_cache_prefix + name
Armin Ronacher023b5e92008-05-08 11:03:10 +020045
Armin Ronacher762079c2008-05-08 23:57:56 +020046 # try to load the block from the cache
Armin Ronacher023b5e92008-05-08 11:03:10 +020047 # if there is no fragment in the cache, render it and store
48 # it in the cache.
Armin Ronacher762079c2008-05-08 23:57:56 +020049 rv = self.environment.fragment_cache.get(key)
Armin Ronacher7850dc52009-02-08 11:11:57 +010050 if rv is not None:
Armin Ronacher762079c2008-05-08 23:57:56 +020051 return rv
Armin Ronacherd89f0f32009-02-04 18:57:27 +010052 rv = caller()
53 self.environment.fragment_cache.add(key, rv, timeout)
Armin Ronacher023b5e92008-05-08 11:03:10 +020054 return rv