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