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

Source Code for Module tdi.markup._analyzer

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