# HG changeset patch
# User cmlenz
# Date 1188256847 0
# Node ID 6a37018199fd5779d4c239710c40ce0bfd42cddf
# Parent 23705008082765c285d6c4402b0536358586c856
* XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
* If an include is found when parsing a template, but no template loader has been specified, a `TemplateSyntaxError` is raised.
diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -29,6 +29,12 @@
* Text templates now default to rendering as plain text; it is no longer
necessary to explicitly specify the "text" method to the `render()` or
`serialize()` method of the generated markup stream.
+ * XInclude elements in markup templates now support the `parse` attribute; when
+ set to "xml" (the default), the include is processed as before, but when set
+ to "text", the included template is parsed as a text template using the new
+ syntax (ticket #101).
+ * If an include is found when parsing a template, but no template loader has
+ been specified, a `TemplateSyntaxError` is raised.
Version 0.4.4
diff --git a/doc/xml-templates.txt b/doc/xml-templates.txt
--- a/doc/xml-templates.txt
+++ b/doc/xml-templates.txt
@@ -601,6 +601,10 @@
.. _`xinclude specification`: http://www.w3.org/TR/xinclude/
+
+Dynamic Includes
+================
+
Incudes in Genshi are fully dynamic: Just like normal attributes, the `href`
attribute accepts expressions, and directives_ can be used on the
```` element just as on any other element, meaning you can do
@@ -612,6 +616,23 @@
py:for="name in ('foo', 'bar', 'baz')" />
+Including Text Templates
+========================
+
+The ``parse`` attribute of the ```` element can be used to specify
+whether the included template is an XML template or a text template (using the
+new syntax added in Genshi 0.5):
+
+.. code-block:: genshi
+
+
+
+This example would load the ``myscript.js`` file as a ``NewTextTemplate``. See
+`text templates`_ for details on the syntax of text templates.
+
+.. _`text templates`: text-templates.html
+
+
.. _comments:
--------
diff --git a/genshi/template/base.py b/genshi/template/base.py
--- a/genshi/template/base.py
+++ b/genshi/template/base.py
@@ -39,7 +39,7 @@
class TemplateError(Exception):
"""Base exception class for errors related to template processing."""
- def __init__(self, message, filename='', lineno=-1, offset=-1):
+ def __init__(self, message, filename=None, lineno=-1, offset=-1):
"""Create the exception.
:param message: the error message
@@ -48,6 +48,8 @@
occurred
:param offset: the column number at which the error occurred
"""
+ if filename is None:
+ filename = ''
self.msg = message #: the error message string
if filename != '' or lineno >= 0:
message = '%s (%s, line %d)' % (self.msg, filename, lineno)
@@ -62,7 +64,7 @@
error, or the template is not well-formed.
"""
- def __init__(self, message, filename='', lineno=-1, offset=-1):
+ def __init__(self, message, filename=None, lineno=-1, offset=-1):
"""Create the exception
:param message: the error message
@@ -84,7 +86,7 @@
with a local name that doesn't match any registered directive.
"""
- def __init__(self, name, filename='', lineno=-1):
+ def __init__(self, name, filename=None, lineno=-1):
"""Create the exception
:param name: the name of the directive
@@ -333,6 +335,10 @@
self.lookup = lookup
self.allow_exec = allow_exec
+ self.filters = [self._flatten, self._eval, self._exec]
+ if loader:
+ self.filters.append(self._include)
+
if isinstance(source, basestring):
source = StringIO(source)
else:
@@ -341,9 +347,6 @@
self.stream = list(self._prepare(self._parse(source, encoding)))
except ParseError, e:
raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset)
- self.filters = [self._flatten, self._eval, self._exec]
- if loader:
- self.filters.append(self._include)
def __repr__(self):
return '<%s "%s">' % (self.__class__.__name__, self.filename)
@@ -386,7 +389,7 @@
yield event
else:
if kind is INCLUDE:
- href, fallback = data
+ href, cls, fallback = data
if isinstance(href, basestring) and \
not getattr(self.loader, 'auto_reload', True):
# If the path to the included template is static, and
@@ -394,7 +397,7 @@
# the template is inlined into the stream
try:
tmpl = self.loader.load(href, relative_to=pos[0],
- cls=self.__class__)
+ cls=cls or self.__class__)
for event in tmpl.stream:
yield event
except TemplateNotFound:
@@ -513,7 +516,7 @@
for event in stream:
if event[0] is INCLUDE:
- href, fallback = event[1]
+ href, cls, fallback = event[1]
if not isinstance(href, basestring):
parts = []
for subkind, subdata, subpos in self._eval(href, ctxt):
@@ -522,7 +525,7 @@
href = u''.join([x for x in parts if x is not None])
try:
tmpl = self.loader.load(href, relative_to=event[2][0],
- cls=self.__class__)
+ cls=cls or self.__class__)
for event in tmpl.generate(ctxt):
yield event
except TemplateNotFound:
diff --git a/genshi/template/markup.py b/genshi/template/markup.py
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -24,6 +24,7 @@
from genshi.template.eval import Suite
from genshi.template.interpolation import interpolate
from genshi.template.directives import *
+from genshi.template.text import NewTextTemplate
__all__ = ['MarkupTemplate']
__docformat__ = 'restructuredtext en'
@@ -141,13 +142,17 @@
dirmap[(depth, tag)] = (directives, len(stream), strip)
if tag in self.XINCLUDE_NAMESPACE:
+ if self._include not in self.filters:
+ raise TemplateSyntaxError('Include found but no '
+ 'template loader specified',
+ self.filepath, *pos[1:])
if tag.localname == 'include':
include_href = new_attrs.get('href')
if not include_href:
raise TemplateSyntaxError('Include misses required '
'attribute "href"',
self.filepath, *pos[1:])
- includes.append(include_href)
+ includes.append((include_href, new_attrs.get('parse')))
streams.append([])
elif tag.localname == 'fallback':
streams.append([])
@@ -170,7 +175,17 @@
streams.pop() # discard anything between the include tags
# and the fallback element
stream = streams[-1]
- stream.append((INCLUDE, (includes.pop(), fallback), pos))
+ href, parse = includes.pop()
+ try:
+ cls = {
+ 'xml': MarkupTemplate,
+ 'text': NewTextTemplate
+ }[parse or 'xml']
+ except KeyError:
+ raise TemplateSyntaxError('Invalid value for "parse" '
+ 'attribute of include',
+ self.filepath, *pos[1:])
+ stream.append((INCLUDE, (href, cls, fallback), pos))
else:
stream.append((kind, data, pos))
diff --git a/genshi/template/tests/markup.py b/genshi/template/tests/markup.py
--- a/genshi/template/tests/markup.py
+++ b/genshi/template/tests/markup.py
@@ -244,6 +244,12 @@
""", str(tmpl.generate()))
+ def test_include_without_loader(self):
+ xml = """
+
+ """
+ self.assertRaises(TemplateSyntaxError, MarkupTemplate, xml)
+
def test_include_in_loop(self):
dirname = tempfile.mkdtemp(suffix='genshi_test')
try:
diff --git a/genshi/template/tests/text.py b/genshi/template/tests/text.py
--- a/genshi/template/tests/text.py
+++ b/genshi/template/tests/text.py
@@ -17,6 +17,7 @@
import tempfile
import unittest
+from genshi.template.base import TemplateSyntaxError
from genshi.template.loader import TemplateLoader
from genshi.template.text import OldTextTemplate, NewTextTemplate
@@ -111,6 +112,10 @@
----- Included data above this line -----""",
tmpl.generate().render())
+ def test_include_without_loader(self):
+ text = '#include "oops.html"'
+ self.assertRaises(TemplateSyntaxError, OldTextTemplate, text)
+
class NewTextTemplateTestCase(unittest.TestCase):
"""Tests for text template processing."""
@@ -231,6 +236,10 @@
Included
----- Included data above this line -----""", tmpl.generate().render())
+ def test_include_without_loader(self):
+ text = '{% include "oops.html" %}'
+ self.assertRaises(TemplateSyntaxError, NewTextTemplate, text)
+
def suite():
suite = unittest.TestSuite()
diff --git a/genshi/template/text.py b/genshi/template/text.py
--- a/genshi/template/text.py
+++ b/genshi/template/text.py
@@ -28,7 +28,8 @@
import re
-from genshi.template.base import BadDirectiveError, Template, EXEC, INCLUDE, SUB
+from genshi.template.base import BadDirectiveError, Template, \
+ TemplateSyntaxError, EXEC, INCLUDE, SUB
from genshi.template.eval import Suite
from genshi.template.directives import *
from genshi.template.directives import Directive, _apply_directives
@@ -186,8 +187,12 @@
command, value = mo.group(2, 3)
if command == 'include':
+ if self._include not in self.filters:
+ raise TemplateSyntaxError('Include found but no template '
+ 'loader specified', self.filepath,
+ lineno)
pos = (self.filename, lineno, 0)
- stream.append((INCLUDE, (value.strip(), []), pos))
+ stream.append((INCLUDE, (value.strip(), None, []), pos))
elif command == 'python':
if not self.allow_exec:
@@ -306,8 +311,12 @@
stream[start_offset:] = [(SUB, ([directive], substream),
(self.filepath, lineno, 0))]
elif command == 'include':
+ if self._include not in self.filters:
+ raise TemplateSyntaxError('Include found but no template '
+ 'loader specified', self.filepath,
+ lineno)
pos = (self.filename, lineno, 0)
- stream.append((INCLUDE, (value.strip(), []), pos))
+ stream.append((INCLUDE, (value.strip(), None, []), pos))
elif command != '#':
cls = self._dir_by_name.get(command)
if cls is None: