# HG changeset patch # User cmlenz # Date 1174600456 0 # Node ID e065d7906b6807f18370273100f74ee2cefd7069 # Parent 6d01e91f2a4959ef9f118200b8c82f40790f34f5 * Better method to propogate the full path to the template file on parse errors. Supersedes r513. * More API docs and doctests. diff --git a/genshi/core.py b/genshi/core.py --- a/genshi/core.py +++ b/genshi/core.py @@ -219,7 +219,7 @@ class Attrs(tuple): """Immutable sequence type that stores the attributes of an element. - Ordering of the attributes is preserved, while accessing by name is also + Ordering of the attributes is preserved, while access by name is also supported. >>> attrs = Attrs([('href', '#'), ('title', 'Foo')]) @@ -316,6 +316,9 @@ The returned event is a `TEXT` event, the data is the value of all attributes joined together. + + >>> Attrs([('href', '#'), ('title', 'Foo')]).totuple() + ('TEXT', u'#Foo', (None, -1, -1)) """ return TEXT, u''.join([x[1] for x in self]), (None, -1, -1) @@ -352,6 +355,19 @@ return '<%s %r>' % (self.__class__.__name__, unicode(self)) def join(self, seq, escape_quotes=True): + """Return a `Markup` object which is the concatenation of the strings + in the given sequence, where this `Markup` object is the separator + between the joined elements. + + Any element in the sequence that is not a `Markup` instance is + automatically escaped. + + :param seq: the sequence of strings to join + :param escape_quotes: whether double quote characters in the elements + should be escaped + :return: the joined `Markup` object + :see: `escape` + """ return Markup(unicode(self).join([escape(item, quotes=escape_quotes) for item in seq])) @@ -359,9 +375,21 @@ """Create a Markup instance from a string and escape special characters it may contain (<, >, & and \"). + >>> escape('"1 < 2"') + + If the `quotes` parameter is set to `False`, the \" character is left as is. Escaping quotes is generally only required for strings that are to be used in attribute values. + + >>> escape('"1 < 2"', quotes=False) + + + :param text: the text to escape + :param quotes: if ``True``, double quote characters are escaped in + addition to the other special characters + :return: the escaped `Markup` string + :see: `genshi.core.escape` """ if not text: return cls() @@ -376,7 +404,13 @@ escape = classmethod(escape) def unescape(self): - """Reverse-escapes &, <, > and \" and returns a `unicode` object.""" + """Reverse-escapes &, <, >, and \" and returns a `unicode` object. + + >>> Markup('1 < 2').unescape() + u'1 < 2' + + :see: `genshi.core.unescape` + """ if not self: return u'' return unicode(self).replace('"', '"') \ @@ -389,20 +423,37 @@ replaced by the equivalent UTF-8 characters. If the `keepxmlentities` parameter is provided and evaluates to `True`, - the core XML entities (&, ', >, < and ") are not - stripped. + the core XML entities (``&``, ``'``, ``>``, ``<`` and + ``"``) are not stripped. + + :see: `genshi.util.stripentities` """ return Markup(stripentities(self, keepxmlentities=keepxmlentities)) def striptags(self): - """Return a copy of the text with all XML/HTML tags removed.""" + """Return a copy of the text with all XML/HTML tags removed. + + :see: `genshi.util.striptags` + """ return Markup(striptags(self)) escape = Markup.escape def unescape(text): - """Reverse-escapes &, <, > and \" and returns a `unicode` object.""" + """Reverse-escapes &, <, >, and \" and returns a `unicode` object. + + >>> unescape(Markup('1 < 2')) + u'1 < 2' + + If the provided `text` object is not a `Markup` instance, the text is + returned as-is. + + >>> unescape('1 < 2') + '1 < 2' + + :param text: the text to unescape + """ if not isinstance(text, Markup): return text return text.unescape() diff --git a/genshi/input.py b/genshi/input.py --- a/genshi/input.py +++ b/genshi/input.py @@ -23,7 +23,6 @@ from sets import ImmutableSet as frozenset import HTMLParser as html import htmlentitydefs -import os from StringIO import StringIO from genshi.core import Attrs, QName, Stream, stripentities @@ -68,7 +67,7 @@ """ self.msg = message if filename: - message += ', in ' + os.path.basename(filename) + message += ', in ' + filename Exception.__init__(self, message) self.filename = filename or '' self.lineno = lineno diff --git a/genshi/template/base.py b/genshi/template/base.py --- a/genshi/template/base.py +++ b/genshi/template/base.py @@ -23,6 +23,7 @@ from StringIO import StringIO from genshi.core import Attrs, Stream, StreamEventKind, START, TEXT, _ensure +from genshi.input import ParseError __all__ = ['Context', 'Template', 'TemplateError', 'TemplateRuntimeError', 'TemplateSyntaxError', 'BadDirectiveError'] @@ -235,7 +236,10 @@ source = StringIO(source) else: source = source - self.stream = list(self._prepare(self._parse(source, encoding))) + try: + 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] def __repr__(self): diff --git a/genshi/template/markup.py b/genshi/template/markup.py --- a/genshi/template/markup.py +++ b/genshi/template/markup.py @@ -87,7 +87,7 @@ include_href = None if not isinstance(source, Stream): - source = XMLParser(source, filename=self.filepath, + source = XMLParser(source, filename=self.filename, encoding=encoding) for kind, data, pos in source: