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