paul@399 | 1 | # HG changeset patch |
paul@399 | 2 | # User Paul Boddie <paul@boddie.org.uk> |
paul@399 | 3 | # Date 1390585938 -3600 |
paul@399 | 4 | # Node ID 71d9815d2e6f5487ec87c4f5c04e5a156ffd21d3 |
paul@399 | 5 | # Parent 5738b78f53b540ece0b48efa49b63fa4cbc09be9 |
paul@399 | 6 | Added caching support for the "pageparams" dependency where the request |
paul@399 | 7 | parameters are combined with the page name to make a cache entry. |
paul@399 | 8 | |
paul@399 | 9 | diff -r 5738b78f53b5 -r 71d9815d2e6f MoinMoin/Page.py |
paul@399 | 10 | --- a/MoinMoin/Page.py Sat Nov 30 20:09:43 2013 +0100 |
paul@399 | 11 | +++ b/MoinMoin/Page.py Fri Jan 24 18:52:18 2014 +0100 |
paul@399 | 12 | @@ -41,6 +41,7 @@ |
paul@399 | 13 | |
paul@399 | 14 | from MoinMoin import config, caching, user, util, wikiutil |
paul@399 | 15 | from MoinMoin.logfile import eventlog |
paul@399 | 16 | +from MoinMoin.support.python_compatibility import hash_new |
paul@399 | 17 | |
paul@399 | 18 | def is_cache_exception(e): |
paul@399 | 19 | args = e.args |
paul@399 | 20 | @@ -1351,7 +1352,7 @@ |
paul@399 | 21 | |
paul@399 | 22 | def loadCache(self, request): |
paul@399 | 23 | """ Return page content cache or raises 'CacheNeedsUpdate' """ |
paul@399 | 24 | - cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item') |
paul@399 | 25 | + cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') |
paul@399 | 26 | attachmentsPath = self.getPagePath('attachments', check_create=0) |
paul@399 | 27 | if cache.needsUpdate(self._text_filename(), attachmentsPath): |
paul@399 | 28 | raise Exception('CacheNeedsUpdate') |
paul@399 | 29 | @@ -1372,7 +1373,7 @@ |
paul@399 | 30 | """ Format content into code, update cache and return code """ |
paul@399 | 31 | import marshal |
paul@399 | 32 | from MoinMoin.formatter.text_python import Formatter |
paul@399 | 33 | - formatter = Formatter(request, ["page"], self.formatter) |
paul@399 | 34 | + formatter = Formatter(request, ["page", "pageparams"], self.formatter) |
paul@399 | 35 | |
paul@399 | 36 | # Save request state while formatting page |
paul@399 | 37 | saved_current_lang = request.current_lang |
paul@399 | 38 | @@ -1384,10 +1385,39 @@ |
paul@399 | 39 | src = formatter.assemble_code(text) |
paul@399 | 40 | code = compile(src.encode(config.charset), |
paul@399 | 41 | self.page_name.encode(config.charset), 'exec') |
paul@399 | 42 | - cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item') |
paul@399 | 43 | + self.enforceCacheLimit(request) |
paul@399 | 44 | + cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') |
paul@399 | 45 | cache.update(marshal.dumps(code)) |
paul@399 | 46 | return code |
paul@399 | 47 | |
paul@399 | 48 | + def enforceCacheLimit(self, request): |
paul@399 | 49 | + """ Prevent too many cache entries being stored for a page """ |
paul@399 | 50 | + keys = caching.get_cache_list(request, self, 'item') |
paul@399 | 51 | + try: |
paul@399 | 52 | + cache_limit = int(getattr(request.cfg, 'page_cache_limit', "10")) |
paul@399 | 53 | + except ValueError: |
paul@399 | 54 | + cache_limit = 10 |
paul@399 | 55 | + |
paul@399 | 56 | + if len(keys) >= cache_limit: |
paul@399 | 57 | + items = [caching.CacheEntry(request, self, key, scope='item') for key in keys] |
paul@399 | 58 | + item_ages = [(item.mtime(), item) for item in items] |
paul@399 | 59 | + item_ages.sort() |
paul@399 | 60 | + for item_age, item in item_ages[:-cache_limit]: |
paul@399 | 61 | + item.remove() |
paul@399 | 62 | + |
paul@399 | 63 | + def getCacheKey(self, request): |
paul@399 | 64 | + """ Generate a cache key for a page using optional request information """ |
paul@399 | 65 | + key = self.getFormatterName() |
paul@399 | 66 | + if request.args: |
paul@399 | 67 | + args = request.args.items() |
paul@399 | 68 | + args.sort() |
paul@399 | 69 | + key_args = [] |
paul@399 | 70 | + for k, v in args: |
paul@399 | 71 | + key_args.append("%s=%s" % (k, wikiutil.url_quote(v))) |
paul@399 | 72 | + arg_str = "&".join(key_args) |
paul@399 | 73 | + key = "%s:%s" % (key, hash_new('sha1', arg_str).hexdigest()) |
paul@399 | 74 | + return key |
paul@399 | 75 | + |
paul@399 | 76 | def _specialPageText(self, request, special_type): |
paul@399 | 77 | """ Output the default page content for new pages. |
paul@399 | 78 | |
paul@399 | 79 | # HG changeset patch |
paul@399 | 80 | # User Paul Boddie <paul@boddie.org.uk> |
paul@399 | 81 | # Date 1390599896 -3600 |
paul@399 | 82 | # Node ID e3850612c891d5b6468f212acd797413c70fe18f |
paul@399 | 83 | # Parent 71d9815d2e6f5487ec87c4f5c04e5a156ffd21d3 |
paul@399 | 84 | Acquire the request formatter name when the page formatter has not yet been set. |
paul@399 | 85 | |
paul@399 | 86 | diff -r 71d9815d2e6f -r e3850612c891 MoinMoin/Page.py |
paul@399 | 87 | --- a/MoinMoin/Page.py Fri Jan 24 18:52:18 2014 +0100 |
paul@399 | 88 | +++ b/MoinMoin/Page.py Fri Jan 24 22:44:56 2014 +0100 |
paul@399 | 89 | @@ -1255,15 +1255,17 @@ |
paul@399 | 90 | request.formatter = old_formatter |
paul@399 | 91 | |
paul@399 | 92 | |
paul@399 | 93 | - def getFormatterName(self): |
paul@399 | 94 | + def getFormatterName(self, request=None): |
paul@399 | 95 | """ Return a formatter name as used in the caching system |
paul@399 | 96 | |
paul@399 | 97 | + @param request: the active request (optional) |
paul@399 | 98 | @rtype: string |
paul@399 | 99 | @return: formatter name as used in caching |
paul@399 | 100 | """ |
paul@399 | 101 | - if not hasattr(self, 'formatter') or self.formatter is None: |
paul@399 | 102 | + formatter = getattr(self, 'formatter', None) or request and getattr(request, 'formatter', None) |
paul@399 | 103 | + if not formatter: |
paul@399 | 104 | return '' |
paul@399 | 105 | - module = self.formatter.__module__ |
paul@399 | 106 | + module = formatter.__module__ |
paul@399 | 107 | return module[module.rfind('.') + 1:] |
paul@399 | 108 | |
paul@399 | 109 | def canUseCache(self, parser=None): |
paul@399 | 110 | @@ -1407,7 +1409,7 @@ |
paul@399 | 111 | |
paul@399 | 112 | def getCacheKey(self, request): |
paul@399 | 113 | """ Generate a cache key for a page using optional request information """ |
paul@399 | 114 | - key = self.getFormatterName() |
paul@399 | 115 | + key = self.getFormatterName(request) |
paul@399 | 116 | if request.args: |
paul@399 | 117 | args = request.args.items() |
paul@399 | 118 | args.sort() |
paul@401 | 119 | # HG changeset patch |
paul@401 | 120 | # User Paul Boddie <paul@boddie.org.uk> |
paul@401 | 121 | # Date 1390675730 -3600 |
paul@401 | 122 | # Node ID 908ceaf41dbe9f664f0c0813abc7b4c0611022fe |
paul@401 | 123 | # Parent e3850612c891d5b6468f212acd797413c70fe18f |
paul@401 | 124 | Added tracking of reported dependencies and prevented generation of specific |
paul@401 | 125 | cache entries for request parameter combinations when no extension requires or |
paul@401 | 126 | supports such specific entries. |
paul@401 | 127 | |
paul@401 | 128 | diff -r e3850612c891 -r 908ceaf41dbe MoinMoin/Page.py |
paul@401 | 129 | --- a/MoinMoin/Page.py Fri Jan 24 22:44:56 2014 +0100 |
paul@401 | 130 | +++ b/MoinMoin/Page.py Sat Jan 25 19:48:50 2014 +0100 |
paul@401 | 131 | @@ -1354,9 +1354,12 @@ |
paul@401 | 132 | |
paul@401 | 133 | def loadCache(self, request): |
paul@401 | 134 | """ Return page content cache or raises 'CacheNeedsUpdate' """ |
paul@401 | 135 | - cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') |
paul@401 | 136 | - attachmentsPath = self.getPagePath('attachments', check_create=0) |
paul@401 | 137 | - if cache.needsUpdate(self._text_filename(), attachmentsPath): |
paul@401 | 138 | + for with_params in (True, False): |
paul@401 | 139 | + cache = caching.CacheEntry(request, self, self.getCacheKey(request, with_params), scope='item') |
paul@401 | 140 | + attachmentsPath = self.getPagePath('attachments', check_create=0) |
paul@401 | 141 | + if not cache.needsUpdate(self._text_filename(), attachmentsPath): |
paul@401 | 142 | + break |
paul@401 | 143 | + else: |
paul@401 | 144 | raise Exception('CacheNeedsUpdate') |
paul@401 | 145 | |
paul@401 | 146 | import marshal |
paul@401 | 147 | @@ -1388,7 +1391,10 @@ |
paul@401 | 148 | code = compile(src.encode(config.charset), |
paul@401 | 149 | self.page_name.encode(config.charset), 'exec') |
paul@401 | 150 | self.enforceCacheLimit(request) |
paul@401 | 151 | - cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') |
paul@401 | 152 | + # Determine whether the parameters/args need to be incorporated into the |
paul@401 | 153 | + # cache entry key. |
paul@401 | 154 | + with_params = "pageparams" in formatter.getReportedDependencies() |
paul@401 | 155 | + cache = caching.CacheEntry(request, self, self.getCacheKey(request, with_params), scope='item') |
paul@401 | 156 | cache.update(marshal.dumps(code)) |
paul@401 | 157 | return code |
paul@401 | 158 | |
paul@401 | 159 | @@ -1407,10 +1413,10 @@ |
paul@401 | 160 | for item_age, item in item_ages[:-cache_limit]: |
paul@401 | 161 | item.remove() |
paul@401 | 162 | |
paul@401 | 163 | - def getCacheKey(self, request): |
paul@401 | 164 | + def getCacheKey(self, request, with_params=False): |
paul@401 | 165 | """ Generate a cache key for a page using optional request information """ |
paul@401 | 166 | key = self.getFormatterName(request) |
paul@401 | 167 | - if request.args: |
paul@401 | 168 | + if with_params and request.args: |
paul@401 | 169 | args = request.args.items() |
paul@401 | 170 | args.sort() |
paul@401 | 171 | key_args = [] |
paul@401 | 172 | diff -r e3850612c891 -r 908ceaf41dbe MoinMoin/formatter/text_python.py |
paul@401 | 173 | --- a/MoinMoin/formatter/text_python.py Fri Jan 24 22:44:56 2014 +0100 |
paul@401 | 174 | +++ b/MoinMoin/formatter/text_python.py Sat Jan 25 19:48:50 2014 +0100 |
paul@401 | 175 | @@ -10,6 +10,7 @@ |
paul@401 | 176 | |
paul@401 | 177 | import time |
paul@401 | 178 | from MoinMoin import wikiutil |
paul@401 | 179 | +from MoinMoin.support.python_compatibility import set |
paul@401 | 180 | |
paul@401 | 181 | |
paul@401 | 182 | class Formatter: |
paul@401 | 183 | @@ -40,6 +41,12 @@ |
paul@401 | 184 | self.text_cmd_begin = '\nrequest.write(' |
paul@401 | 185 | self.text_cmd_end = ')\n' |
paul@401 | 186 | |
paul@401 | 187 | + # Record dependency requirements of certain content |
paul@401 | 188 | + self.__dependencies = set() |
paul@401 | 189 | + |
paul@401 | 190 | + def getReportedDependencies(self): |
paul@401 | 191 | + return self.__dependencies |
paul@401 | 192 | + |
paul@401 | 193 | def assemble_code(self, text): |
paul@401 | 194 | """inserts the code into the generated text |
paul@401 | 195 | """ |
paul@401 | 196 | @@ -186,7 +193,10 @@ |
paul@401 | 197 | return self.formatter.div(on, **kw) |
paul@401 | 198 | |
paul@401 | 199 | def macro(self, macro_obj, name, args, markup=None): |
paul@401 | 200 | - if self.__is_static(macro_obj.get_dependencies(name)): |
paul@401 | 201 | + Dependencies = macro_obj.get_dependencies(name) |
paul@401 | 202 | + self.__dependencies.update(Dependencies) |
paul@401 | 203 | + |
paul@401 | 204 | + if self.__is_static(Dependencies): |
paul@401 | 205 | # XXX: why is this necessary?? |
paul@401 | 206 | macro_obj.formatter = self |
paul@401 | 207 | return macro_obj.execute(name, args) |
paul@401 | 208 | @@ -204,6 +214,7 @@ |
paul@401 | 209 | Dependencies = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", parser_name, "Dependencies") |
paul@401 | 210 | except (wikiutil.PluginMissingError, wikiutil.PluginAttributeError): |
paul@401 | 211 | Dependencies = self.defaultDependencies |
paul@401 | 212 | + self.__dependencies.update(Dependencies) |
paul@401 | 213 | |
paul@401 | 214 | if self.__is_static(Dependencies): |
paul@401 | 215 | return self.formatter.parser(parser_name, lines) |