Mercurial > genshi > mirror
changeset 1031:5dccab13ec85 trunk
Fix infinite recursion in template inlining (fixes #584).
author | hodgestar |
---|---|
date | Wed, 19 Mar 2014 13:52:01 +0000 |
parents | 7d4414a4a5d0 |
children | e02843c0fecc |
files | genshi/template/base.py genshi/template/markup.py genshi/template/tests/markup.py |
diffstat | 3 files changed, 66 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/genshi/template/base.py +++ b/genshi/template/base.py @@ -452,8 +452,7 @@ @property def stream(self): if not self._prepared: - self._stream = list(self._prepare(self._stream)) - self._prepared = True + self._prepare_self() return self._stream def _parse(self, source, encoding): @@ -470,12 +469,19 @@ """ raise NotImplementedError - def _prepare(self, stream): + def _prepare_self(self, inlined=None): + if not self._prepared: + self._stream = list(self._prepare(self._stream, inlined)) + self._prepared = True + + def _prepare(self, stream, inlined): """Call the `attach` method of every directive found in the template. :param stream: the event stream of the template """ from genshi.template.loader import TemplateNotFound + if inlined is None: + inlined = set((self.filepath,)) for kind, data, pos in stream: if kind is SUB: @@ -486,7 +492,7 @@ namespaces, pos) if directive: directives.append(directive) - substream = self._prepare(substream) + substream = self._prepare(substream, inlined) if directives: yield kind, (directives, list(substream)), pos else: @@ -495,27 +501,42 @@ else: if kind is INCLUDE: href, cls, fallback = data - if isinstance(href, basestring) and \ - not getattr(self.loader, 'auto_reload', True): + tmpl_inlined = False + if (isinstance(href, basestring) and + not getattr(self.loader, 'auto_reload', True)): # If the path to the included template is static, and # auto-reloading is disabled on the template loader, - # the template is inlined into the stream + # the template is inlined into the stream provided it + # is not already in the stack of templates being + # processed. + tmpl = None try: tmpl = self.loader.load(href, relative_to=pos[0], cls=cls or self.__class__) - for event in tmpl.stream: - yield event except TemplateNotFound: if fallback is None: raise - for event in self._prepare(fallback): + if tmpl is not None: + if tmpl.filepath not in inlined: + inlined.add(tmpl.filepath) + tmpl._prepare_self(inlined) + for event in tmpl.stream: + yield event + inlined.discard(tmpl.filepath) + tmpl_inlined = True + else: + for event in self._prepare(fallback, inlined): yield event + tmpl_inlined = True + if tmpl_inlined: continue - elif fallback: + if fallback: # Otherwise the include is performed at run time - data = href, cls, list(self._prepare(fallback)) - - yield kind, data, pos + data = href, cls, list( + self._prepare(fallback, inlined)) + yield kind, data, pos + else: + yield kind, data, pos def generate(self, *args, **kwargs): """Apply the template to the given context data.
--- a/genshi/template/markup.py +++ b/genshi/template/markup.py @@ -284,10 +284,10 @@ yield kind, data, pos - def _prepare(self, stream): - return Template._prepare(self, - self._extract_includes(self._interpolate_attrs(stream)) - ) + def _prepare(self, stream, inlined=None): + return Template._prepare( + self, self._extract_includes(self._interpolate_attrs(stream)), + inlined=inlined) def add_directives(self, namespace, factory): """Register a custom `DirectiveFactory` for a given namespace.
--- a/genshi/template/tests/markup.py +++ b/genshi/template/tests/markup.py @@ -580,7 +580,33 @@ finally: shutil.rmtree(dirname) - def test_allow_exec_false(self): + def test_include_inline_recursive(self): + dirname = tempfile.mkdtemp(suffix='genshi_test') + try: + file1 = open(os.path.join(dirname, 'tmpl1.html'), 'w') + try: + file1.write( + '<div xmlns:xi="http://www.w3.org/2001/XInclude"' + ' xmlns:py="http://genshi.edgewall.org/">' + '$depth' + '<py:with vars="depth = depth + 1">' + '<xi:include href="tmpl1.html"' + ' py:if="depth < 3"/>' + '</py:with>' + '</div>' + ) + finally: + file1.close() + + loader = TemplateLoader([dirname], auto_reload=False) + tmpl = loader.load(os.path.join(dirname, 'tmpl1.html')) + self.assertEqual( + "<div>0<div>1<div>2</div></div></div>", + tmpl.generate(depth=0).render(encoding=None)) + finally: + shutil.rmtree(dirname) + + def test_allow_exec_false(self): xml = ("""<?python title = "A Genshi Template" ?>