Package tdi :: Package markup :: Module _analyzer
[frames] | no frames]

Source Code for Module tdi.markup._analyzer

  1  # -*- coding: ascii -*- 
  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   
39 -class AttributeAnalyzer(object):
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 #: Regex matcher for valid tdi attributes 59 #: 60 #: :Type: ``callable`` 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 #: Regex matcher for valid tdi:overlay attributes 72 #: 73 #: :Type: ``callable`` 74 _OVMATCH = _re.compile(ur''' 75 (?P<flags>(?:[-+][<>]?|[<>][+-]?)?) 76 (?P<name>[A-Za-z][A-Za-z\d_]*)$ 77 ''', _re.X).match 78 79 #: Regex matcher for valid tdi:scope attributes 80 #: 81 #: :Type: ``callable`` 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 #: Default tdi attribute name 88 #: 89 #: :Type: ``str`` 90 _DEFAULT_ATTRIBUTE = 'tdi' 91 92 #: Default overlay attribute name 93 #: 94 #: :Type: ``str`` 95 _DEFAULT_OVERLAY = 'tdi:overlay' 96 97 #: Default scope attribute name 98 #: 99 #: :Type: ``str`` 100 _DEFAULT_SCOPE = 'tdi:scope' 101 102 #: Default value for removing the tdi attribute 103 #: 104 #: :Type: ``bool`` 105 _DEFAULT_REMOVEATTR = True 106
107 - def __init__(self, decoder, attribute=None, overlay=None, scope=None, 108 removeattribute=None, hidden=None):
109 """ 110 Initialization 111 112 :Parameters: 113 `attribute` : ``str`` 114 The special tdi attribute name 115 116 `overlay` : ``str`` 117 The overlay attribute name 118 119 `scope` : ``str`` 120 The scope attribute name 121 122 `removeattribute` : ``bool`` 123 Should `attribute` be removed from the starttag? 124 125 `hidden` : ``bool`` 126 The default +- flag value. True: Tags are hidden, False: 127 Tags are kept. If omitted or ``None``, it's false. 128 """ 129 if attribute is None: 130 attribute = self._DEFAULT_ATTRIBUTE 131 self.attribute = decoder.normalize(attribute) 132 if overlay is None: 133 overlay = self._DEFAULT_OVERLAY 134 self._overlay = decoder.normalize(overlay) 135 if scope is None: 136 scope = self._DEFAULT_SCOPE 137 self.scope = decoder.normalize(scope) 138 if removeattribute is None: 139 removeattribute = self._DEFAULT_REMOVEATTR 140 self._removeattr = bool(removeattribute) 141 self._hidden = bool(hidden) 142 self._decoder = decoder 143 self._decode_attr = decoder.attribute 144 self._normalize = decoder.normalize
145
146 - def _parse_attr(self, name, value, matcher):
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
209 - def __call__(self, attr, name=''):
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 # Scope 242 if scope in special: 243 result['scope'] = self._parse_attr( 244 scope, special[scope], self._SCMATCH, 245 ) 246 247 # Overlay 248 if overlay in special: 249 result['overlay'] = self._parse_attr( 250 overlay, special[overlay], self._OVMATCH, 251 ) 252 253 # TDI 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