annotate genshi/template/markup.py @ 1031:5dccab13ec85 trunk

Fix infinite recursion in template inlining (fixes #584).
author hodgestar
date Wed, 19 Mar 2014 13:52:01 +0000
parents cdb5d435d237
children
rev   line source
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
2 #
897
64f04a2c5e66 Update changelog and copyright years.
cmlenz
parents: 880
diff changeset
3 # Copyright (C) 2006-2010 Edgewall Software
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
4 # All rights reserved.
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
5 #
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
8 # are also available at http://genshi.edgewall.org/wiki/License.
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
9 #
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
12 # history and logs, available at http://genshi.edgewall.org/log/.
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
13
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
14 """Markup templating engine."""
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
15
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
16 from itertools import chain
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
17
636
699601cce3cc Follow-up to [751]: applying the optimization to text templates was actually slowing them down, so only do it for markup templates.
cmlenz
parents: 629
diff changeset
18 from genshi.core import Attrs, Markup, Namespace, Stream, StreamEventKind
405
5340931530e2 Support for Python code blocks using the `<?python ?>` processing instruction. Closes #84.
cmlenz
parents: 400
diff changeset
19 from genshi.core import START, END, START_NS, END_NS, TEXT, PI, COMMENT
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
20 from genshi.input import XMLParser
400
e29a94b3ba0c Renamed `genshi.template.core` to `genshi.template.base`, mainly to avoid confusion with `genshi.core`.
cmlenz
parents: 381
diff changeset
21 from genshi.template.base import BadDirectiveError, Template, \
475
b373f80f7763 Added include directive for text templates (#115). Thanks to Alastair for the original patch.
cmlenz
parents: 442
diff changeset
22 TemplateSyntaxError, _apply_directives, \
609
6d4877844e28 Add support for Python code blocks in text templates using the new syntax.
cmlenz
parents: 606
diff changeset
23 EXEC, INCLUDE, SUB
405
5340931530e2 Support for Python code blocks using the `<?python ?>` processing instruction. Closes #84.
cmlenz
parents: 400
diff changeset
24 from genshi.template.eval import Suite
407
f37d8e6acdf2 Move string interpolation code into separate module (`genshi.template.interpolation`).
cmlenz
parents: 405
diff changeset
25 from genshi.template.interpolation import interpolate
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
26 from genshi.template.directives import *
610
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
27 from genshi.template.text import NewTextTemplate
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
28
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 422
diff changeset
29 __all__ = ['MarkupTemplate']
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 422
diff changeset
30 __docformat__ = 'restructuredtext en'
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 422
diff changeset
31
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
32
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
33 class MarkupTemplate(Template):
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
34 """Implementation of the template language for XML-based templates.
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
35
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
36 >>> tmpl = MarkupTemplate('''<ul xmlns:py="http://genshi.edgewall.org/">
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
37 ... <li py:for="item in items">${item}</li>
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
38 ... </ul>''')
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 847
diff changeset
39 >>> print(tmpl.generate(items=[1, 2, 3]))
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
40 <ul>
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
41 <li>1</li><li>2</li><li>3</li>
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
42 </ul>
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
43 """
427
55c574767df2 More API documentation.
cmlenz
parents: 425
diff changeset
44
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
45 DIRECTIVE_NAMESPACE = 'http://genshi.edgewall.org/'
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
46 XINCLUDE_NAMESPACE = 'http://www.w3.org/2001/XInclude'
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
47
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
48 directives = [('def', DefDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
49 ('match', MatchDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
50 ('when', WhenDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
51 ('otherwise', OtherwiseDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
52 ('for', ForDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
53 ('if', IfDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
54 ('choose', ChooseDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
55 ('with', WithDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
56 ('replace', ReplaceDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
57 ('content', ContentDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
58 ('attrs', AttrsDirective),
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
59 ('strip', StripDirective)]
605
d0345c64da65 Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents: 602
diff changeset
60 serializer = 'xml'
636
699601cce3cc Follow-up to [751]: applying the optimization to text templates was actually slowing them down, so only do it for markup templates.
cmlenz
parents: 629
diff changeset
61 _number_conv = Markup
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
62
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
63 def __init__(self, source, filepath=None, filename=None, loader=None,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
64 encoding=None, lookup='strict', allow_exec=True):
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
65 Template.__init__(self, source, filepath=filepath, filename=filename,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
66 loader=loader, encoding=encoding, lookup=lookup,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
67 allow_exec=allow_exec)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
68 self.add_directives(self.DIRECTIVE_NAMESPACE, self)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
69
715
b5bd8c109209 Enable pickling of `Template` and `Code` objects.
cmlenz
parents: 714
diff changeset
70 def _init_filters(self):
b5bd8c109209 Enable pickling of `Template` and `Code` objects.
cmlenz
parents: 714
diff changeset
71 Template._init_filters(self)
496
f56046e4b0de Fix bug introduced in [575]: includes weren't being processed inside match templates.
cmlenz
parents: 475
diff changeset
72 # Make sure the include filter comes after the match filter
876
124b57282f81 Templates instantiated without a loader now get an implicit loader based on their file path, or the current directory as a fallback. Closes #320.
cmlenz
parents: 874
diff changeset
73 self.filters.remove(self._include)
124b57282f81 Templates instantiated without a loader now get an implicit loader based on their file path, or the current directory as a fallback. Closes #320.
cmlenz
parents: 874
diff changeset
74 self.filters += [self._match, self._include]
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
75
374
b146277eb54a `MarkupTemplate`s can now be instantiated from markup streams, in addition to strings and file-like objects. Thanks to David Fraser for the patch. Closes #69.
cmlenz
parents: 364
diff changeset
76 def _parse(self, source, encoding):
b146277eb54a `MarkupTemplate`s can now be instantiated from markup streams, in addition to strings and file-like objects. Thanks to David Fraser for the patch. Closes #69.
cmlenz
parents: 364
diff changeset
77 if not isinstance(source, Stream):
434
5692bc32ba5f * Better method to propogate the full path to the template file on parse errors. Supersedes r513.
cmlenz
parents: 427
diff changeset
78 source = XMLParser(source, filename=self.filename,
374
b146277eb54a `MarkupTemplate`s can now be instantiated from markup streams, in addition to strings and file-like objects. Thanks to David Fraser for the patch. Closes #69.
cmlenz
parents: 364
diff changeset
79 encoding=encoding)
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
80 stream = []
374
b146277eb54a `MarkupTemplate`s can now be instantiated from markup streams, in addition to strings and file-like objects. Thanks to David Fraser for the patch. Closes #69.
cmlenz
parents: 364
diff changeset
81
b146277eb54a `MarkupTemplate`s can now be instantiated from markup streams, in addition to strings and file-like objects. Thanks to David Fraser for the patch. Closes #69.
cmlenz
parents: 364
diff changeset
82 for kind, data, pos in source:
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
83
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
84 if kind is TEXT:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
85 for kind, data, pos in interpolate(data, self.filepath, pos[1],
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
86 pos[2], lookup=self.lookup):
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
87 stream.append((kind, data, pos))
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
88
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
89 elif kind is PI and data[0] == 'python':
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
90 if not self.allow_exec:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
91 raise TemplateSyntaxError('Python code blocks not allowed',
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
92 self.filepath, *pos[1:])
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
93 try:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
94 suite = Suite(data[1], self.filepath, pos[1],
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
95 lookup=self.lookup)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
96 except SyntaxError, err:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
97 raise TemplateSyntaxError(err, self.filepath,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
98 pos[1] + (err.lineno or 1) - 1,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
99 pos[2] + (err.offset or 0))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
100 stream.append((EXEC, suite, pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
101
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
102 elif kind is COMMENT:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
103 if not data.lstrip().startswith('!'):
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
104 stream.append((kind, data, pos))
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
105
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
106 else:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
107 stream.append((kind, data, pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
108
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
109 return stream
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
110
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
111 def _extract_directives(self, stream, namespace, factory):
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
112 depth = 0
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
113 dirmap = {} # temporary mapping of directives to elements
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
114 new_stream = []
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
115 ns_prefix = {} # namespace prefixes in use
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
116
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
117 for kind, data, pos in stream:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
118
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
119 if kind is START:
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
120 tag, attrs = data
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
121 directives = []
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
122 strip = False
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
123
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
124 if tag.namespace == namespace:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
125 cls = factory.get_directive(tag.localname)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
126 if cls is None:
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
127 raise BadDirectiveError(tag.localname,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
128 self.filepath, pos[1])
552
b59d99d2f631 For directives used as elements, pass all attributes without a namespace to the directive class. This enables adding optional extra attributes to directives.
cmlenz
parents: 545
diff changeset
129 args = dict([(name.localname, value) for name, value
b59d99d2f631 For directives used as elements, pass all attributes without a namespace to the directive class. This enables adding optional extra attributes to directives.
cmlenz
parents: 545
diff changeset
130 in attrs if not name.namespace])
847
e16447a9605f Backported a couple of templating core changes from the advanced-i18n branch, in particular considering the determination of directive ordering../set
cmlenz
parents: 843
diff changeset
131 directives.append((factory.get_directive_index(cls), cls,
e16447a9605f Backported a couple of templating core changes from the advanced-i18n branch, in particular considering the determination of directive ordering../set
cmlenz
parents: 843
diff changeset
132 args, ns_prefix.copy(), pos))
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
133 strip = True
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
134
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
135 new_attrs = []
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
136 for name, value in attrs:
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
137 if name.namespace == namespace:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
138 cls = factory.get_directive(name.localname)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
139 if cls is None:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
140 raise BadDirectiveError(name.localname,
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
141 self.filepath, pos[1])
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
142 if type(value) is list and len(value) == 1:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
143 value = value[0][1]
847
e16447a9605f Backported a couple of templating core changes from the advanced-i18n branch, in particular considering the determination of directive ordering../set
cmlenz
parents: 843
diff changeset
144 directives.append((factory.get_directive_index(cls),
e16447a9605f Backported a couple of templating core changes from the advanced-i18n branch, in particular considering the determination of directive ordering../set
cmlenz
parents: 843
diff changeset
145 cls, value, ns_prefix.copy(), pos))
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
146 else:
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
147 new_attrs.append((name, value))
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
148 new_attrs = Attrs(new_attrs)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
149
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
150 if directives:
847
e16447a9605f Backported a couple of templating core changes from the advanced-i18n branch, in particular considering the determination of directive ordering../set
cmlenz
parents: 843
diff changeset
151 directives.sort()
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
152 dirmap[(depth, tag)] = (directives, len(new_stream),
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
153 strip)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
154
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
155 new_stream.append((kind, (tag, new_attrs), pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
156 depth += 1
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
157
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
158 elif kind is END:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
159 depth -= 1
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
160 new_stream.append((kind, data, pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
161
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
162 # If there have have directive attributes with the
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
163 # corresponding start tag, move the events inbetween into
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
164 # a "subprogram"
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
165 if (depth, data) in dirmap:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
166 directives, offset, strip = dirmap.pop((depth, data))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
167 substream = new_stream[offset:]
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
168 if strip:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
169 substream = substream[1:-1]
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
170 new_stream[offset:] = [
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
171 (SUB, (directives, substream), pos)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
172 ]
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
173
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
174 elif kind is SUB:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
175 directives, substream = data
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
176 substream = self._extract_directives(substream, namespace,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
177 factory)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
178
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
179 if len(substream) == 1 and substream[0][0] is SUB:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
180 added_directives, substream = substream[0][1]
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
181 directives += added_directives
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
182
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
183 new_stream.append((kind, (directives, substream), pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
184
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
185 elif kind is START_NS:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
186 # Strip out the namespace declaration for template
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
187 # directives
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
188 prefix, uri = data
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
189 ns_prefix[prefix] = uri
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
190 if uri != namespace:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
191 new_stream.append((kind, data, pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
192
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
193 elif kind is END_NS:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
194 uri = ns_prefix.pop(data, None)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
195 if uri and uri != namespace:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
196 new_stream.append((kind, data, pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
197
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
198 else:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
199 new_stream.append((kind, data, pos))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
200
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
201 return new_stream
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
202
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
203 def _extract_includes(self, stream):
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
204 streams = [[]] # stacked lists of events of the "compiled" template
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
205 prefixes = {}
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
206 fallbacks = []
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
207 includes = []
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
208 xinclude_ns = Namespace(self.XINCLUDE_NAMESPACE)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
209
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
210 for kind, data, pos in stream:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
211 stream = streams[-1]
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
212
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
213 if kind is START:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
214 # Record any directive attributes in start tags
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
215 tag, attrs = data
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
216 if tag in xinclude_ns:
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
217 if tag.localname == 'include':
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
218 include_href = attrs.get('href')
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
219 if not include_href:
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
220 raise TemplateSyntaxError('Include misses required '
422
5d08a744636e More work to include absolute file paths in exceptions.
cmlenz
parents: 421
diff changeset
221 'attribute "href"',
5d08a744636e More work to include absolute file paths in exceptions.
cmlenz
parents: 421
diff changeset
222 self.filepath, *pos[1:])
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
223 includes.append((include_href, attrs.get('parse')))
381
b9fc7a1f76ca Fix for #80: fallback only shown when the template to include wasn't found. In addition, the nesting of includes and fallback content should work correctly, and directives/expressions/etc inside fallback content are processed. Thanks to Christian Boos for the original patch and unit tests.
cmlenz
parents: 374
diff changeset
224 streams.append([])
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
225 elif tag.localname == 'fallback':
590
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
226 streams.append([])
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
227 fallbacks.append(streams[-1])
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
228 else:
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
229 stream.append((kind, (tag, attrs), pos))
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
230
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
231 elif kind is END:
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
232 if fallbacks and data == xinclude_ns['fallback']:
1012
cdb5d435d237 Fix assert with side-effect in xi:fallback directive processing (see #565).
hodgestar
parents: 945
diff changeset
233 fallback_stream = streams.pop()
cdb5d435d237 Fix assert with side-effect in xi:fallback directive processing (see #565).
hodgestar
parents: 945
diff changeset
234 assert fallback_stream is fallbacks[-1]
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
235 elif data == xinclude_ns['include']:
590
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
236 fallback = None
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
237 if len(fallbacks) == len(includes):
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
238 fallback = fallbacks.pop()
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
239 streams.pop() # discard anything between the include tags
36b5a03534a0 Fix includes so that they again raise an exception when the included template is not found and no fallback has been provided.
cmlenz
parents: 552
diff changeset
240 # and the fallback element
381
b9fc7a1f76ca Fix for #80: fallback only shown when the template to include wasn't found. In addition, the nesting of includes and fallback content should work correctly, and directives/expressions/etc inside fallback content are processed. Thanks to Christian Boos for the original patch and unit tests.
cmlenz
parents: 374
diff changeset
241 stream = streams[-1]
610
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
242 href, parse = includes.pop()
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
243 try:
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
244 cls = {
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
245 'xml': MarkupTemplate,
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
246 'text': NewTextTemplate
880
aaaf6bb7bf96 Default XInclude-included template class to the class of the including template. Closes #302.
cmlenz
parents: 876
diff changeset
247 }.get(parse) or self.__class__
610
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
248 except KeyError:
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
249 raise TemplateSyntaxError('Invalid value for "parse" '
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
250 'attribute of include',
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
251 self.filepath, *pos[1:])
5e358de79e4c * XInclude elements in markup templates now support the `parse` attribute; when set to "xml" (the default), the include is processed as before, but when set to "text", the included template is parsed as a text template using the new syntax (ticket #101).
cmlenz
parents: 609
diff changeset
252 stream.append((INCLUDE, (href, cls, fallback), pos))
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
253 else:
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
254 stream.append((kind, data, pos))
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
255
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
256 elif kind is START_NS and data[1] == xinclude_ns:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
257 # Strip out the XInclude namespace
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
258 prefixes[data[0]] = data[1]
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
259
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
260 elif kind is END_NS and data in prefixes:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
261 prefixes.pop(data)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
262
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
263 else:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
264 stream.append((kind, data, pos))
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
265
381
b9fc7a1f76ca Fix for #80: fallback only shown when the template to include wasn't found. In addition, the nesting of includes and fallback content should work correctly, and directives/expressions/etc inside fallback content are processed. Thanks to Christian Boos for the original patch and unit tests.
cmlenz
parents: 374
diff changeset
266 assert len(streams) == 1
b9fc7a1f76ca Fix for #80: fallback only shown when the template to include wasn't found. In addition, the nesting of includes and fallback content should work correctly, and directives/expressions/etc inside fallback content are processed. Thanks to Christian Boos for the original patch and unit tests.
cmlenz
parents: 374
diff changeset
267 return streams[0]
b9fc7a1f76ca Fix for #80: fallback only shown when the template to include wasn't found. In addition, the nesting of includes and fallback content should work correctly, and directives/expressions/etc inside fallback content are processed. Thanks to Christian Boos for the original patch and unit tests.
cmlenz
parents: 374
diff changeset
268
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
269 def _interpolate_attrs(self, stream):
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
270 for kind, data, pos in stream:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
271
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
272 if kind is START:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
273 # Record any directive attributes in start tags
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
274 tag, attrs = data
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
275 new_attrs = []
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
276 for name, value in attrs:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
277 if value:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
278 value = list(interpolate(value, self.filepath, pos[1],
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
279 pos[2], lookup=self.lookup))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
280 if len(value) == 1 and value[0][0] is TEXT:
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
281 value = value[0][1]
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
282 new_attrs.append((name, value))
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
283 data = tag, Attrs(new_attrs)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
284
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
285 yield kind, data, pos
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
286
1031
5dccab13ec85 Fix infinite recursion in template inlining (fixes #584).
hodgestar
parents: 1012
diff changeset
287 def _prepare(self, stream, inlined=None):
5dccab13ec85 Fix infinite recursion in template inlining (fixes #584).
hodgestar
parents: 1012
diff changeset
288 return Template._prepare(
5dccab13ec85 Fix infinite recursion in template inlining (fixes #584).
hodgestar
parents: 1012
diff changeset
289 self, self._extract_includes(self._interpolate_attrs(stream)),
5dccab13ec85 Fix infinite recursion in template inlining (fixes #584).
hodgestar
parents: 1012
diff changeset
290 inlined=inlined)
790
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
291
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
292 def add_directives(self, namespace, factory):
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
293 """Register a custom `DirectiveFactory` for a given namespace.
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
294
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
295 :param namespace: the namespace URI
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
296 :type namespace: `basestring`
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
297 :param factory: the directive factory to register
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
298 :type factory: `DirectiveFactory`
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
299 :since: version 0.6
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
300 """
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
301 assert not self._prepared, 'Too late for adding directives, ' \
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
302 'template already prepared'
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
303 self._stream = self._extract_directives(self._stream, namespace,
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
304 factory)
da90cee22560 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
305
766
b5b4b465e84c Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
306 def _match(self, stream, ctxt, start=0, end=None, **vars):
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
307 """Internal stream filter that applies any defined match templates
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
308 to the stream.
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
309 """
766
b5b4b465e84c Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
310 match_templates = ctxt._match_templates
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
311
924
763d6a185b49 Pull up r1146 to trunk.
jruigrok
parents: 897
diff changeset
312 def _strip(stream, append):
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
313 depth = 1
843
d10e5bceaa1f Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
314 next = stream.next
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
315 while 1:
843
d10e5bceaa1f Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
316 event = next()
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
317 if event[0] is START:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
318 depth += 1
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
319 elif event[0] is END:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
320 depth -= 1
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
321 if depth > 0:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
322 yield event
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
323 else:
843
d10e5bceaa1f Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
324 append(event)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
325 break
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
326
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
327 for event in stream:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
328
809
68f652fa9e6c Revert [914] for now, see #293.
cmlenz
parents: 790
diff changeset
329 # We (currently) only care about start and end events for matching
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
330 # We might care about namespace events in the future, though
809
68f652fa9e6c Revert [914] for now, see #293.
cmlenz
parents: 790
diff changeset
331 if not match_templates or (event[0] is not START and
68f652fa9e6c Revert [914] for now, see #293.
cmlenz
parents: 790
diff changeset
332 event[0] is not END):
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
333 yield event
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
334 continue
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
335
602
d7b957e92ea9 Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
336 for idx, (test, path, template, hints, namespaces, directives) \
d7b957e92ea9 Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
337 in enumerate(match_templates):
766
b5b4b465e84c Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
338 if idx < start or end is not None and idx >= end:
758
9f28d17b1f72 Fix problem with nested match templates not being applied when buffering on the outer `py:match` is disabled. Thanks to Erik Bray for reporting the problem and providing a test case.
cmlenz
parents: 719
diff changeset
339 continue
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
340
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
341 if test(event, namespaces, ctxt) is True:
602
d7b957e92ea9 Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
342 if 'match_once' in hints:
d7b957e92ea9 Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
343 del match_templates[idx]
d7b957e92ea9 Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
344 idx -= 1
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
345
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
346 # Let the remaining match templates know about the event so
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
347 # they get a chance to update their internal state
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
348 for test in [mt[0] for mt in match_templates[idx + 1:]]:
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
349 test(event, namespaces, ctxt, updateonly=True)
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
350
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
351 # Consume and store all events until an end event
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
352 # corresponding to this start event is encountered
766
b5b4b465e84c Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
353 pre_end = idx + 1
694
07e3f6f0ef57 Match templates are now applied in a more controlled fashion: in the order they are declared in the template source, all match templates up to (and including) the matching template itself are applied to the matched content, whereas the match templates declared after the matching template are only applied to the generated content. Fixes #186. Many thanks to Matt Chaput for reporting the problem and providing a test case.
cmlenz
parents: 650
diff changeset
354 if 'match_once' not in hints and 'not_recursive' in hints:
766
b5b4b465e84c Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
355 pre_end -= 1
924
763d6a185b49 Pull up r1146 to trunk.
jruigrok
parents: 897
diff changeset
356 tail = []
763d6a185b49 Pull up r1146 to trunk.
jruigrok
parents: 897
diff changeset
357 inner = _strip(stream, tail.append)
766
b5b4b465e84c Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
358 if pre_end > 0:
870
c52e2bf11edc Apply patch Felix Schwarz that finally fixes the duplicated output in match template processing. Thanks so much!
cmlenz
parents: 853
diff changeset
359 inner = self._match(inner, ctxt, start=start,
c52e2bf11edc Apply patch Felix Schwarz that finally fixes the duplicated output in match template processing. Thanks so much!
cmlenz
parents: 853
diff changeset
360 end=pre_end, **vars)
700
08f22328303d Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
361 content = self._include(chain([event], inner, tail), ctxt)
08f22328303d Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
362 if 'not_buffered' not in hints:
08f22328303d Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
363 content = list(content)
843
d10e5bceaa1f Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
364 content = Stream(content)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
365
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
366 # Make the select() function available in the body of the
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
367 # match template
771
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
368 selected = [False]
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
369 def select(path):
771
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
370 selected[0] = True
843
d10e5bceaa1f Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
371 return content.select(path, namespaces, ctxt)
700
08f22328303d Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
372 vars = dict(select=select)
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
373
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
374 # Recursively process the output
700
08f22328303d Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
375 template = _apply_directives(template, directives, ctxt,
827
bebc68529176 Avoid varargs on internal functions in template processing for slightly better performance.
cmlenz
parents: 814
diff changeset
376 vars)
813
ae8727f7e1e1 Merge the internal template filters `_eval` and `_exec` into the `_flatten` function for slightly better performance.
cmlenz
parents: 809
diff changeset
377 for event in self._match(self._flatten(template, ctxt,
ae8727f7e1e1 Merge the internal template filters `_eval` and `_exec` into the `_flatten` function for slightly better performance.
cmlenz
parents: 809
diff changeset
378 **vars),
ae8727f7e1e1 Merge the internal template filters `_eval` and `_exec` into the `_flatten` function for slightly better performance.
cmlenz
parents: 809
diff changeset
379 ctxt, start=idx + 1, **vars):
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
380 yield event
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
381
771
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
382 # If the match template did not actually call select to
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
383 # consume the matched stream, the original events need to
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
384 # be consumed here or they'll get appended to the output
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
385 if not selected[0]:
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
386 for event in content:
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
387 pass
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
388
945
d1edb246cc61 Fix out-by-one error introduced in r1097 that led to match templates missing the last event from the stream they were processing.
hodgestar
parents: 924
diff changeset
389 # Let this match template and the remaining match
d1edb246cc61 Fix out-by-one error introduced in r1097 that led to match templates missing the last event from the stream they were processing.
hodgestar
parents: 924
diff changeset
390 # templates know about the last event in the
d1edb246cc61 Fix out-by-one error introduced in r1097 that led to match templates missing the last event from the stream they were processing.
hodgestar
parents: 924
diff changeset
391 # matched content, so they can update their
771
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
392 # internal state accordingly
945
d1edb246cc61 Fix out-by-one error introduced in r1097 that led to match templates missing the last event from the stream they were processing.
hodgestar
parents: 924
diff changeset
393 for test in [mt[0] for mt in match_templates[idx:]]:
771
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
394 test(tail[0], namespaces, ctxt, updateonly=True)
2c2e9e685424 Unbuffered match templates could result in parts of the matched content being included in the output if the match template didn't actually consume it via one or more calls to the `select()` function. Closes #243. Thanks to Felix Schwarz for the report and test case.
cmlenz
parents: 766
diff changeset
395
336
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
396 break
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
397
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
398 else: # no matches
7763f7aec949 Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
399 yield event
Copyright (C) 2012-2017 Edgewall Software