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

Source Code for Module tdi.markup._analyzer

  1  # -*- coding: ascii -*- 
  2  # 
  3  # Copyright 2006 - 2013 
  4  # Andr\xe9 Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 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   
35 -class AttributeAnalyzer(object):
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 #: Regex matcher for valid tdi attributes 55 #: 56 #: :Type: ``callable`` 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 #: Regex matcher for valid tdi:overlay attributes 68 #: 69 #: :Type: ``callable`` 70 _OVMATCH = _re.compile(ur''' 71 (?P<flags>(?:[-+][<>]?|[<>][+-]?)?) 72 (?P<name>[A-Za-z][A-Za-z\d_]*)$ 73 ''', _re.X).match 74 75 #: Regex matcher for valid tdi:scope attributes 76 #: 77 #: :Type: ``callable`` 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 #: Default tdi attribute name 84 #: 85 #: :Type: ``str`` 86 _DEFAULT_ATTRIBUTE = 'tdi' 87 88 #: Default overlay attribute name 89 #: 90 #: :Type: ``str`` 91 _DEFAULT_OVERLAY = 'tdi:overlay' 92 93 #: Default scope attribute name 94 #: 95 #: :Type: ``str`` 96 _DEFAULT_SCOPE = 'tdi:scope' 97 98 #: Default value for removing the tdi attribute 99 #: 100 #: :Type: ``bool`` 101 _DEFAULT_REMOVEATTR = True 102
103 - def __init__(self, decoder, attribute=None, overlay=None, scope=None, 104 removeattribute=None, hidden=None):
105 """ 106 Initialization 107 108 :Parameters: 109 `attribute` : ``str`` 110 The special tdi attribute name 111 112 `overlay` : ``str`` 113 The overlay attribute name 114 115 `scope` : ``str`` 116 The scope attribute name 117 118 `removeattribute` : ``bool`` 119 Should `attribute` be removed from the starttag? 120 121 `hidden` : ``bool`` 122 The default +- flag value. True: Tags are hidden, False: 123 Tags are kept. If omitted or ``None``, it's false. 124 """ 125 if attribute is None: 126 attribute = self._DEFAULT_ATTRIBUTE 127 self.attribute = decoder.normalize(attribute) 128 if overlay is None: 129 overlay = self._DEFAULT_OVERLAY 130 self._overlay = decoder.normalize(overlay) 131 if scope is None: 132 scope = self._DEFAULT_SCOPE 133 self.scope = decoder.normalize(scope) 134 if removeattribute is None: 135 removeattribute = self._DEFAULT_REMOVEATTR 136 self._removeattr = bool(removeattribute) 137 self._hidden = bool(hidden) 138 self._decoder = decoder 139 self._decode_attr = decoder.attribute 140 self._normalize = decoder.normalize
141
142 - def _parse_attr(self, name, value, matcher):
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
205 - def __call__(self, attr, name=''):
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 # Scope 238 if scope in special: 239 result['scope'] = self._parse_attr( 240 scope, special[scope], self._SCMATCH, 241 ) 242 243 # Overlay 244 if overlay in special: 245 result['overlay'] = self._parse_attr( 246 overlay, special[overlay], self._OVMATCH, 247 ) 248 249 # TDI 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