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