def _parse(self, source, encoding): """Parse the template from text input.""" stream = [] # list of events of the "compiled" template dirmap = {} # temporary mapping of directives to elements depth = 0 source = source.read() if not isinstance(source, str): source = source.decode(encoding or 'utf-8', 'replace') offset = 0 lineno = 1 for idx, mo in enumerate(self._DIRECTIVE_RE.finditer(source)): start, end = mo.span() if start > offset: text = source[offset:start] for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) lineno += len(text.splitlines()) text = source[start:end].lstrip()[1:] lineno += len(text.splitlines()) directive = text.split(None, 1) if len(directive) > 1: command, value = directive else: command, value = directive[0], None if command == 'end': depth -= 1 if depth in dirmap: directive, start_offset = dirmap.pop(depth) substream = stream[start_offset:] stream[start_offset:] = [(SUB, ([directive], substream), (self.filepath, lineno, 0))] elif command == 'include': pos = (self.filename, lineno, 0) stream.append((INCLUDE, (value.strip(), None, []), pos)) elif command != '#': cls = self.get_directive(command) if cls is None: raise BadDirectiveError(command) directive = 0, cls, value, None, (self.filepath, lineno, 0) dirmap[depth] = (directive, len(stream)) depth += 1 offset = end if offset < len(source): text = source[offset:].replace('\\#', '#') for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) return stream
def _parse(self, source, encoding): """Parse the template from text input.""" stream = [] # list of events of the "compiled" template dirmap = {} # temporary mapping of directives to elements depth = 0 source = source.read() if not isinstance(source, unicode): source = source.decode(encoding or 'utf-8', 'replace') offset = 0 lineno = 1 _escape_sub = self._escape_re.sub def _escape_repl(mo): groups = [g for g in mo.groups() if g] if not groups: return '' return groups[0] for idx, mo in enumerate(self._directive_re.finditer(source)): start, end = mo.span(1) if start > offset: text = _escape_sub(_escape_repl, source[offset:start]) for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) lineno += len(text.splitlines()) lineno += len(source[start:end].splitlines()) command, value = mo.group(2, 3) if command == 'include': pos = (self.filename, lineno, 0) value = list(interpolate(value, self.filepath, lineno, 0, lookup=self.lookup)) if len(value) == 1 and value[0][0] is TEXT: value = value[0][1] stream.append((INCLUDE, (value, None, []), pos)) elif command == 'python': if not self.allow_exec: raise TemplateSyntaxError('Python code blocks not allowed', self.filepath, lineno) try: suite = Suite(value, self.filepath, lineno, lookup=self.lookup) except SyntaxError, err: raise TemplateSyntaxError(err, self.filepath, lineno + (err.lineno or 1) - 1) pos = (self.filename, lineno, 0) stream.append((EXEC, suite, pos)) elif command == 'end': depth -= 1 if depth in dirmap: directive, start_offset = dirmap.pop(depth) substream = stream[start_offset:] stream[start_offset:] = [(SUB, ([directive], substream), (self.filepath, lineno, 0))]
def _parse(self, source, encoding): """Parse the template from text input.""" stream = [] # list of events of the "compiled" template dirmap = {} # temporary mapping of directives to elements depth = 0 source = source.read() if not isinstance(source, unicode): source = source.decode(encoding or 'utf-8', 'replace') offset = 0 lineno = 1 for idx, mo in enumerate(self._DIRECTIVE_RE.finditer(source)): start, end = mo.span() if start > offset: text = source[offset:start] for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) lineno += len(text.splitlines()) text = source[start:end].lstrip()[1:] lineno += len(text.splitlines()) directive = text.split(None, 1) if len(directive) > 1: command, value = directive else: command, value = directive[0], None if command == 'end': depth -= 1 if depth in dirmap: directive, start_offset = dirmap.pop(depth) substream = stream[start_offset:] stream[start_offset:] = [(SUB, ([directive], substream), (self.filepath, lineno, 0))] elif command == 'include': pos = (self.filename, lineno, 0) stream.append((INCLUDE, (value.strip(), None, []), pos)) elif command != '#': cls = self.get_directive(command) if cls is None: raise BadDirectiveError(command) directive = 0, cls, value, None, (self.filepath, lineno, 0) dirmap[depth] = (directive, len(stream)) depth += 1 offset = end if offset < len(source): text = source[offset:].replace('\\#', '#') for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) return stream
def test_interpolate_full_mismatched_brackets(self): try: list(interpolate('${{1:2}')) except TemplateSyntaxError as e: pass else: self.fail('Expected TemplateSyntaxError')
def _parse(self, source, encoding): if not isinstance(source, Stream): source = XMLParser(source, filename=self.filename, encoding=encoding) stream = [] for kind, data, pos in source: if kind is TEXT: for kind, data, pos in interpolate(data, self.filepath, pos[1], pos[2], lookup=self.lookup): stream.append((kind, data, pos)) elif kind is PI and data[0] == 'python': if not self.allow_exec: raise TemplateSyntaxError('Python code blocks not allowed', self.filepath, *pos[1:]) try: suite = Suite(data[1], self.filepath, pos[1], lookup=self.lookup) except SyntaxError, err: raise TemplateSyntaxError(err, self.filepath, pos[1] + (err.lineno or 1) - 1, pos[2] + (err.offset or 0)) stream.append((EXEC, suite, pos)) elif kind is COMMENT: if not data.lstrip().startswith('!'): stream.append((kind, data, pos))
def test_interpolate_short_doubleescaped(self): parts = list(interpolate('$$$bla')) self.assertEqual(2, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('$', parts[0][1]) self.assertEqual(EXPR, parts[1][0]) self.assertEqual('bla', parts[1][1].source)
def test_interpolate_mixed2(self): parts = list(interpolate('foo $bar baz')) self.assertEqual(3, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('foo ', parts[0][1]) self.assertEqual(EXPR, parts[1][0]) self.assertEqual('bar', parts[1][1].source) self.assertEqual(TEXT, parts[2][0]) self.assertEqual(' baz', parts[2][1])
def __init__(self, value, template=None, namespaces=None, lineno=-1, offset=-1): Directive.__init__(self, None, template, namespaces, lineno, offset) # allow interpolation inside control attributes raw_value = list(interpolate(value, lineno=lineno, offset=offset)) if all(kind is TEXT for (kind, _, _) in raw_value): self.raw_value = u''.join(event[1] for event in raw_value) else: self.raw_value = raw_value
def _interpolate_attrs(self, stream): for kind, data, pos in stream: if kind is START: # Record any directive attributes in start tags tag, attrs = data new_attrs = [] for name, value in attrs: if value: value = list(interpolate(value, self.filepath, pos[1], pos[2], lookup=self.lookup)) if len(value) == 1 and value[0][0] is TEXT: value = value[0][1] new_attrs.append((name, value)) data = tag, Attrs(new_attrs) yield kind, data, pos
def test_interpolate_full_mismatched_brackets(self): try: list(interpolate('${{1:2}')) except TemplateSyntaxError, e: pass
def test_interpolate_full_nested_brackets(self): parts = list(interpolate('${{1:2}}')) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual('{1:2}', parts[0][1].source)
def test_interpolate_short(self): parts = list(interpolate('$bla')) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual('bla', parts[0][1].source)
def test_interpolate_short_escaped_2(self): parts = list(interpolate('my $$bla = 2')) self.assertEqual(1, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('my $bla = 2', parts[0][1])
def test_interpolate_escaped(self): parts = list(interpolate('$${bla}')) self.assertEqual(1, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('${bla}', parts[0][1])
def test_interpolate_triplequoted(self): parts = list(interpolate('${"""foo\nbar"""}')) self.assertEqual(1, len(parts)) self.assertEqual('"""foo\nbar"""', parts[0][1].source)
def test_interpolate_quoted_brackets_5(self): parts = list(interpolate(r"${'\'}'}")) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual(r"'\'}'", parts[0][1].source)
stream[start_offset:] = [(SUB, ([directive], substream), (self.filepath, lineno, 0))] elif command: cls = self.get_directive(command) if cls is None: raise BadDirectiveError(command) directive = 0, cls, value, None, (self.filepath, lineno, 0) dirmap[depth] = (directive, len(stream)) depth += 1 offset = end if offset < len(source): text = _escape_sub(_escape_repl, source[offset:]) for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) return stream class OldTextTemplate(Template): """Legacy implementation of the old syntax text-based templates. This class is provided in a transition phase for backwards compatibility. New code should use the `NewTextTemplate` class and the improved syntax it provides. >>> tmpl = OldTextTemplate('''Dear $name, ... ... We have the following items for you: ... #for item in items ... * $item
(self.filepath, lineno, 0))] elif command: cls = self.get_directive(command) if cls is None: raise BadDirectiveError(command) directive = 0, cls, value, None, (self.filepath, lineno, 0) dirmap[depth] = (directive, len(stream)) depth += 1 offset = end if offset < len(source): text = _escape_sub(_escape_repl, source[offset:]) for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) return stream class OldTextTemplate(Template): """Legacy implementation of the old syntax text-based templates. This class is provided in a transition phase for backwards compatibility. New code should use the `NewTextTemplate` class and the improved syntax it provides. >>> tmpl = OldTextTemplate('''Dear $name, ... ... We have the following items for you: ... #for item in items
def test_interpolate_short_starting_with_digit(self): parts = list(interpolate('$0bla')) self.assertEqual(1, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('$0bla', parts[0][1])
def test_interpolate_short_containing_dot(self): parts = list(interpolate('$foo.bar')) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual('foo.bar', parts[0][1].source)
def test_interpolate_short_starting_with_underscore(self): parts = list(interpolate('$_bla')) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual('_bla', parts[0][1].source)
def test_interpolate_quoted_brackets_1(self): parts = list(interpolate('${"}"}')) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual('"}"', parts[0][1].source)
def test_interpolate_quoted_brackets_4(self): parts = list(interpolate("${'''}\"\"\"'''}")) self.assertEqual(1, len(parts)) self.assertEqual(EXPR, parts[0][0]) self.assertEqual("'''}\"\"\"'''", parts[0][1].source)
def test_interpolate_string(self): parts = list(interpolate('bla')) self.assertEqual(1, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('bla', parts[0][1])
def _parse(self, source, encoding): streams = [[]] # stacked lists of events of the "compiled" template dirmap = {} # temporary mapping of directives to elements ns_prefix = {} depth = 0 fallbacks = [] includes = [] if not isinstance(source, Stream): source = XMLParser(source, filename=self.filename, encoding=encoding) for kind, data, pos in source: stream = streams[-1] if kind is START_NS: # Strip out the namespace declaration for template directives prefix, uri = data ns_prefix[prefix] = uri if uri not in (self.DIRECTIVE_NAMESPACE, self.XINCLUDE_NAMESPACE): stream.append((kind, data, pos)) elif kind is END_NS: uri = ns_prefix.pop(data, None) if uri and uri not in (self.DIRECTIVE_NAMESPACE, self.XINCLUDE_NAMESPACE): stream.append((kind, data, pos)) elif kind is START: # Record any directive attributes in start tags tag, attrs = data directives = [] strip = False if tag in self.DIRECTIVE_NAMESPACE: cls = self._dir_by_name.get(tag.localname) if cls is None: raise BadDirectiveError(tag.localname, self.filepath, pos[1]) args = dict([(name.localname, value) for name, value in attrs if not name.namespace]) directives.append((cls, args, ns_prefix.copy(), pos)) strip = True new_attrs = [] for name, value in attrs: if name in self.DIRECTIVE_NAMESPACE: cls = self._dir_by_name.get(name.localname) if cls is None: raise BadDirectiveError(name.localname, self.filepath, pos[1]) directives.append((cls, value, ns_prefix.copy(), pos)) else: if value: value = list(interpolate(value, self.filepath, pos[1], pos[2], lookup=self.lookup)) if len(value) == 1 and value[0][0] is TEXT: value = value[0][1] else: value = [(TEXT, u"", pos)] new_attrs.append((name, value)) new_attrs = Attrs(new_attrs) if directives: index = self._dir_order.index directives.sort(lambda a, b: cmp(index(a[0]), index(b[0]))) dirmap[(depth, tag)] = (directives, len(stream), strip) if tag in self.XINCLUDE_NAMESPACE: if tag.localname == "include": include_href = new_attrs.get("href") if not include_href: raise TemplateSyntaxError( "Include misses required " 'attribute "href"', self.filepath, *pos[1:] ) includes.append((include_href, new_attrs.get("parse"))) streams.append([]) elif tag.localname == "fallback": streams.append([]) fallbacks.append(streams[-1]) else: stream.append((kind, (tag, new_attrs), pos)) depth += 1 elif kind is END: depth -= 1 if fallbacks and data == self.XINCLUDE_NAMESPACE["fallback"]: assert streams.pop() is fallbacks[-1] elif data == self.XINCLUDE_NAMESPACE["include"]: fallback = None if len(fallbacks) == len(includes): fallback = fallbacks.pop() streams.pop() # discard anything between the include tags # and the fallback element stream = streams[-1] href, parse = includes.pop() try: cls = {"xml": MarkupTemplate, "text": NewTextTemplate}[parse or "xml"] except KeyError: raise TemplateSyntaxError( 'Invalid value for "parse" ' "attribute of include", self.filepath, *pos[1:] ) stream.append((INCLUDE, (href, cls, fallback), pos)) else: stream.append((kind, data, pos)) # If there have have directive attributes with the corresponding # start tag, move the events inbetween into a "subprogram" if (depth, data) in dirmap: directives, start_offset, strip = dirmap.pop((depth, data)) substream = stream[start_offset:] if strip: substream = substream[1:-1] stream[start_offset:] = [(SUB, (directives, substream), pos)] elif kind is PI and data[0] == "python": if not self.allow_exec: raise TemplateSyntaxError("Python code blocks not allowed", self.filepath, *pos[1:]) try: suite = Suite(data[1], self.filepath, pos[1], lookup=self.lookup) except SyntaxError, err: raise TemplateSyntaxError( err, self.filepath, pos[1] + (err.lineno or 1) - 1, pos[2] + (err.offset or 0) ) stream.append((EXEC, suite, pos)) elif kind is TEXT: for kind, data, pos in interpolate(data, self.filepath, pos[1], pos[2], lookup=self.lookup): stream.append((kind, data, pos))
def test_interpolate_short_starting_with_dot(self): parts = list(interpolate('$.bla')) self.assertEqual(1, len(parts)) self.assertEqual(TEXT, parts[0][0]) self.assertEqual('$.bla', parts[0][1])