annotate genshi/template/markup.py @ 874:fcf378812238

Fix for match template processing involving multiple match directives targetting the same element. Should close #370.
author cmlenz
date Thu, 15 Apr 2010 20:30:09 +0000
parents 1ea88e82713d
children 52d7d6b7b6c1
rev   line source
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
2 #
847
4f9e5e6f1aab 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
3 # Copyright (C) 2006-2009 Edgewall Software
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
5 #
5f2c7782cd8a 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
5f2c7782cd8a 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
5f2c7782cd8a 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.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
9 #
5f2c7782cd8a 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
5f2c7782cd8a 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
5f2c7782cd8a 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/.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
13
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
14 """Markup templating engine."""
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
15
5f2c7782cd8a 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
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
17
636
e0f12a6f3612 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
bd5da099c113 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
5f2c7782cd8a 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
8dd0d34a9fb7 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
bb939ed3058c Added include directive for text templates (#115). Thanks to Alastair for the original patch.
cmlenz
parents: 442
diff changeset
22 TemplateSyntaxError, _apply_directives, \
609
237050080827 Add support for Python code blocks in text templates using the new syntax.
cmlenz
parents: 606
diff changeset
23 EXEC, INCLUDE, SUB
405
bd5da099c113 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
ea71a51e0258 Move string interpolation code into separate module (`genshi.template.interpolation`).
cmlenz
parents: 405
diff changeset
25 from genshi.template.interpolation import interpolate
336
5f2c7782cd8a 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
6a37018199fd * 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
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
28
425
5b248708bbed Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 422
diff changeset
29 __all__ = ['MarkupTemplate']
5b248708bbed Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 422
diff changeset
30 __docformat__ = 'restructuredtext en'
5b248708bbed Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 422
diff changeset
31
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
32
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
33 class MarkupTemplate(Template):
5f2c7782cd8a 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.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
35
5f2c7782cd8a 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/">
5f2c7782cd8a 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>
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
38 ... </ul>''')
853
4376010bb97e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 847
diff changeset
39 >>> print(tmpl.generate(items=[1, 2, 3]))
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
40 <ul>
5f2c7782cd8a 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>
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
42 </ul>
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
43 """
427
68a8308309b9 More API documentation.
cmlenz
parents: 425
diff changeset
44
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
45 DIRECTIVE_NAMESPACE = 'http://genshi.edgewall.org/'
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
46 XINCLUDE_NAMESPACE = 'http://www.w3.org/2001/XInclude'
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
47
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
48 directives = [('def', DefDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
49 ('match', MatchDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
50 ('when', WhenDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
51 ('otherwise', OtherwiseDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
52 ('for', ForDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
53 ('if', IfDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
54 ('choose', ChooseDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
55 ('with', WithDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
56 ('replace', ReplaceDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
57 ('content', ContentDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
58 ('attrs', AttrsDirective),
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
59 ('strip', StripDirective)]
605
bc5faca93699 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
e0f12a6f3612 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
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
62
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
63 def __init__(self, source, filepath=None, filename=None, loader=None,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
64 encoding=None, lookup='strict', allow_exec=True):
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
65 Template.__init__(self, source, filepath=filepath, filename=filename,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
66 loader=loader, encoding=encoding, lookup=lookup,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
67 allow_exec=allow_exec)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
68 self.add_directives(self.DIRECTIVE_NAMESPACE, self)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
69
715
09715f868a73 Enable pickling of `Template` and `Code` objects.
cmlenz
parents: 714
diff changeset
70 def _init_filters(self):
09715f868a73 Enable pickling of `Template` and `Code` objects.
cmlenz
parents: 714
diff changeset
71 Template._init_filters(self)
496
782931585925 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
715
09715f868a73 Enable pickling of `Template` and `Code` objects.
cmlenz
parents: 714
diff changeset
73 if self.loader:
496
782931585925 Fix bug introduced in [575]: includes weren't being processed inside match templates.
cmlenz
parents: 475
diff changeset
74 self.filters.remove(self._include)
609
237050080827 Add support for Python code blocks in text templates using the new syntax.
cmlenz
parents: 606
diff changeset
75 self.filters += [self._match]
715
09715f868a73 Enable pickling of `Template` and `Code` objects.
cmlenz
parents: 714
diff changeset
76 if self.loader:
496
782931585925 Fix bug introduced in [575]: includes weren't being processed inside match templates.
cmlenz
parents: 475
diff changeset
77 self.filters.append(self._include)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
78
374
ca46dc9c7761 `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 def _parse(self, source, encoding):
ca46dc9c7761 `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
80 if not isinstance(source, Stream):
434
e065d7906b68 * Better method to propogate the full path to the template file on parse errors. Supersedes r513.
cmlenz
parents: 427
diff changeset
81 source = XMLParser(source, filename=self.filename,
374
ca46dc9c7761 `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 encoding=encoding)
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
83 stream = []
374
ca46dc9c7761 `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
84
ca46dc9c7761 `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
85 for kind, data, pos in source:
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
86
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
87 if kind is TEXT:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
88 for kind, data, pos in interpolate(data, self.filepath, pos[1],
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
89 pos[2], lookup=self.lookup):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
90 stream.append((kind, data, pos))
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
91
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
92 elif kind is PI and data[0] == 'python':
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
93 if not self.allow_exec:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
94 raise TemplateSyntaxError('Python code blocks not allowed',
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
95 self.filepath, *pos[1:])
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
96 try:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
97 suite = Suite(data[1], self.filepath, pos[1],
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
98 lookup=self.lookup)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
99 except SyntaxError, err:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
100 raise TemplateSyntaxError(err, self.filepath,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
101 pos[1] + (err.lineno or 1) - 1,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
102 pos[2] + (err.offset or 0))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
103 stream.append((EXEC, suite, pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
104
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
105 elif kind is COMMENT:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
106 if not data.lstrip().startswith('!'):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
107 stream.append((kind, data, pos))
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
108
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
109 else:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
110 stream.append((kind, data, pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
111
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
112 return stream
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
113
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
114 def _extract_directives(self, stream, namespace, factory):
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
115 depth = 0
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
116 dirmap = {} # temporary mapping of directives to elements
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
117 new_stream = []
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
118 ns_prefix = {} # namespace prefixes in use
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
119
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
120 for kind, data, pos in stream:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
121
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
122 if kind is START:
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
123 tag, attrs = data
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
124 directives = []
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
125 strip = False
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
126
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
127 if tag.namespace == namespace:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
128 cls = factory.get_directive(tag.localname)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
129 if cls is None:
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
130 raise BadDirectiveError(tag.localname,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
131 self.filepath, pos[1])
552
5458a4e8814c 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
132 args = dict([(name.localname, value) for name, value
5458a4e8814c 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
133 in attrs if not name.namespace])
847
4f9e5e6f1aab 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
134 directives.append((factory.get_directive_index(cls), cls,
4f9e5e6f1aab 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
135 args, ns_prefix.copy(), pos))
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
136 strip = True
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
137
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
138 new_attrs = []
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
139 for name, value in attrs:
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
140 if name.namespace == namespace:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
141 cls = factory.get_directive(name.localname)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
142 if cls is None:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
143 raise BadDirectiveError(name.localname,
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
144 self.filepath, pos[1])
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
145 if type(value) is list and len(value) == 1:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
146 value = value[0][1]
847
4f9e5e6f1aab 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
147 directives.append((factory.get_directive_index(cls),
4f9e5e6f1aab 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
148 cls, value, ns_prefix.copy(), pos))
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
149 else:
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
150 new_attrs.append((name, value))
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
151 new_attrs = Attrs(new_attrs)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
152
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
153 if directives:
847
4f9e5e6f1aab 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
154 directives.sort()
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
155 dirmap[(depth, tag)] = (directives, len(new_stream),
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
156 strip)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
157
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
158 new_stream.append((kind, (tag, new_attrs), pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
159 depth += 1
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
160
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
161 elif kind is END:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
162 depth -= 1
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
163 new_stream.append((kind, data, pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
164
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
165 # If there have have directive attributes with the
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
166 # corresponding start tag, move the events inbetween into
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
167 # a "subprogram"
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
168 if (depth, data) in dirmap:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
169 directives, offset, strip = dirmap.pop((depth, data))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
170 substream = new_stream[offset:]
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
171 if strip:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
172 substream = substream[1:-1]
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
173 new_stream[offset:] = [
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
174 (SUB, (directives, substream), pos)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
175 ]
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
176
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
177 elif kind is SUB:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
178 directives, substream = data
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
179 substream = self._extract_directives(substream, namespace,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
180 factory)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
181
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
182 if len(substream) == 1 and substream[0][0] is SUB:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
183 added_directives, substream = substream[0][1]
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
184 directives += added_directives
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
185
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
186 new_stream.append((kind, (directives, substream), pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
187
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
188 elif kind is START_NS:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
189 # Strip out the namespace declaration for template
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
190 # directives
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
191 prefix, uri = data
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
192 ns_prefix[prefix] = uri
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
193 if uri != namespace:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
194 new_stream.append((kind, data, pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
195
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
196 elif kind is END_NS:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
197 uri = ns_prefix.pop(data, None)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
198 if uri and uri != namespace:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
199 new_stream.append((kind, data, pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
200
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
201 else:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
202 new_stream.append((kind, data, pos))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
203
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
204 return new_stream
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
205
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
206 def _extract_includes(self, stream):
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
207 streams = [[]] # stacked lists of events of the "compiled" template
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
208 prefixes = {}
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
209 fallbacks = []
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
210 includes = []
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
211 xinclude_ns = Namespace(self.XINCLUDE_NAMESPACE)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
212
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
213 for kind, data, pos in stream:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
214 stream = streams[-1]
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
215
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
216 if kind is START:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
217 # Record any directive attributes in start tags
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
218 tag, attrs = data
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
219 if tag in xinclude_ns:
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
220 if tag.localname == 'include':
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
221 include_href = attrs.get('href')
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
222 if not include_href:
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
223 raise TemplateSyntaxError('Include misses required '
422
95089b6e37ca More work to include absolute file paths in exceptions.
cmlenz
parents: 421
diff changeset
224 'attribute "href"',
95089b6e37ca More work to include absolute file paths in exceptions.
cmlenz
parents: 421
diff changeset
225 self.filepath, *pos[1:])
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
226 includes.append((include_href, attrs.get('parse')))
381
a6c2a9cd2e92 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
227 streams.append([])
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
228 elif tag.localname == 'fallback':
590
880b1a75d046 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
229 streams.append([])
880b1a75d046 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
230 fallbacks.append(streams[-1])
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
231 else:
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
232 stream.append((kind, (tag, attrs), pos))
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
233
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
234 elif kind is END:
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
235 if fallbacks and data == xinclude_ns['fallback']:
590
880b1a75d046 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 assert streams.pop() is fallbacks[-1]
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
237 elif data == xinclude_ns['include']:
590
880b1a75d046 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 = None
880b1a75d046 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 if len(fallbacks) == len(includes):
880b1a75d046 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 fallback = fallbacks.pop()
880b1a75d046 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
241 streams.pop() # discard anything between the include tags
880b1a75d046 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
242 # and the fallback element
381
a6c2a9cd2e92 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
243 stream = streams[-1]
610
6a37018199fd * 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 href, parse = includes.pop()
6a37018199fd * 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 try:
6a37018199fd * 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 cls = {
6a37018199fd * 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
247 'xml': MarkupTemplate,
6a37018199fd * 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 'text': NewTextTemplate
6a37018199fd * 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 }[parse or 'xml']
6a37018199fd * 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 except KeyError:
6a37018199fd * 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 raise TemplateSyntaxError('Invalid value for "parse" '
6a37018199fd * 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 'attribute of include',
6a37018199fd * 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
253 self.filepath, *pos[1:])
6a37018199fd * 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
254 stream.append((INCLUDE, (href, cls, fallback), pos))
363
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
255 else:
caf7b68ab5dc Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 362
diff changeset
256 stream.append((kind, data, pos))
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
257
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
258 elif kind is START_NS and data[1] == xinclude_ns:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
259 # Strip out the XInclude namespace
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
260 prefixes[data[0]] = data[1]
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
261
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
262 elif kind is END_NS and data in prefixes:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
263 prefixes.pop(data)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
264
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
265 else:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
266 stream.append((kind, data, pos))
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
267
381
a6c2a9cd2e92 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 assert len(streams) == 1
a6c2a9cd2e92 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
269 return streams[0]
a6c2a9cd2e92 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
270
790
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
271 def _interpolate_attrs(self, stream):
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
272 for kind, data, pos in stream:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
273
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
274 if kind is START:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
275 # Record any directive attributes in start tags
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
276 tag, attrs = data
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
277 new_attrs = []
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
278 for name, value in attrs:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
279 if value:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
280 value = list(interpolate(value, self.filepath, pos[1],
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
281 pos[2], lookup=self.lookup))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
282 if len(value) == 1 and value[0][0] is TEXT:
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
283 value = value[0][1]
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
284 new_attrs.append((name, value))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
285 data = tag, Attrs(new_attrs)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
286
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
287 yield kind, data, pos
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
288
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
289 def _prepare(self, stream):
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
290 return Template._prepare(self,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
291 self._extract_includes(self._interpolate_attrs(stream))
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
292 )
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
293
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
294 def add_directives(self, namespace, factory):
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
295 """Register a custom `DirectiveFactory` for a given namespace.
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
296
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
297 :param namespace: the namespace URI
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
298 :type namespace: `basestring`
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
299 :param factory: the directive factory to register
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
300 :type factory: `DirectiveFactory`
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
301 :since: version 0.6
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
302 """
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
303 assert not self._prepared, 'Too late for adding directives, ' \
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
304 'template already prepared'
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
305 self._stream = self._extract_directives(self._stream, namespace,
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
306 factory)
1b6968d31089 Merged the custom-directives branch back into trunk.
cmlenz
parents: 783
diff changeset
307
766
584aa6bfbe54 Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
308 def _match(self, stream, ctxt, start=0, end=None, **vars):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
309 """Internal stream filter that applies any defined match templates
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
310 to the stream.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
311 """
766
584aa6bfbe54 Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
312 match_templates = ctxt._match_templates
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
313
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
314 tail = []
843
004f81b59d97 Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
315 def _strip(stream, append=tail.append):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
316 depth = 1
843
004f81b59d97 Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
317 next = stream.next
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
318 while 1:
843
004f81b59d97 Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
319 event = next()
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
320 if event[0] is START:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
321 depth += 1
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
322 elif event[0] is END:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
323 depth -= 1
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
324 if depth > 0:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
325 yield event
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
326 else:
843
004f81b59d97 Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
327 append(event)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
328 break
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
329
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
330 for event in stream:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
331
809
92c5d936616b Revert [914] for now, see #293.
cmlenz
parents: 790
diff changeset
332 # We (currently) only care about start and end events for matching
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
333 # We might care about namespace events in the future, though
809
92c5d936616b Revert [914] for now, see #293.
cmlenz
parents: 790
diff changeset
334 if not match_templates or (event[0] is not START and
92c5d936616b Revert [914] for now, see #293.
cmlenz
parents: 790
diff changeset
335 event[0] is not END):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
336 yield event
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
337 continue
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
338
602
509b3a5e765e Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
339 for idx, (test, path, template, hints, namespaces, directives) \
509b3a5e765e Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
340 in enumerate(match_templates):
766
584aa6bfbe54 Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
341 if idx < start or end is not None and idx >= end:
758
0ea32786b624 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
342 continue
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
343
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
344 if test(event, namespaces, ctxt) is True:
602
509b3a5e765e Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
345 if 'match_once' in hints:
509b3a5e765e Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
346 del match_templates[idx]
509b3a5e765e Add runtime optimization hints for match templates.
cmlenz
parents: 601
diff changeset
347 idx -= 1
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
348
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
349 # Let the remaining match templates know about the event so
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
350 # they get a chance to update their internal state
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
351 for test in [mt[0] for mt in match_templates[idx + 1:]]:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
352 test(event, namespaces, ctxt, updateonly=True)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
353
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
354 # Consume and store all events until an end event
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
355 # corresponding to this start event is encountered
766
584aa6bfbe54 Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
356 pre_end = idx + 1
694
812671b40022 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
357 if 'match_once' not in hints and 'not_recursive' in hints:
766
584aa6bfbe54 Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
358 pre_end -= 1
700
8d079cee6822 Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
359 inner = _strip(stream)
766
584aa6bfbe54 Fix bug where in some cases match templates would incorrectly applied multiple times.
cmlenz
parents: 758
diff changeset
360 if pre_end > 0:
870
1ea88e82713d Apply patch Felix Schwarz that finally fixes the duplicated output in match template processing. Thanks so much!
cmlenz
parents: 853
diff changeset
361 inner = self._match(inner, ctxt, start=start,
1ea88e82713d Apply patch Felix Schwarz that finally fixes the duplicated output in match template processing. Thanks so much!
cmlenz
parents: 853
diff changeset
362 end=pre_end, **vars)
700
8d079cee6822 Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
363 content = self._include(chain([event], inner, tail), ctxt)
8d079cee6822 Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
364 if 'not_buffered' not in hints:
8d079cee6822 Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
365 content = list(content)
843
004f81b59d97 Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
366 content = Stream(content)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
367
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
368 # Make the select() function available in the body of the
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
369 # match template
771
b4c973fbe6f5 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 = [False]
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
371 def select(path):
771
b4c973fbe6f5 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
372 selected[0] = True
843
004f81b59d97 Refactored the template flattening method to be less recursive.
cmlenz
parents: 827
diff changeset
373 return content.select(path, namespaces, ctxt)
700
8d079cee6822 Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
374 vars = dict(select=select)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
375
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
376 # Recursively process the output
700
8d079cee6822 Add option for unbuffered match template processing, which could cause excessive memory usage. Closes #190.
cmlenz
parents: 694
diff changeset
377 template = _apply_directives(template, directives, ctxt,
827
0319a8874510 Avoid varargs on internal functions in template processing for slightly better performance.
cmlenz
parents: 814
diff changeset
378 vars)
813
3047d5d83ccc Merge the internal template filters `_eval` and `_exec` into the `_flatten` function for slightly better performance.
cmlenz
parents: 809
diff changeset
379 for event in self._match(self._flatten(template, ctxt,
3047d5d83ccc Merge the internal template filters `_eval` and `_exec` into the `_flatten` function for slightly better performance.
cmlenz
parents: 809
diff changeset
380 **vars),
3047d5d83ccc Merge the internal template filters `_eval` and `_exec` into the `_flatten` function for slightly better performance.
cmlenz
parents: 809
diff changeset
381 ctxt, start=idx + 1, **vars):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
382 yield event
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
383
771
b4c973fbe6f5 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 # If the match template did not actually call select to
b4c973fbe6f5 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 # consume the matched stream, the original events need to
b4c973fbe6f5 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 # be consumed here or they'll get appended to the output
b4c973fbe6f5 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 if not selected[0]:
b4c973fbe6f5 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 for event in content:
b4c973fbe6f5 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
389 pass
b4c973fbe6f5 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
390
b4c973fbe6f5 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
391 # Let the remaining match templates know about the last
b4c973fbe6f5 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 # event in the matched content, so they can update their
b4c973fbe6f5 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
393 # internal state accordingly
874
fcf378812238 Fix for match template processing involving multiple match directives targetting the same element. Should close #370.
cmlenz
parents: 870
diff changeset
394 for test in [mt[0] for mt in match_templates[idx + 1:]]:
771
b4c973fbe6f5 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 test(tail[0], namespaces, ctxt, updateonly=True)
b4c973fbe6f5 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
396
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
397 break
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
398
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
399 else: # no matches
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
400 yield event
Copyright (C) 2012-2017 Edgewall Software