Edgewall Software

source: branches/stable/0.4.x/genshi/template/text.py

Last change on this file was 617, checked in by cmlenz, 16 years ago

Ported [616] to 0.4.x branch.

  • Property svn:eol-style set to native
File size: 4.0 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2006-2007 Edgewall Software
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://genshi.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://genshi.edgewall.org/log/.
13
14"""Plain text templating engine."""
15
16import re
17
18from genshi.template.base import BadDirectiveError, Template, SUB
19from genshi.template.directives import *
20from genshi.template.interpolation import interpolate
21
22__all__ = ['TextTemplate']
23__docformat__ = 'restructuredtext en'
24
25
26class TextTemplate(Template):
27    """Implementation of a simple text-based template engine.
28   
29    >>> tmpl = TextTemplate('''Dear $name,
30    ...
31    ... We have the following items for you:
32    ... #for item in items
33    ...  * $item
34    ... #end
35    ...
36    ... All the best,
37    ... Foobar''')
38    >>> print tmpl.generate(name='Joe', items=[1, 2, 3]).render('text')
39    Dear Joe,
40    <BLANKLINE>
41    We have the following items for you:
42     * 1
43     * 2
44     * 3
45    <BLANKLINE>
46    All the best,
47    Foobar
48    """
49    directives = [('def', DefDirective),
50                  ('when', WhenDirective),
51                  ('otherwise', OtherwiseDirective),
52                  ('for', ForDirective),
53                  ('if', IfDirective),
54                  ('choose', ChooseDirective),
55                  ('with', WithDirective)]
56
57    _DIRECTIVE_RE = re.compile(r'(?:^[ \t]*(?<!\\)#(end).*\n?)|'
58                               r'(?:^[ \t]*(?<!\\)#((?:\w+|#).*)\n?)',
59                               re.MULTILINE)
60
61    def _parse(self, source, encoding):
62        """Parse the template from text input."""
63        stream = [] # list of events of the "compiled" template
64        dirmap = {} # temporary mapping of directives to elements
65        depth = 0
66
67        source = source.read()
68        if isinstance(source, str):
69            source = source.decode(encoding or 'utf-8', 'replace')
70        offset = 0
71        lineno = 1
72
73        for idx, mo in enumerate(self._DIRECTIVE_RE.finditer(source)):
74            start, end = mo.span()
75            if start > offset:
76                text = source[offset:start]
77                for kind, data, pos in interpolate(text, self.basedir,
78                                                   self.filename, lineno,
79                                                   lookup=self.lookup):
80                    stream.append((kind, data, pos))
81                lineno += len(text.splitlines())
82
83            text = source[start:end].lstrip()[1:]
84            lineno += len(text.splitlines())
85            directive = text.split(None, 1)
86            if len(directive) > 1:
87                command, value = directive
88            else:
89                command, value = directive[0], None
90
91            if command == 'end':
92                depth -= 1
93                if depth in dirmap:
94                    directive, start_offset = dirmap.pop(depth)
95                    substream = stream[start_offset:]
96                    stream[start_offset:] = [(SUB, ([directive], substream),
97                                              (self.filepath, lineno, 0))]
98            elif command != '#':
99                cls = self._dir_by_name.get(command)
100                if cls is None:
101                    raise BadDirectiveError(command)
102                directive = cls, value, None, (self.filepath, lineno, 0)
103                dirmap[depth] = (directive, len(stream))
104                depth += 1
105
106            offset = end
107
108        if offset < len(source):
109            text = source[offset:].replace('\\#', '#')
110            for kind, data, pos in interpolate(text, self.basedir,
111                                               self.filename, lineno,
112                                               lookup=self.lookup):
113                stream.append((kind, data, pos))
114
115        return stream
Note: See TracBrowser for help on using the repository browser.