Ticket #5: recursive_inclusion_detect.patch
| File recursive_inclusion_detect.patch, 12.7 KB (added by Carsten Klein <carsten.klein@…>, 13 years ago) |
|---|
-
template/base.py
94 94 """ 95 95 96 96 97 class RecursiveInclusionError(TemplateRuntimeError): 98 """ 99 """ 100 def __init__(self, filepath, backtrace, includefile, includepos): 101 pass 102 103 97 104 class Context(object): 98 105 """Container for template input data. 99 106 … … 370 377 _number_conv = unicode # function used to convert numbers to event data 371 378 372 379 def __init__(self, source, filepath=None, filename=None, loader=None, 373 encoding=None, lookup='strict', allow_exec=True): 380 encoding=None, lookup='strict', allow_exec=True, 381 backtrace=[], include_pos=(None, -1, -1)): 374 382 """Initialize a template from either a string, a file-like object, or 375 383 an already parsed markup stream. 376 384 … … 386 394 default), "lenient", or a custom lookup class 387 395 :param allow_exec: whether Python code blocks in templates should be 388 396 allowed 397 :param backtrace: a list of tuples of type (filepath, pos) used with 398 recursive inclusion detection 399 :param include_pos: a tuple containing the position in the stream at 400 which the include was detected 389 401 390 402 :note: Changed in 0.5: Added the `allow_exec` argument 391 403 """ … … 396 408 self.allow_exec = allow_exec 397 409 self._init_filters() 398 410 self._prepared = False 411 self.backtrace = backtrace 412 self.include_pos = include_pos 413 for fpath, pos in backtrace: 414 # TODO include trace in output message where template was first 415 # included in 416 if fpath == filepath: 417 raise TemplateSyntaxError('Recursive inclusion of ' 418 '"%s" detected. Backtrace: "%s"' % (self.filepath, self.backtrace), 419 backtrace[-1][0], *self.include_pos[1:]) 420 self.backtrace.append((self.filepath, self.include_pos)) 399 421 400 422 if isinstance(source, basestring): 401 423 source = StringIO(source) … … 468 490 yield event 469 491 else: 470 492 if kind is INCLUDE: 471 href, cls, fallback = data493 href, cls, backtrace, fallback = data 472 494 if isinstance(href, basestring) and \ 473 495 not getattr(self.loader, 'auto_reload', True): 474 496 # If the path to the included template is static, and … … 476 498 # the template is inlined into the stream 477 499 try: 478 500 tmpl = self.loader.load(href, relative_to=pos[0], 479 cls=cls or self.__class__) 501 cls=cls or self.__class__, 502 backtrace=backtrace, 503 include_pos=pos) 480 504 for event in tmpl.stream: 481 505 yield event 482 506 except TemplateNotFound: … … 490 514 data = href, cls, list(self._prepare(fallback)) 491 515 492 516 yield kind, data, pos 517 518 #self.file.close() 493 519 494 520 def generate(self, *args, **kwargs): 495 521 """Apply the template to the given context data. … … 591 617 592 618 for event in stream: 593 619 if event[0] is INCLUDE: 594 href, cls, fallback = event[1]620 href, cls, backtrace, fallback = event[1] 595 621 if not isinstance(href, basestring): 596 622 parts = [] 597 623 for subkind, subdata, subpos in self._flatten(href, ctxt, … … 601 627 href = ''.join([x for x in parts if x is not None]) 602 628 try: 603 629 tmpl = self.loader.load(href, relative_to=event[2][0], 604 cls=cls or self.__class__) 630 cls=cls or self.__class__, 631 backtrace=backtrace, 632 include_pos=event[2]) 605 633 for event in tmpl.generate(ctxt, **vars): 606 634 yield event 607 635 except TemplateNotFound: -
template/markup.py
61 61 _number_conv = Markup 62 62 63 63 def __init__(self, source, filepath=None, filename=None, loader=None, 64 encoding=None, lookup='strict', allow_exec=True): 64 encoding=None, lookup='strict', allow_exec=True, 65 backtrace=[], include_pos=(None, -1, -1)): 65 66 Template.__init__(self, source, filepath=filepath, filename=filename, 66 67 loader=loader, encoding=encoding, lookup=lookup, 67 allow_exec=allow_exec) 68 allow_exec=allow_exec, backtrace=backtrace, 69 include_pos=include_pos) 68 70 self.add_directives(self.DIRECTIVE_NAMESPACE, self) 69 71 70 72 def _init_filters(self): … … 251 253 raise TemplateSyntaxError('Invalid value for "parse" ' 252 254 'attribute of include', 253 255 self.filepath, *pos[1:]) 254 stream.append((INCLUDE, (href, cls, fallback), pos)) 256 stream.append((INCLUDE, (href, cls, self.backtrace, 257 fallback), pos)) 255 258 else: 256 259 stream.append((kind, data, pos)) 257 260 -
template/loader.py
19 19 except ImportError: 20 20 import dummy_threading as threading 21 21 22 from genshi.template.base import TemplateError 22 from genshi.template.base import TemplateError, TemplateSyntaxError 23 23 from genshi.util import LRUCache 24 24 25 25 __all__ = ['TemplateLoader', 'TemplateNotFound', 'directory', 'package', … … 131 131 self._uptodate = {} 132 132 self._lock = threading.RLock() 133 133 134 def load(self, filename, relative_to=None, cls=None, encoding=None): 134 def load(self, filename, relative_to=None, cls=None, encoding=None, 135 backtrace=[], include_pos=(-1,-1)): 135 136 """Load the template with the given name. 136 137 137 138 If the `filename` parameter is relative, this method searches the … … 158 159 :param cls: the class of the template object to instantiate 159 160 :param encoding: the encoding of the template to load; defaults to the 160 161 ``default_encoding`` of the loader instance 162 :param backtrace: a list of tuples of type (filepath, pos) used with 163 recursive inclusion detection 164 :param include_pos: the position in the stream at which the include was 165 detected in the including template 161 166 :return: the loaded `Template` instance 162 167 :raises TemplateNotFound: if a template with the given name could not 163 168 be found … … 173 178 filename = os.path.join(os.path.dirname(relative_to), filename) 174 179 175 180 filename = os.path.normpath(filename) 176 cachekey = filename177 181 178 182 self._lock.acquire() 179 183 try: 180 # First check the cache to avoid reparsing the same file181 try:182 tmpl = self._cache[cachekey]183 if not self.auto_reload:184 return tmpl185 uptodate = self._uptodate[cachekey]186 if uptodate is not None and uptodate():187 return tmpl188 except (KeyError, OSError):189 pass190 191 184 isabs = False 192 185 193 186 if os.path.isabs(filename): … … 212 205 loadfunc = directory(loadfunc) 213 206 try: 214 207 filepath, filename, fileobj, uptodate = loadfunc(filename) 208 break 215 209 except IOError: 216 210 continue 217 211 else: 218 try: 219 if isabs: 220 # If the filename of either the included or the 221 # including template is absolute, make sure the 222 # included template gets an absolute path, too, 223 # so that nested includes work properly without a 224 # search path 225 filename = filepath 226 tmpl = self._instantiate(cls, fileobj, filepath, 227 filename, encoding=encoding) 228 if self.callback: 229 self.callback(tmpl) 230 self._cache[cachekey] = tmpl 231 self._uptodate[cachekey] = uptodate 232 finally: 233 if hasattr(fileobj, 'close'): 234 fileobj.close() 212 if isabs: 213 # If the filename of either the included or the 214 # including template is absolute, make sure the 215 # included template gets an absolute path, too, 216 # so that nested includes work properly without a 217 # search path 218 filename = filepath 219 220 cachekey = filepath 221 self._uptodate[cachekey] = uptodate 222 223 # First check the cache to avoid reparsing the same file 224 try: 225 tmpl = self._cache[cachekey] 226 for fpath, pos in backtrace: 227 # TODO include trace in output message where template was first 228 # included in 229 if fpath == filepath: 230 raise TemplateSyntaxError('Recursive inclusion of ' 231 '"%s" detected. Backtrace: "%s"' % (filename, backtrace), 232 backtrace[-1][0], *include_pos[1:]) 233 if not self.auto_reload: 235 234 return tmpl 235 uptodate = self._uptodate[cachekey] 236 if uptodate is not None and uptodate(): 237 return tmpl 238 except (KeyError, OSError): 239 pass 236 240 241 try: 242 tmpl = self._instantiate(cls, fileobj, filepath, 243 filename, encoding=encoding, 244 backtrace=backtrace, 245 include_pos=include_pos) 246 if self.callback: 247 self.callback(tmpl) 248 self._cache[cachekey] = tmpl 249 finally: 250 if hasattr(fileobj, 'close'): 251 fileobj.close() 252 return tmpl 253 237 254 raise TemplateNotFound(filename, search_path) 238 255 239 256 finally: 240 257 self._lock.release() 241 258 242 def _instantiate(self, cls, fileobj, filepath, filename, encoding=None): 259 def _instantiate(self, cls, fileobj, filepath, filename, encoding=None, 260 backtrace=[], include_pos=(-1,-1)): 243 261 """Instantiate and return the `Template` object based on the given 244 262 class and parameters. 245 263 … … 255 273 path 256 274 :param encoding: the encoding of the template to load; defaults to the 257 275 ``default_encoding`` of the loader instance 276 :param backtrace: a list of tuples of type (filepath, pos) used with 277 recursive inclusion detection 278 :param include_pos: the position in the stream at which the include was 279 detected in the including template 258 280 :return: the loaded `Template` instance 259 281 :rtype: `Template` 260 282 """ … … 262 284 encoding = self.default_encoding 263 285 return cls(fileobj, filepath=filepath, filename=filename, loader=self, 264 286 encoding=encoding, lookup=self.variable_lookup, 265 allow_exec=self.allow_exec) 287 allow_exec=self.allow_exec, backtrace=backtrace, 288 include_pos=include_pos) 266 289 267 290 @staticmethod 268 291 def directory(path):
