Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tinycss2/parser.py: 11%
130 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1from .ast import AtRule, Declaration, ParseError, QualifiedRule
2from .tokenizer import parse_component_value_list
5def _to_token_iterator(input, skip_comments=False):
6 """Iterate component values out of string or component values iterable.
8 :type input: :obj:`str` or :term:`iterable`
9 :param input: A string or an iterable of :term:`component values`.
10 :type skip_comments: :obj:`bool`
11 :param skip_comments: If the input is a string, ignore all CSS comments.
12 :returns: An iterator yielding :term:`component values`.
14 """
15 # Accept ASCII-only byte strings on Python 2, with implicit conversion.
16 if isinstance(input, str):
17 input = parse_component_value_list(input, skip_comments)
18 return iter(input)
21def _next_significant(tokens):
22 """Return the next significant (neither whitespace or comment) token.
24 :type tokens: :term:`iterator`
25 :param tokens: An iterator yielding :term:`component values`.
26 :returns: A :term:`component value`, or :obj:`None`.
28 """
29 for token in tokens:
30 if token.type not in ('whitespace', 'comment'):
31 return token
34def parse_one_component_value(input, skip_comments=False):
35 """Parse a single :diagram:`component value`.
37 This is used e.g. for an attribute value
38 referred to by ``attr(foo length)``.
40 :type input: :obj:`str` or :term:`iterable`
41 :param input: A string or an iterable of :term:`component values`.
42 :type skip_comments: :obj:`bool`
43 :param skip_comments: If the input is a string, ignore all CSS comments.
44 :returns:
45 A :term:`component value` (that is neither whitespace or comment),
46 or a :class:`~tinycss2.ast.ParseError`.
48 """
49 tokens = _to_token_iterator(input, skip_comments)
50 first = _next_significant(tokens)
51 second = _next_significant(tokens)
52 if first is None:
53 return ParseError(1, 1, 'empty', 'Input is empty')
54 if second is not None:
55 return ParseError(
56 second.source_line, second.source_column, 'extra-input',
57 'Got more than one token')
58 else:
59 return first
62def parse_one_declaration(input, skip_comments=False):
63 """Parse a single :diagram:`declaration`.
65 This is used e.g. for a declaration in an `@supports
66 <https://drafts.csswg.org/css-conditional/#at-supports>`_ test.
68 :type input: :obj:`str` or :term:`iterable`
69 :param input: A string or an iterable of :term:`component values`.
70 :type skip_comments: :obj:`bool`
71 :param skip_comments: If the input is a string, ignore all CSS comments.
72 :returns:
73 A :class:`~tinycss2.ast.Declaration`
74 or :class:`~tinycss2.ast.ParseError`.
76 Any whitespace or comment before the ``:`` colon is dropped.
78 """
79 tokens = _to_token_iterator(input, skip_comments)
80 first_token = _next_significant(tokens)
81 if first_token is None:
82 return ParseError(1, 1, 'empty', 'Input is empty')
83 return _parse_declaration(first_token, tokens)
86def _parse_declaration(first_token, tokens):
87 """Parse a declaration.
89 Consume :obj:`tokens` until the end of the declaration or the first error.
91 :type first_token: :term:`component value`
92 :param first_token: The first component value of the rule.
93 :type tokens: :term:`iterator`
94 :param tokens: An iterator yielding :term:`component values`.
95 :returns:
96 A :class:`~tinycss2.ast.Declaration`
97 or :class:`~tinycss2.ast.ParseError`.
99 """
100 name = first_token
101 if name.type != 'ident':
102 return ParseError(name.source_line, name.source_column, 'invalid',
103 'Expected <ident> for declaration name, got %s.'
104 % name.type)
106 colon = _next_significant(tokens)
107 if colon is None:
108 return ParseError(name.source_line, name.source_column, 'invalid',
109 "Expected ':' after declaration name, got EOF")
110 elif colon != ':':
111 return ParseError(colon.source_line, colon.source_column, 'invalid',
112 "Expected ':' after declaration name, got %s."
113 % colon.type)
115 value = []
116 state = 'value'
117 for i, token in enumerate(tokens):
118 if state == 'value' and token == '!':
119 state = 'bang'
120 bang_position = i
121 elif state == 'bang' and token.type == 'ident' \
122 and token.lower_value == 'important':
123 state = 'important'
124 elif token.type not in ('whitespace', 'comment'):
125 state = 'value'
126 value.append(token)
128 if state == 'important':
129 del value[bang_position:]
131 return Declaration(name.source_line, name.source_column, name.value,
132 name.lower_value, value, state == 'important')
135def _consume_declaration_in_list(first_token, tokens):
136 """Like :func:`_parse_declaration`, but stop at the first ``;``."""
137 other_declaration_tokens = []
138 for token in tokens:
139 if token == ';':
140 break
141 other_declaration_tokens.append(token)
142 return _parse_declaration(first_token, iter(other_declaration_tokens))
145def parse_declaration_list(input, skip_comments=False, skip_whitespace=False):
146 """Parse a :diagram:`declaration list` (which may also contain at-rules).
148 This is used e.g. for the :attr:`~tinycss2.ast.QualifiedRule.content`
149 of a style rule or ``@page`` rule,
150 or for the ``style`` attribute of an HTML element.
152 In contexts that don’t expect any at-rule,
153 all :class:`~tinycss2.ast.AtRule` objects
154 should simply be rejected as invalid.
156 :type input: :obj:`str` or :term:`iterable`
157 :param input: A string or an iterable of :term:`component values`.
158 :type skip_comments: :obj:`bool`
159 :param skip_comments:
160 Ignore CSS comments at the top-level of the list.
161 If the input is a string, ignore all comments.
162 :type skip_whitespace: :obj:`bool`
163 :param skip_whitespace:
164 Ignore whitespace at the top-level of the list.
165 Whitespace is still preserved
166 in the :attr:`~tinycss2.ast.Declaration.value` of declarations
167 and the :attr:`~tinycss2.ast.AtRule.prelude`
168 and :attr:`~tinycss2.ast.AtRule.content` of at-rules.
169 :returns:
170 A list of
171 :class:`~tinycss2.ast.Declaration`,
172 :class:`~tinycss2.ast.AtRule`,
173 :class:`~tinycss2.ast.Comment` (if ``skip_comments`` is false),
174 :class:`~tinycss2.ast.WhitespaceToken`
175 (if ``skip_whitespace`` is false),
176 and :class:`~tinycss2.ast.ParseError` objects
178 """
179 tokens = _to_token_iterator(input, skip_comments)
180 result = []
181 for token in tokens:
182 if token.type == 'whitespace':
183 if not skip_whitespace:
184 result.append(token)
185 elif token.type == 'comment':
186 if not skip_comments:
187 result.append(token)
188 elif token.type == 'at-keyword':
189 result.append(_consume_at_rule(token, tokens))
190 elif token != ';':
191 result.append(_consume_declaration_in_list(token, tokens))
192 return result
195def parse_one_rule(input, skip_comments=False):
196 """Parse a single :diagram:`qualified rule` or :diagram:`at-rule`.
198 This would be used e.g. by `insertRule()
199 <https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule>`_
200 in an implementation of CSSOM.
202 :type input: :obj:`str` or :term:`iterable`
203 :param input: A string or an iterable of :term:`component values`.
204 :type skip_comments: :obj:`bool`
205 :param skip_comments:
206 If the input is a string, ignore all CSS comments.
207 :returns:
208 A :class:`~tinycss2.ast.QualifiedRule`,
209 :class:`~tinycss2.ast.AtRule`,
210 or :class:`~tinycss2.ast.ParseError` objects.
212 Any whitespace or comment before or after the rule is dropped.
214 """
215 tokens = _to_token_iterator(input, skip_comments)
216 first = _next_significant(tokens)
217 if first is None:
218 return ParseError(1, 1, 'empty', 'Input is empty')
220 rule = _consume_rule(first, tokens)
221 next = _next_significant(tokens)
222 if next is not None:
223 return ParseError(
224 next.source_line, next.source_column, 'extra-input',
225 'Expected a single rule, got %s after the first rule.' % next.type)
226 return rule
229def parse_rule_list(input, skip_comments=False, skip_whitespace=False):
230 """Parse a non-top-level :diagram:`rule list`.
232 This is used for parsing the :attr:`~tinycss2.ast.AtRule.content`
233 of nested rules like ``@media``.
234 This differs from :func:`parse_stylesheet` in that
235 top-level ``<!--`` and ``-->`` tokens are not ignored.
237 :type input: :obj:`str` or :term:`iterable`
238 :param input: A string or an iterable of :term:`component values`.
239 :type skip_comments: :obj:`bool`
240 :param skip_comments:
241 Ignore CSS comments at the top-level of the list.
242 If the input is a string, ignore all comments.
243 :type skip_whitespace: :obj:`bool`
244 :param skip_whitespace:
245 Ignore whitespace at the top-level of the list.
246 Whitespace is still preserved
247 in the :attr:`~tinycss2.ast.QualifiedRule.prelude`
248 and the :attr:`~tinycss2.ast.QualifiedRule.content` of rules.
249 :returns:
250 A list of
251 :class:`~tinycss2.ast.QualifiedRule`,
252 :class:`~tinycss2.ast.AtRule`,
253 :class:`~tinycss2.ast.Comment` (if ``skip_comments`` is false),
254 :class:`~tinycss2.ast.WhitespaceToken`
255 (if ``skip_whitespace`` is false),
256 and :class:`~tinycss2.ast.ParseError` objects.
258 """
259 tokens = _to_token_iterator(input, skip_comments)
260 result = []
261 for token in tokens:
262 if token.type == 'whitespace':
263 if not skip_whitespace:
264 result.append(token)
265 elif token.type == 'comment':
266 if not skip_comments:
267 result.append(token)
268 else:
269 result.append(_consume_rule(token, tokens))
270 return result
273def parse_stylesheet(input, skip_comments=False, skip_whitespace=False):
274 """Parse :diagram:`stylesheet` from text.
276 This is used e.g. for a ``<style>`` HTML element.
278 This differs from :func:`parse_rule_list` in that
279 top-level ``<!--`` and ``-->`` tokens are ignored.
280 This is a legacy quirk for the ``<style>`` HTML element.
282 :type input: :obj:`str` or :term:`iterable`
283 :param input: A string or an iterable of :term:`component values`.
284 :type skip_comments: :obj:`bool`
285 :param skip_comments:
286 Ignore CSS comments at the top-level of the stylesheet.
287 If the input is a string, ignore all comments.
288 :type skip_whitespace: :obj:`bool`
289 :param skip_whitespace:
290 Ignore whitespace at the top-level of the stylesheet.
291 Whitespace is still preserved
292 in the :attr:`~tinycss2.ast.QualifiedRule.prelude`
293 and the :attr:`~tinycss2.ast.QualifiedRule.content` of rules.
294 :returns:
295 A list of
296 :class:`~tinycss2.ast.QualifiedRule`,
297 :class:`~tinycss2.ast.AtRule`,
298 :class:`~tinycss2.ast.Comment` (if ``skip_comments`` is false),
299 :class:`~tinycss2.ast.WhitespaceToken`
300 (if ``skip_whitespace`` is false),
301 and :class:`~tinycss2.ast.ParseError` objects.
303 """
304 tokens = _to_token_iterator(input, skip_comments)
305 result = []
306 for token in tokens:
307 if token.type == 'whitespace':
308 if not skip_whitespace:
309 result.append(token)
310 elif token.type == 'comment':
311 if not skip_comments:
312 result.append(token)
313 elif token not in ('<!--', '-->'):
314 result.append(_consume_rule(token, tokens))
315 return result
318def _consume_rule(first_token, tokens):
319 """Parse a qualified rule or at-rule.
321 Consume just enough of :obj:`tokens` for this rule.
323 :type first_token: :term:`component value`
324 :param first_token: The first component value of the rule.
325 :type tokens: :term:`iterator`
326 :param tokens: An iterator yielding :term:`component values`.
327 :returns:
328 A :class:`~tinycss2.ast.QualifiedRule`,
329 :class:`~tinycss2.ast.AtRule`,
330 or :class:`~tinycss2.ast.ParseError`.
332 """
333 if first_token.type == 'at-keyword':
334 return _consume_at_rule(first_token, tokens)
335 if first_token.type == '{} block':
336 prelude = []
337 block = first_token
338 else:
339 prelude = [first_token]
340 for token in tokens:
341 if token.type == '{} block':
342 block = token
343 break
344 prelude.append(token)
345 else:
346 return ParseError(
347 prelude[-1].source_line, prelude[-1].source_column, 'invalid',
348 'EOF reached before {} block for a qualified rule.')
349 return QualifiedRule(first_token.source_line, first_token.source_column,
350 prelude, block.content)
353def _consume_at_rule(at_keyword, tokens):
354 """Parse an at-rule.
356 Consume just enough of :obj:`tokens` for this rule.
358 :type at_keyword: :class:`AtKeywordToken`
359 :param at_keyword: The at-rule keyword token starting this rule.
360 :type tokens: :term:`iterator`
361 :param tokens: An iterator yielding :term:`component values`.
362 :returns:
363 A :class:`~tinycss2.ast.QualifiedRule`,
364 or :class:`~tinycss2.ast.ParseError`.
366 """
367 prelude = []
368 content = None
369 for token in tokens:
370 if token.type == '{} block':
371 content = token.content
372 break
373 elif token == ';':
374 break
375 prelude.append(token)
376 return AtRule(at_keyword.source_line, at_keyword.source_column,
377 at_keyword.value, at_keyword.lower_value, prelude, content)