Package cssutils :: Module util
[hide private]
[frames] | no frames]

Source Code for Module cssutils.util

  1  """base classes for css and stylesheets packages 
  2  """ 
  3  __all__ = [] 
  4  __docformat__ = 'restructuredtext' 
  5  __author__ = '$LastChangedBy: cthedot $' 
  6  __date__ = '$LastChangedDate: 2007-11-06 22:23:55 +0100 (Di, 06 Nov 2007) $' 
  7  __version__ = '$LastChangedRevision: 653 $' 
  8   
  9  import re 
 10  import types 
 11  import xml.dom 
 12  import cssutils 
 13  from tokenize2 import Tokenizer 
 14   
15 -class Seq(object):
16 """ 17 (EXPERIMENTAL) 18 a list like sequence of (value, type) used in almost all cssutils classes 19 20 behaves almost like a list but keeps extra attribute "type" for 21 each value in the list 22 23 types are tokens types like e.g. "COMMENT" (value='/*...*/', all uppercase) 24 or productions like e.g. "universal" (value='*', all lowercase) 25 """
26 - def __init__(self):
27 self.values = [] 28 self.types = []
29
30 - def __contains__(self, item):
31 return item in self.values
32
33 - def __getitem__(self, index):
34 return self.values[index]
35
36 - def __setitem__(self, index, value_type):
37 "might be set with tuple (value, type) or a single value" 38 if type(value_type) == tuple: 39 val = value_type[0] 40 typ = value_type[1] 41 else: 42 val = value_type 43 typ = None 44 self.values[index] = val 45 self.types[index] = typ
46
47 - def __iter__(self):
48 "returns an iterator of values only " 49 return iter(self.values)
50
51 - def __len__(self):
52 "same as len(list)" 53 return len(self.values)
54
55 - def __repr__(self):
56 "returns a repr same as a list of tuples of (value, type)" 57 return u'[%s]' % u',\n '.join([u'(%r, %r)' % (value, self.types[i]) 58 for i, value in enumerate(self.values)])
59 - def __str__(self):
60 "returns a concanated string of all values" 61 items = [] 62 for i, value in enumerate(self.values): 63 if self.types[i]: 64 if self.types[i] != 'COMMENT': 65 items.append(value) 66 items.append(value) 67 return u''.join(items)
68
69 - def append(self, value, type=None):
70 """ 71 same as list.append but not a simple value but a SeqItem is appended 72 """ 73 self.values.append(value) # str(value)??? does not work if value is e.g. comment 74 self.types.append(type)
75 76
77 -class Base(object):
78 """ 79 Base class for most CSS and StyleSheets classes 80 81 Contains helper methods for inheriting classes helping parsing 82 83 ``_normalize`` is static as used be Preferences. 84 """ 85 __tokenizer2 = Tokenizer() 86 _log = cssutils.log 87 _prods = cssutils.tokenize2.CSSProductions 88 89 # for more on shorthand properties see 90 # http://www.dustindiaz.com/css-shorthand/ 91 # format: shorthand: [(propname, mandatorycheck?)*] 92 _SHORTHANDPROPERTIES = { 93 u'background': [], 94 u'border': [], 95 u'border-left': [], 96 u'border-right': [], 97 u'border-top': [], 98 u'border-bottom': [], 99 u'border-color': [], 100 u'border-style': [], 101 u'border-width': [], 102 u'cue': [], 103 u'font': [('font-weight', True), 104 ('font-size', True), 105 ('line-height', False), 106 ('font-family', True)], 107 u'list-style': [], 108 u'margin': [], 109 u'outline': [], 110 u'padding': [], 111 u'pause': [] 112 } 113 114 # simple escapes, all non unicodes 115 __escapes = re.compile(ur'(\\[^0-9a-fA-F])').sub 116 # all unicode (see cssproductions "unicode") 117 __unicodes = re.compile(ur'\\[0-9a-fA-F]{1,6}[\t|\r|\n|\f|\x20]?').sub 118 119 @staticmethod
120 - def _normalize(x):
121 """ 122 normalizes x, namely: 123 124 - remove any \ before non unicode sequences (0-9a-zA-Z) so for 125 x=="c\olor\" return "color" (unicode escape sequences should have 126 been resolved by the tokenizer already) 127 - lowercase 128 """ 129 if x: 130 def removeescape(matchobj): 131 return matchobj.group(0)[1:]
132 x = Base.__escapes(removeescape, x) 133 return x.lower() 134 else: 135 return x
136
137 - def _checkReadonly(self):
138 "raises xml.dom.NoModificationAllowedErr if rule/... is readonly" 139 if hasattr(self, '_readonly') and self._readonly: 140 raise xml.dom.NoModificationAllowedErr( 141 u'%s is readonly.' % self.__class__) 142 return True 143 return False
144
145 - def _newseq(self):
146 # used by Selector but should be used by most classes? 147 return Seq()
148
149 - def _valuestr(self, t):
150 """ 151 returns string value of t (t may be a string, a list of token tuples 152 or a single tuple in format (type, value, line, col) or a 153 tokenlist[old]) 154 """ 155 if not t: 156 return u'' 157 elif isinstance(t, basestring): 158 return t 159 elif isinstance(t, list) and isinstance(t[0], tuple): 160 return u''.join([x[1] for x in t]) 161 elif isinstance(t, tuple): # needed? 162 return self._tokenvalue(t) 163 else: # old 164 return u''.join([x.value for x in t])
165
166 - def _tokenize2(self, textortokens, aslist=False, fullsheet=False):
167 """ 168 returns tokens of textortokens which may already be tokens in which 169 case simply returns input 170 """ 171 if not textortokens: 172 return None 173 if types.GeneratorType == type(textortokens) and not aslist: 174 # already tokenized 175 return textortokens 176 if isinstance(textortokens, basestring): 177 if aslist: 178 return [t for t in self.__tokenizer2.tokenize( 179 textortokens, fullsheet=fullsheet)] 180 else: 181 return self.__tokenizer2.tokenize( 182 textortokens, fullsheet=fullsheet) 183 elif isinstance(textortokens, tuple): 184 # a single token (like a comment) 185 return [textortokens] 186 else: 187 # already tokenized but return generator 188 return (x for x in textortokens)
189
190 - def _nexttoken(self, tokenizer, default=None):
191 "returns next token in generator tokenizer or the default value" 192 try: 193 return tokenizer.next() 194 except (StopIteration, AttributeError): 195 return default
196
197 - def _type(self, token):
198 "type of Tokenizer token" 199 if not token: 200 return None 201 else: 202 return token[0]
203
204 - def _tokenvalue(self, token, normalize=False):
205 "value of Tokenizer token" 206 if not token: 207 return None 208 elif normalize: 209 return Base._normalize(token[1]) 210 else: 211 return token[1]
212
213 - def _tokensupto2(self, 214 tokenizer, 215 starttoken=None, 216 blockstartonly=False, 217 blockendonly=False, 218 mediaendonly=False, 219 semicolon=False, 220 propertynameendonly=False, 221 propertyvalueendonly=False, 222 propertypriorityendonly=False, 223 selectorattendonly=False, 224 funcendonly=False, 225 listseponly=False, # , 226 keepEnd=True, 227 keepEOF=True):
228 """ 229 returns tokens upto end of atrule and end index 230 end is defined by parameters, might be ; } ) or other 231 232 default looks for ending "}" and ";" 233 """ 234 ends = u';}' 235 brace = bracket = parant = 0 # {}, [], () 236 237 if blockstartonly: # { 238 ends = u'{' 239 brace = -1 # set to 0 with first { 240 elif blockendonly: # } 241 ends = u'}' 242 elif mediaendonly: # } 243 ends = u'}' 244 brace = 1 # rules } and mediarules } 245 elif semicolon: 246 ends = u';' 247 elif propertynameendonly: # : and ; in case of an error 248 ends = u':;' 249 elif propertyvalueendonly: # ; or !important 250 ends = (u';', u'!') 251 elif propertypriorityendonly: # ; 252 ends = u';' 253 elif selectorattendonly: # ] 254 ends = u']' 255 if starttoken and self._tokenvalue(starttoken) == u'[': 256 bracket = 1 257 elif funcendonly: # ) 258 ends = u')' 259 parant = 1 260 elif listseponly: # , 261 ends = u',' 262 263 resulttokens = [] 264 265 # NEEDED? 266 if starttoken: 267 resulttokens.append(starttoken) 268 269 if not tokenizer: 270 return resulttokens 271 else: 272 for token in tokenizer: 273 if self._type(token) == 'EOF': 274 if keepEOF and keepEnd: 275 resulttokens.append(token) 276 break 277 val = self._tokenvalue(token) 278 if u'{' == val: brace += 1 279 elif u'}' == val: brace -= 1 280 elif u'[' == val: bracket += 1 281 elif u']' == val: bracket -= 1 282 # function( or single ( 283 elif u'(' == val or \ 284 Base._prods.FUNCTION == self._type(token): parant += 1 285 elif u')' == val: parant -= 1 286 if val in ends and (brace == bracket == parant == 0): 287 if keepEnd: 288 resulttokens.append(token) 289 break 290 else: 291 resulttokens.append(token) 292 293 return resulttokens
294
295 - def _getProductions(self, productions):
296 """ 297 each production should return the next expected token 298 normaly a name like "uri" or "EOF" 299 some have no expectation like S or COMMENT, so simply return 300 the current value of self.__expected 301 """ 302 def ATKEYWORD(expected, seq, token, tokenizer=None): 303 "TODO: add default impl for unexpected @rule" 304 return expected
305 306 def COMMENT(expected, seq, token, tokenizer=None): 307 "default implementation for COMMENT token" 308 seq.append(cssutils.css.CSSComment([token])) 309 return expected 310 311 def S(expected, seq, token, tokenizer=None): 312 "default implementation for S token" 313 return expected 314 315 def EOF(expected=None, seq=None, token=None, tokenizer=None): 316 "default implementation for EOF token" 317 return 'EOF' 318 319 p = {'COMMENT': COMMENT, 320 'S': S, 321 'ATKEYWORD': ATKEYWORD, 322 'EOF': EOF # only available if fullsheet 323 } 324 p.update(productions) 325 return p 326
327 - def _parse(self, expected, seq, tokenizer, productions, default=None):
328 """ 329 puts parsed tokens in seq by calling a production with 330 (seq, tokenizer, token) 331 332 expected 333 a name what token or value is expected next, e.g. 'uri' 334 seq 335 to add rules etc to 336 tokenizer 337 call tokenizer.next() to get next token 338 productions 339 callbacks {tokentype: callback} 340 default 341 default callback if tokentype not in productions 342 343 returns (wellformed, expected) which the last prod might have set 344 """ 345 wellformed = True 346 347 if not tokenizer: 348 return wellformed, expected 349 350 prods = self._getProductions(productions) 351 for token in tokenizer: 352 typ, val, lin, col = token 353 p = prods.get(typ, default) 354 if p: 355 expected = p(expected, seq, token, tokenizer) 356 else: 357 wellformed = False 358 self._log.error(u'Unexpected token (%s, %s, %s, %s)' % token) 359 360 return wellformed, expected
361 362
363 -class Deprecated(object):
364 """This is a decorator which can be used to mark functions 365 as deprecated. It will result in a warning being emitted 366 when the function is used. 367 368 It accepts a single paramter ``msg`` which is shown with the warning. 369 It should contain information which function or method to use instead. 370 """
371 - def __init__(self, msg):
372 self.msg = msg
373
374 - def __call__(self, func):
375 def newFunc(*args, **kwargs): 376 import warnings 377 warnings.warn("Call to deprecated method %r. %s" % 378 (func.__name__, self.msg), 379 category=DeprecationWarning, 380 stacklevel=2) 381 return func(*args, **kwargs)
382 newFunc.__name__ = func.__name__ 383 newFunc.__doc__ = func.__doc__ 384 newFunc.__dict__.update(func.__dict__) 385 return newFunc
386