1
2 u"""
3 :Copyright:
4
5 Copyright 2006 - 2013
6 Andr\xe9 Malo or his licensors, as applicable
7
8 :License:
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 ========================
23 Template Builder Logic
24 ========================
25
26 This module provides the logic to build a nodetree out of parser
27 events.
28 """
29 __author__ = u"Andr\xe9 Malo"
30 __docformat__ = "restructuredtext en"
31
32 import re as _re
33
34 from tdi._exceptions import TemplateAttributeError
35 from tdi._exceptions import TemplateAttributeEmptyError
36 from tdi import interfaces as _interfaces
37
38
40 """
41 Attribute analyzer
42
43 :IVariables:
44 `attribute` : ``str``
45 The attribute name
46
47 `scope` : ``str``
48 The scope attribute name
49
50 `_overlay` : ``str``
51 The overlay attribute name
52
53 `_removeattr` : ``bool``
54 Should `attribute` be removed from the starttag?
55 """
56 __implements__ = [_interfaces.AttributeAnalyzerInterface]
57
58
59
60
61 _IDMATCH = _re.compile(ur'''
62 -$ |
63 (?P<flags>(?:
64 :|[+-]|\*|
65 :[+-]|:\*|[+-]:|[+-]\*|\*:|\*[+-]|
66 :[+-]\*|:\*[+-]|[+-]:\*|[+-]\*:|\*:[+-]|\*[+-]:
67 )?)
68 (?P<name>[A-Za-z][A-Za-z\d_]*)$
69 ''', _re.X).match
70
71
72
73
74 _OVMATCH = _re.compile(ur'''
75 (?P<flags>(?:[-+][<>]?|[<>][+-]?)?)
76 (?P<name>[A-Za-z][A-Za-z\d_]*)$
77 ''', _re.X).match
78
79
80
81
82 _SCMATCH = _re.compile(ur'''
83 (?P<flags>(?:[+-]=?|=[+-]?)?)
84 (?P<name>(?:[A-Za-z][A-Za-z\d_]*(?:\.[A-Za-z][A-Za-z\d_]*)*)?)$
85 ''', _re.X).match
86
87
88
89
90 _DEFAULT_ATTRIBUTE = 'tdi'
91
92
93
94
95 _DEFAULT_OVERLAY = 'tdi:overlay'
96
97
98
99
100 _DEFAULT_SCOPE = 'tdi:scope'
101
102
103
104
105 _DEFAULT_REMOVEATTR = True
106
107 - def __init__(self, decoder, attribute=None, overlay=None, scope=None,
108 removeattribute=None, hidden=None):
145
147 """
148 Parse attribute value
149
150 :Parameters:
151 `name` : ``str``
152 Name of the attribute (used for error messages)
153
154 `value` : ``str``
155 Raw attribute value (maybe ``None``, but it raises an error,
156 because there's some information expected here!)
157
158 `matcher` : ``callable``
159 Matcher, expected to return a match object or ``None``.
160
161 :Return: flags and name
162 :Rtype: ``tuple`` (``(str, str)``)
163 """
164 if value is None:
165 raise TemplateAttributeError(
166 "Invalid short %s attribute" % (name,)
167 )
168 value = self._decode_attr(value).strip()
169 if not value:
170 raise TemplateAttributeEmptyError("Empty %s attribute" % (name,))
171 return self._parse(name, value, matcher)
172
173 - def _parse(self, name, value, matcher):
174 """
175 Parse value
176
177 :Parameters:
178 `name` : ``str``
179 Name of the attribute (used for error messages)
180
181 `value` : ``str``
182 Raw attribute value (maybe ``None``, but it raises an error,
183 because there's some information expected here!)
184
185 `matcher` : ``callable``
186 Matcher, expected to return a match object or ``None``.
187
188 :Return: flags and name
189 :Rtype: ``tuple`` (``(str, str)``)
190 """
191 match = matcher(value)
192 if match is None:
193 raise TemplateAttributeError(
194 "Invalid %s attribute %r" % (name, value)
195 )
196 def uni2str(value):
197 """ Simple None-aware encoder """
198 if value is None:
199 return None
200 return value.encode(self._decoder.encoding)
201 flags, name = map(uni2str, match.group('flags', 'name'))
202 if name is not None:
203 if '+' in flags:
204 flags = flags.replace('+', '')
205 elif self._hidden and '-' not in flags:
206 flags += '-'
207 return flags, name
208
210 """
211 Analyze attributes
212
213 :Parameters:
214 `attr` : sequence
215 (key, value) list of attributes. value may be ``None``
216
217 `name` : ``str``
218 Name of the tag. If set and containing a value, it's additionally
219 considered being equal to a TDI attribute.
220
221 :Return: Either ``None`` if there's nothing special or a tuple of:
222 tdi name, tdi flags, (possibly) reduced attributes, overlay
223 info, scope info
224 :Rtype: ``tuple``
225 """
226 normalize, reduced, special = self._normalize, [], {}
227 attribute, overlay, scope = wanted = (
228 self.attribute, self._overlay, self.scope
229 )
230 remove = self._removeattr
231
232 for key, value in attr:
233 nkey = normalize(key)
234 if nkey in wanted:
235 special[nkey] = value
236 if remove:
237 continue
238 reduced.append((key, value))
239
240 result = {}
241
242 if scope in special:
243 result['scope'] = self._parse_attr(
244 scope, special[scope], self._SCMATCH,
245 )
246
247
248 if overlay in special:
249 result['overlay'] = self._parse_attr(
250 overlay, special[overlay], self._OVMATCH,
251 )
252
253
254 if name:
255 nflags, ntdi = self._parse(
256 attribute, self._decoder.decode(name), self._IDMATCH
257 )
258 if not ntdi:
259 nflags, ntdi = '-', None
260 if attribute in special:
261 flags, tdi = self._parse_attr(
262 attribute, special[attribute], self._IDMATCH,
263 )
264 if not tdi:
265 flags, tdi = '-', None
266 if name and (nflags != flags or ntdi != tdi):
267 raise TemplateAttributeError(
268 "%s attribute value %r must equal name" % (
269 attribute, name
270 )
271 )
272 result['attribute'] = flags, tdi
273 elif name:
274 result['attribute'] = nflags, ntdi
275
276 return reduced, result
277
278
279 from tdi import c
280 c = c.load('impl')
281 if c is not None:
282 DEFAULT_ANALYZER = c.AttributeAnalyzer
283 else:
284 DEFAULT_ANALYZER = AttributeAnalyzer
285 del c
286