annotate markup/filters.py @ 17:74cc70129d04 trunk

Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs. Also, output filters are now applied in the `Stream.serialize()` method instead of by the `Template.generate()` method, which just makes more sense.
author cmlenz
date Sun, 18 Jun 2006 22:33:33 +0000
parents b3edbde541c4
children 5420cfe42d36
rev   line source
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
2 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
3 # Copyright (C) 2006 Christopher Lenz
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
5 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
8 # are also available at http://trac.edgewall.com/license.html.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
9 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
12 # history and logs, available at http://projects.edgewall.com/trac/.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
13
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
14 """Implementation of a number of stream filters."""
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
15
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
16 try:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
17 frozenset
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
18 except NameError:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
19 from sets import ImmutableSet as frozenset
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
20 import re
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
21
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
22 from markup.core import Attributes, Markup, Stream
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
23 from markup.path import Path
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
24
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
25 __all__ = ['IncludeFilter', 'WhitespaceFilter', 'HTMLSanitizer']
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
26
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
27
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
28 class IncludeFilter(object):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
29 """Template filter providing (very) basic XInclude support
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
30 (see http://www.w3.org/TR/xinclude/) in templates.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
31 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
32
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
33 _NAMESPACE = 'http://www.w3.org/2001/XInclude'
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
34
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
35 def __init__(self, loader):
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
36 """Initialize the filter.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
37
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
38 @param loader: the `TemplateLoader` to use for resolving references to
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
39 external template files
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
40 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
41 self.loader = loader
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
42
12
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
43 def __call__(self, stream, ctxt=None, ns_prefixes=None):
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
44 """Filter the stream, processing any XInclude directives it may
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
45 contain.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
46
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
47 @param ctxt: the template context
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
48 @param stream: the markup event stream to filter
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
49 """
12
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
50 from markup.template import Template, TemplateError, TemplateNotFound
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
51
12
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
52 if ns_prefixes is None:
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
53 ns_prefixes = []
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
54 in_fallback = False
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
55 include_href, fallback_stream = None, None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
56 indent = 0
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
57
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
58 for kind, data, pos in stream:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
59
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
60 if kind is Stream.START and data[0].namespace == self._NAMESPACE \
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
61 and not in_fallback:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
62 tag, attrib = data
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
63 if tag.localname == 'include':
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
64 include_href = attrib.get('href')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
65 indent = pos[1]
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
66 elif tag.localname == 'fallback':
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
67 in_fallback = True
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
68 fallback_stream = []
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
69
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
70 elif kind is Stream.END and data.namespace == self._NAMESPACE:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
71 if data.localname == 'include':
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
72 try:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
73 if not include_href:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
74 raise TemplateError('Include misses required '
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
75 'attribute "href"')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
76 template = self.loader.load(include_href)
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
77 for event in template.generate(ctxt):
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
78 yield event
13
f9001cd6785b Match directives should now also be applied when included indirectly.
cmlenz
parents: 12
diff changeset
79
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
80 except TemplateNotFound:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
81 if fallback_stream is None:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
82 raise
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
83 for event in fallback_stream:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
84 yield event
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
85
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
86 include_href = None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
87 fallback_stream = None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
88 indent = 0
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
89
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
90 elif data.localname == 'fallback':
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
91 in_fallback = False
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
92
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
93 elif in_fallback:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
94 fallback_stream.append((kind, data, pos))
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
95
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
96 elif kind is Stream.START_NS and data[1] == self._NAMESPACE:
12
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
97 ns_prefixes.append(data[0])
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
98
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
99 elif kind is Stream.END_NS and data in ns_prefixes:
97423376736e Make the XInclude filter track namespace context, to enable it to omit `END_NS` events for the XInclude namespace.
cmlenz
parents: 10
diff changeset
100 ns_prefixes.pop()
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
101
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
102 else:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
103 yield kind, data, pos
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
104
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
105
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
106 class WhitespaceFilter(object):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
107 """A filter that removes extraneous white space from the stream.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
108
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
109 Todo:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
110 * Support for xml:space
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
111 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
112
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
113 _TRAILING_SPACE = re.compile('[ \t]+(?=\n)')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
114 _LINE_COLLAPSE = re.compile('\n{2,}')
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
115
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
116 def __call__(self, stream, ctxt=None):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
117 textbuf = []
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
118 prev_kind = None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
119 for kind, data, pos in stream:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
120 if kind is Stream.TEXT:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
121 textbuf.append(data)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
122 elif prev_kind is Stream.TEXT:
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
123 text = Markup('').join(textbuf)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
124 text = self._TRAILING_SPACE.sub('', text)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
125 text = self._LINE_COLLAPSE.sub('\n', text)
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
126 yield Stream.TEXT, Markup(text), pos
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
127 del textbuf[:]
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
128 prev_kind = kind
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
129 if kind is not Stream.TEXT:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
130 yield kind, data, pos
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
131
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
132 if textbuf:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
133 text = self._LINE_COLLAPSE.sub('\n', ''.join(textbuf))
17
74cc70129d04 Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents: 15
diff changeset
134 yield Stream.TEXT, Markup(text), pos
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
135
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
136
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
137 class HTMLSanitizer(object):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
138 """A filter that removes potentially dangerous HTML tags and attributes
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
139 from the stream.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
140 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
141
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
142 _SAFE_TAGS = frozenset(['a', 'abbr', 'acronym', 'address', 'area', 'b',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
143 'big', 'blockquote', 'br', 'button', 'caption', 'center', 'cite',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
144 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
145 'em', 'fieldset', 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
146 'hr', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'map',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
147 'menu', 'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
148 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
149 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
150 'ul', 'var'])
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
151
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
152 _SAFE_ATTRS = frozenset(['abbr', 'accept', 'accept-charset', 'accesskey',
15
b3edbde541c4 Port HTML sanitizer fix from trac:changeset:3417.
cmlenz
parents: 14
diff changeset
153 'action', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellpadding',
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
154 'cellspacing', 'char', 'charoff', 'charset', 'checked', 'cite', 'class',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
155 'clear', 'cols', 'colspan', 'color', 'compact', 'coords', 'datetime',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
156 'dir', 'disabled', 'enctype', 'for', 'frame', 'headers', 'height',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
157 'href', 'hreflang', 'hspace', 'id', 'ismap', 'label', 'lang',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
158 'longdesc', 'maxlength', 'media', 'method', 'multiple', 'name',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
159 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly', 'rel', 'rev',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
160 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
161 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'target',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
162 'title', 'type', 'usemap', 'valign', 'value', 'vspace', 'width'])
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
163 _URI_ATTRS = frozenset(['action', 'background', 'dynsrc', 'href', 'lowsrc',
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
164 'src'])
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
165 _SAFE_SCHEMES = frozenset(['file', 'ftp', 'http', 'https', 'mailto', None])
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
166
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
167 def __call__(self, stream, ctxt=None):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
168 waiting_for = None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
169
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
170 for kind, data, pos in stream:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
171 if kind is Stream.START:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
172 if waiting_for:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
173 continue
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
174 tag, attrib = data
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
175 if tag not in self._SAFE_TAGS:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
176 waiting_for = tag
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
177 continue
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
178
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
179 new_attrib = []
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
180 for attr, value in attrib:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
181 if attr not in self._SAFE_ATTRS:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
182 continue
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
183 elif attr in self._URI_ATTRS:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
184 # Don't allow URI schemes such as "javascript:"
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
185 if self._get_scheme(value) not in self._SAFE_SCHEMES:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
186 continue
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
187 elif attr == 'style':
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
188 # Remove dangerous CSS declarations from inline styles
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
189 decls = []
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
190 for decl in filter(None, value.split(';')):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
191 is_evil = False
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
192 if 'expression' in decl:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
193 is_evil = True
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
194 for m in re.finditer(r'url\s*\(([^)]+)', decl):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
195 if self._get_scheme(m.group(1)) not in self._SAFE_SCHEMES:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
196 is_evil = True
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
197 break
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
198 if not is_evil:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
199 decls.append(decl.strip())
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
200 if not decls:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
201 continue
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
202 value = '; '.join(decls)
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
203 new_attrib.append((attr, value))
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
204
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
205 yield kind, (tag, new_attrib), pos
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
206
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
207 elif kind is Stream.END:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
208 tag = data
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
209 if waiting_for:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
210 if waiting_for == tag:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
211 waiting_for = None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
212 else:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
213 yield kind, data, pos
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
214
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
215 else:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
216 if not waiting_for:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
217 yield kind, data, pos
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
218
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
219 def _get_scheme(self, text):
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
220 if ':' not in text:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
221 return None
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
222 chars = [char for char in text.split(':', 1)[0] if char.isalnum()]
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
223 return ''.join(chars).lower()
Copyright (C) 2012-2017 Edgewall Software