Edgewall Software

Ticket #47: texttmpl.4.diff

File texttmpl.4.diff, 4.4 KB (added by cboos, 9 years ago)

Based on attachment:texttmpl.3.diff and r281, integrates TextTemplate with the TemplateLoader

  • markup/template.py

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