| | 1082 | class 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 | |