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