Edgewall Software

Ticket #47: texttmpl.3.diff

File texttmpl.3.diff, 3.6 KB (added by cmlenz, 9 years ago)

Bugfix: don't ignore last text partition

  • markup/template.py

     
    10791079SUB = Template.SUB
    10801080
    10811081
     1082class TextTemplate(Template):
     1083    """Implementation of a simple text-based template engine.
     1084   
     1085    >>> tmpl = TextTemplate('''Dear $name,
     1086    ...
     1087    ... We have the following items for you:
     1088    ... #for item in items
     1089    ...  * $item
     1090    ... #endfor
     1091    ...
     1092    ... All the best,
     1093    ... Foobar''')
     1094    >>> print tmpl.generate(name='Joe', items=[1, 2, 3]).render('text')
     1095    Dear Joe,
     1096    <BLANKLINE>
     1097    We have the following items for you:
     1098     * 1
     1099     * 2
     1100     * 3
     1101    <BLANKLINE>
     1102    All the best,
     1103    Foobar
     1104
     1105    >>> tmpl = TextTemplate('''#def sayhi(name)
     1106    ... Hello, $name!
     1107    ... #enddef
     1108    ... ${sayhi('world')}''')
     1109    >>> print tmpl.generate(name='Joe', items=[1, 2, 3]).render('text')
     1110    Hello, world!
     1111    <BLANKLINE>
     1112    """
     1113
     1114    directives = [('def', DefDirective),
     1115                  ('when', WhenDirective),
     1116                  ('otherwise', OtherwiseDirective),
     1117                  ('for', ForDirective),
     1118                  ('if', IfDirective),
     1119                  ('choose', ChooseDirective),
     1120                  ('with', WithDirective)]
     1121    _dir_by_name = dict(directives)
     1122    _dir_order = [directive[1] for directive in directives]
     1123
     1124    _directive_re = re.compile('^\s*#(\w+.*)\n?', re.MULTILINE)
     1125
     1126    def parse(self):
     1127        stream = [] # list of events of the "compiled" template
     1128        dirmap = {} # temporary mapping of directives to elements
     1129        depth = 0
     1130
     1131        source = self.source.read()
     1132        offset = 0
     1133        lineno = 1
     1134        for idx, mo in enumerate(self._directive_re.finditer(source)):
     1135            start, end = mo.span()
     1136            if start > offset:
     1137                text = source[offset:start]
     1138                for kind, data, pos in self._interpolate(text, self.filename,
     1139                                                         lineno, 0):
     1140                    stream.append((kind, data, pos))
     1141                lineno += len(text.splitlines())
     1142
     1143            text = source[start:end].lstrip().lstrip('#')
     1144            lineno += len(text.splitlines())
     1145            directive = text.split(None, 1)
     1146            if len(directive) > 1:
     1147                command, value = directive
     1148            else:
     1149                command, value = directive[0], None
     1150
     1151            if not command.startswith('end'):
     1152                cls = self._dir_by_name.get(command)
     1153                if cls is None:
     1154                    raise BadDirectiveError(command)
     1155                directive = cls(value, self.filename, lineno, 0)
     1156                dirmap[depth] = (directive, len(stream))
     1157                depth += 1
     1158            else:
     1159                depth -= 1
     1160                command = command[3:]
     1161                if depth in dirmap:
     1162                    directive, start_offset = dirmap.pop(depth)
     1163                    substream = stream[start_offset:]
     1164                    stream[start_offset:] = [(SUB, ([directive], substream),
     1165                                              (self.filename, lineno, 0))]
     1166
     1167            offset = end
     1168
     1169        if offset < len(source):
     1170            text = source[offset:]
     1171            for kind, data, pos in self._interpolate(text, self.filename,
     1172                                                     lineno, 0):
     1173                stream.append((kind, data, pos))
     1174
     1175        return stream
     1176
     1177    def _match(self, stream, ctxt=None):
     1178        return stream
     1179
     1180
    10821181class TemplateLoader(object):
    10831182    """Responsible for loading templates from files on the specified search
    10841183    path.