1
2 r"""
3 :Copyright:
4
5 Copyright 2006 - 2015
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 if __doc__:
30
31 __doc__ = __doc__.encode('ascii').decode('unicode_escape')
32 __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
33 __docformat__ = "restructuredtext en"
34
35 import re as _re
36
37 from .._exceptions import TemplateAttributeError, TemplateAttributeEmptyError
38 from .. import interfaces as _interfaces
39
40
42 """
43 Attribute analyzer
44
45 :IVariables:
46 `attribute` : ``str``
47 The attribute name
48
49 `scope` : ``str``
50 The scope attribute name
51
52 `_overlay` : ``str``
53 The overlay attribute name
54
55 `_removeattr` : ``bool``
56 Should `attribute` be removed from the starttag?
57 """
58 __implements__ = [_interfaces.AttributeAnalyzerInterface]
59
60
61
62
63 _IDMATCH = _re.compile(ur'''
64 -$ |
65 (?P<flags>(?:
66 :|[+-]|\*|
67 :[+-]|:\*|[+-]:|[+-]\*|\*:|\*[+-]|
68 :[+-]\*|:\*[+-]|[+-]:\*|[+-]\*:|\*:[+-]|\*[+-]:
69 )?)
70 (?P<name>[A-Za-z][A-Za-z\d_]*)$
71 ''', _re.X).match
72
73
74
75
76 _OVMATCH = _re.compile(ur'''
77 (?P<flags>(?:[-+][<>]?|[<>][+-]?)?)
78 (?P<name>[A-Za-z][A-Za-z\d_]*)$
79 ''', _re.X).match
80
81
82
83
84 _SCMATCH = _re.compile(ur'''
85 (?P<flags>(?:[+-]=?|=[+-]?)?)
86 (?P<name>(?:[A-Za-z][A-Za-z\d_]*(?:\.[A-Za-z][A-Za-z\d_]*)*)?)$
87 ''', _re.X).match
88
89
90
91
92 _DEFAULT_ATTRIBUTE = 'tdi'
93
94
95
96
97 _DEFAULT_OVERLAY = 'tdi:overlay'
98
99
100
101
102 _DEFAULT_SCOPE = 'tdi:scope'
103
104
105
106
107 _DEFAULT_REMOVEATTR = True
108
109 - def __init__(self, decoder, attribute=None, overlay=None, scope=None,
110 removeattribute=None, hidden=None):
147
149 """
150 Parse attribute value
151
152 :Parameters:
153 `name` : ``str``
154 Name of the attribute (used for error messages)
155
156 `value` : ``str``
157 Raw attribute value (maybe ``None``, but it raises an error,
158 because there's some information expected here!)
159
160 `matcher` : ``callable``
161 Matcher, expected to return a match object or ``None``.
162
163 :Return: flags and name
164 :Rtype: ``tuple`` (``(str, str)``)
165 """
166 if value is None:
167 raise TemplateAttributeError(
168 "Invalid short %s attribute" % (name,)
169 )
170 value = self._decode_attr(value).strip()
171 if not value:
172 raise TemplateAttributeEmptyError("Empty %s attribute" % (name,))
173 return self._parse(name, value, matcher)
174
175 - def _parse(self, name, value, matcher):
176 """
177 Parse value
178
179 :Parameters:
180 `name` : ``str``
181 Name of the attribute (used for error messages)
182
183 `value` : ``str``
184 Raw attribute value (maybe ``None``, but it raises an error,
185 because there's some information expected here!)
186
187 `matcher` : ``callable``
188 Matcher, expected to return a match object or ``None``.
189
190 :Return: flags and name
191 :Rtype: ``tuple`` (``(str, str)``)
192 """
193 match = matcher(value)
194 if match is None:
195 raise TemplateAttributeError(
196 "Invalid %s attribute %r" % (name, value)
197 )
198
199 def uni2str(value):
200 """ Simple None-aware encoder """
201 if value is None:
202 return None
203 return value.encode(self._decoder.encoding)
204 flags, name = map(uni2str, match.group('flags', 'name'))
205 if name is not None:
206 if '+' in flags:
207 flags = flags.replace('+', '')
208 elif self._hidden and '-' not in flags:
209 flags += '-'
210 return flags, name
211
213 """
214 Analyze attributes
215
216 :Parameters:
217 `attr` : sequence
218 (key, value) list of attributes. value may be ``None``
219
220 `name` : ``str``
221 Name of the tag. If set and containing a value, it's additionally
222 considered being equal to a TDI attribute.
223
224 :Return: Either ``None`` if there's nothing special or a tuple of:
225 tdi name, tdi flags, (possibly) reduced attributes, overlay
226 info, scope info
227 :Rtype: ``tuple``
228 """
229 normalize, reduced, special = self._normalize, [], {}
230 attribute, overlay, scope = wanted = (
231 self.attribute, self._overlay, self.scope
232 )
233 remove = self._removeattr
234
235 for key, value in attr:
236 nkey = normalize(key)
237 if nkey in wanted:
238 special[nkey] = value
239 if remove:
240 continue
241 reduced.append((key, value))
242
243 result = {}
244
245 if scope in special:
246 result['scope'] = self._parse_attr(
247 scope, special[scope], self._SCMATCH,
248 )
249
250
251 if overlay in special:
252 result['overlay'] = self._parse_attr(
253 overlay, special[overlay], self._OVMATCH,
254 )
255
256
257 if name:
258 nflags, ntdi = self._parse(
259 attribute, self._decoder.decode(name), self._IDMATCH
260 )
261 if not ntdi:
262 nflags, ntdi = '-', None
263 if attribute in special:
264 flags, tdi = self._parse_attr(
265 attribute, special[attribute], self._IDMATCH,
266 )
267 if not tdi:
268 flags, tdi = '-', None
269 if name and (nflags != flags or ntdi != tdi):
270 raise TemplateAttributeError(
271 "%s attribute value %r must equal name" % (
272 attribute, name
273 )
274 )
275 result['attribute'] = flags, tdi
276 elif name:
277 result['attribute'] = nflags, ntdi
278
279 return reduced, result
280
281
282 from .. import c
283 c = c.load('impl')
284 if c is not None:
285 DEFAULT_ANALYZER = c.AttributeAnalyzer
286 else:
287 DEFAULT_ANALYZER = AttributeAnalyzer
288 del c
289