Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tinycss2/ast.py: 46%

281 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1""" 

2 

3Data structures for the CSS abstract syntax tree. 

4 

5""" 

6 

7 

8from webencodings import ascii_lower 

9 

10from .serializer import _serialize_to, serialize_identifier, serialize_name 

11 

12 

13class Node: 

14 """Every node type inherits from this class, 

15 which is never instantiated directly. 

16 

17 .. attribute:: type 

18 

19 Each child class has a :attr:`type` class attribute 

20 with a unique string value. 

21 This allows checking for the node type with code like: 

22 

23 .. code-block:: python 

24 

25 if node.type == 'whitespace': 

26 

27 instead of the more verbose: 

28 

29 .. code-block:: python 

30 

31 from tinycss2.ast import WhitespaceToken 

32 if isinstance(node, WhitespaceToken): 

33 

34 Every node also has these attributes and methods, 

35 which are not repeated for brevity: 

36 

37 .. attribute:: source_line 

38 

39 The line number of the start of the node in the CSS source. 

40 Starts at 1. 

41 

42 .. attribute:: source_column 

43 

44 The column number within :attr:`source_line` of the start of the node 

45 in the CSS source. 

46 Starts at 1. 

47 

48 .. automethod:: serialize 

49 

50 """ 

51 __slots__ = ['source_line', 'source_column'] 

52 

53 def __init__(self, source_line, source_column): 

54 self.source_line = source_line 

55 self.source_column = source_column 

56 

57 def __repr__(self): 

58 return self.repr_format.format(self=self) 

59 

60 def serialize(self): 

61 """Serialize this node to CSS syntax and return a Unicode string.""" 

62 chunks = [] 

63 self._serialize_to(chunks.append) 

64 return ''.join(chunks) 

65 

66 def _serialize_to(self, write): 

67 """Serialize this node to CSS syntax, writing chunks as Unicode string 

68 by calling the provided :obj:`write` callback. 

69 

70 """ 

71 raise NotImplementedError # pragma: no cover 

72 

73 

74class ParseError(Node): 

75 """A syntax error of some sort. May occur anywhere in the tree. 

76 

77 Syntax errors are not fatal in the parser 

78 to allow for different error handling behaviors. 

79 For example, an error in a Selector list makes the whole rule invalid, 

80 but an error in a Media Query list only replaces one comma-separated query 

81 with ``not all``. 

82 

83 .. autoattribute:: type 

84 

85 .. attribute:: kind 

86 

87 Machine-readable string indicating the type of error. 

88 Example: ``'bad-url'``. 

89 

90 .. attribute:: message 

91 

92 Human-readable explanation of the error, as a string. 

93 Could be translated, expanded to include details, etc. 

94 

95 """ 

96 __slots__ = ['kind', 'message'] 

97 type = 'error' 

98 repr_format = '<{self.__class__.__name__} {self.kind}>' 

99 

100 def __init__(self, line, column, kind, message): 

101 Node.__init__(self, line, column) 

102 self.kind = kind 

103 self.message = message 

104 

105 def _serialize_to(self, write): 

106 if self.kind == 'bad-string': 

107 write('"[bad string]\n') 

108 elif self.kind == 'bad-url': 

109 write('url([bad url])') 

110 elif self.kind in ')]}': 

111 write(self.kind) 

112 elif self.kind in ('eof-in-string', 'eof-in-url'): 

113 pass 

114 else: # pragma: no cover 

115 raise TypeError('Can not serialize %r' % self) 

116 

117 

118class Comment(Node): 

119 """A CSS comment. 

120 

121 Comments can be ignored by passing ``skip_comments=True`` 

122 to functions such as :func:`~tinycss2.parse_component_value_list`. 

123 

124 .. autoattribute:: type 

125 

126 .. attribute:: value 

127 

128 The content of the comment, between ``/*`` and ``*/``, as a string. 

129 

130 """ 

131 __slots__ = ['value'] 

132 type = 'comment' 

133 repr_format = '<{self.__class__.__name__} {self.value}>' 

134 

135 def __init__(self, line, column, value): 

136 Node.__init__(self, line, column) 

137 self.value = value 

138 

139 def _serialize_to(self, write): 

140 write('/*') 

141 write(self.value) 

142 write('*/') 

143 

144 

145class WhitespaceToken(Node): 

146 """A :diagram:`whitespace-token`. 

147 

148 .. autoattribute:: type 

149 

150 .. attribute:: value 

151 

152 The whitespace sequence, as a string, as in the original CSS source. 

153 

154 

155 """ 

156 __slots__ = ['value'] 

157 type = 'whitespace' 

158 repr_format = '<{self.__class__.__name__}>' 

159 

160 def __init__(self, line, column, value): 

161 Node.__init__(self, line, column) 

162 self.value = value 

163 

164 def _serialize_to(self, write): 

165 write(self.value) 

166 

167 

168class LiteralToken(Node): 

169 r"""Token that represents one or more characters as in the CSS source. 

170 

171 .. autoattribute:: type 

172 

173 .. attribute:: value 

174 

175 A string of one to four characters. 

176 

177 Instances compare equal to their :attr:`value`, 

178 so that these are equivalent: 

179 

180 .. code-block:: python 

181 

182 if node == ';': 

183 if node.type == 'literal' and node.value == ';': 

184 

185 This regroups what `the specification`_ defines as separate token types: 

186 

187 .. _the specification: https://drafts.csswg.org/css-syntax-3/ 

188 

189 * *<colon-token>* ``:`` 

190 * *<semicolon-token>* ``;`` 

191 * *<comma-token>* ``,`` 

192 * *<cdc-token>* ``-->`` 

193 * *<cdo-token>* ``<!--`` 

194 * *<include-match-token>* ``~=`` 

195 * *<dash-match-token>* ``|=`` 

196 * *<prefix-match-token>* ``^=`` 

197 * *<suffix-match-token>* ``$=`` 

198 * *<substring-match-token>* ``*=`` 

199 * *<column-token>* ``||`` 

200 * *<delim-token>* (a single ASCII character not part of any another token) 

201 

202 """ 

203 __slots__ = ['value'] 

204 type = 'literal' 

205 repr_format = '<{self.__class__.__name__} {self.value}>' 

206 

207 def __init__(self, line, column, value): 

208 Node.__init__(self, line, column) 

209 self.value = value 

210 

211 def __eq__(self, other): 

212 return self.value == other or self is other 

213 

214 def __ne__(self, other): 

215 return not self == other 

216 

217 def _serialize_to(self, write): 

218 write(self.value) 

219 

220 

221class IdentToken(Node): 

222 """An :diagram:`ident-token`. 

223 

224 .. autoattribute:: type 

225 

226 .. attribute:: value 

227 

228 The unescaped value, as a Unicode string. 

229 

230 .. attribute:: lower_value 

231 

232 Same as :attr:`value` but normalized to *ASCII lower case*, 

233 see :func:`~webencodings.ascii_lower`. 

234 This is the value to use when comparing to a CSS keyword. 

235 

236 """ 

237 __slots__ = ['value', 'lower_value'] 

238 type = 'ident' 

239 repr_format = '<{self.__class__.__name__} {self.value}>' 

240 

241 def __init__(self, line, column, value): 

242 Node.__init__(self, line, column) 

243 self.value = value 

244 try: 

245 self.lower_value = ascii_lower(value) 

246 except UnicodeEncodeError: 

247 self.lower_value = value 

248 

249 def _serialize_to(self, write): 

250 write(serialize_identifier(self.value)) 

251 

252 

253class AtKeywordToken(Node): 

254 """An :diagram:`at-keyword-token`. 

255 

256 .. code-block:: text 

257 

258 '@' <value> 

259 

260 .. autoattribute:: type 

261 

262 .. attribute:: value 

263 

264 The unescaped value, as a Unicode string, without the preceding ``@``. 

265 

266 .. attribute:: lower_value 

267 

268 Same as :attr:`value` but normalized to *ASCII lower case*, 

269 see :func:`~webencodings.ascii_lower`. 

270 This is the value to use when comparing to a CSS at-keyword. 

271 

272 .. code-block:: python 

273 

274 if node.type == 'at-keyword' and node.lower_value == 'import': 

275 

276 """ 

277 __slots__ = ['value', 'lower_value'] 

278 type = 'at-keyword' 

279 repr_format = '<{self.__class__.__name__} @{self.value}>' 

280 

281 def __init__(self, line, column, value): 

282 Node.__init__(self, line, column) 

283 self.value = value 

284 try: 

285 self.lower_value = ascii_lower(value) 

286 except UnicodeEncodeError: 

287 self.lower_value = value 

288 

289 def _serialize_to(self, write): 

290 write('@') 

291 write(serialize_identifier(self.value)) 

292 

293 

294class HashToken(Node): 

295 r"""A :diagram:`hash-token`. 

296 

297 .. code-block:: text 

298 

299 '#' <value> 

300 

301 .. autoattribute:: type 

302 

303 .. attribute:: value 

304 

305 The unescaped value, as a Unicode string, without the preceding ``#``. 

306 

307 .. attribute:: is_identifier 

308 

309 A boolean, true if the CSS source for this token 

310 was ``#`` followed by a valid identifier. 

311 (Only such hash tokens are valid ID selectors.) 

312 

313 """ 

314 __slots__ = ['value', 'is_identifier'] 

315 type = 'hash' 

316 repr_format = '<{self.__class__.__name__} #{self.value}>' 

317 

318 def __init__(self, line, column, value, is_identifier): 

319 Node.__init__(self, line, column) 

320 self.value = value 

321 self.is_identifier = is_identifier 

322 

323 def _serialize_to(self, write): 

324 write('#') 

325 if self.is_identifier: 

326 write(serialize_identifier(self.value)) 

327 else: 

328 write(serialize_name(self.value)) 

329 

330 

331class StringToken(Node): 

332 """A :diagram:`string-token`. 

333 

334 .. code-block:: text 

335 

336 '"' <value> '"' 

337 

338 .. autoattribute:: type 

339 

340 .. attribute:: value 

341 

342 The unescaped value, as a Unicode string, without the quotes. 

343 

344 """ 

345 __slots__ = ['value', 'representation'] 

346 type = 'string' 

347 repr_format = '<{self.__class__.__name__} {self.representation}>' 

348 

349 def __init__(self, line, column, value, representation): 

350 Node.__init__(self, line, column) 

351 self.value = value 

352 self.representation = representation 

353 

354 def _serialize_to(self, write): 

355 write(self.representation) 

356 

357 

358class URLToken(Node): 

359 """An :diagram:`url-token`. 

360 

361 .. code-block:: text 

362 

363 'url(' <value> ')' 

364 

365 .. autoattribute:: type 

366 

367 .. attribute:: value 

368 

369 The unescaped URL, as a Unicode string, without the ``url(`` and ``)`` 

370 markers. 

371 

372 """ 

373 __slots__ = ['value', 'representation'] 

374 type = 'url' 

375 repr_format = '<{self.__class__.__name__} {self.representation}>' 

376 

377 def __init__(self, line, column, value, representation): 

378 Node.__init__(self, line, column) 

379 self.value = value 

380 self.representation = representation 

381 

382 def _serialize_to(self, write): 

383 write(self.representation) 

384 

385 

386class UnicodeRangeToken(Node): 

387 """A `unicode-range token <https://www.w3.org/TR/css-syntax-3/#urange>`_. 

388 

389 .. autoattribute:: type 

390 

391 .. attribute:: start 

392 

393 The start of the range, as an integer between 0 and 1114111. 

394 

395 .. attribute:: end 

396 

397 The end of the range, as an integer between 0 and 1114111. 

398 Same as :attr:`start` if the source only specified one value. 

399 

400 """ 

401 __slots__ = ['start', 'end'] 

402 type = 'unicode-range' 

403 repr_format = '<{self.__class__.__name__} {self.start} {self.end}>' 

404 

405 def __init__(self, line, column, start, end): 

406 Node.__init__(self, line, column) 

407 self.start = start 

408 self.end = end 

409 

410 def _serialize_to(self, write): 

411 if self.end == self.start: 

412 write('U+%X' % self.start) 

413 else: 

414 write('U+%X-%X' % (self.start, self.end)) 

415 

416 

417class NumberToken(Node): 

418 """A :diagram:`number-token`. 

419 

420 .. autoattribute:: type 

421 

422 .. attribute:: value 

423 

424 The numeric value as a :class:`float`. 

425 

426 .. attribute:: int_value 

427 

428 The numeric value as an :class:`int` 

429 if :attr:`is_integer` is true, :obj:`None` otherwise. 

430 

431 .. attribute:: is_integer 

432 

433 Whether the token was syntactically an integer, as a boolean. 

434 

435 .. attribute:: representation 

436 

437 The CSS representation of the value, as a Unicode string. 

438 

439 """ 

440 __slots__ = ['value', 'int_value', 'is_integer', 'representation'] 

441 type = 'number' 

442 repr_format = '<{self.__class__.__name__} {self.representation}>' 

443 

444 def __init__(self, line, column, value, int_value, representation): 

445 Node.__init__(self, line, column) 

446 self.value = value 

447 self.int_value = int_value 

448 self.is_integer = int_value is not None 

449 self.representation = representation 

450 

451 def _serialize_to(self, write): 

452 write(self.representation) 

453 

454 

455class PercentageToken(Node): 

456 """A :diagram:`percentage-token`. 

457 

458 .. code-block:: text 

459 

460 <representation> '%' 

461 

462 .. autoattribute:: type 

463 

464 .. attribute:: value 

465 

466 The value numeric as a :class:`float`. 

467 

468 .. attribute:: int_value 

469 

470 The numeric value as an :class:`int` 

471 if the token was syntactically an integer, 

472 or :obj:`None`. 

473 

474 .. attribute:: is_integer 

475 

476 Whether the token’s value was syntactically an integer, as a boolean. 

477 

478 .. attribute:: representation 

479 

480 The CSS representation of the value without the unit, 

481 as a Unicode string. 

482 

483 """ 

484 __slots__ = ['value', 'int_value', 'is_integer', 'representation'] 

485 type = 'percentage' 

486 repr_format = '<{self.__class__.__name__} {self.representation}%>' 

487 

488 def __init__(self, line, column, value, int_value, representation): 

489 Node.__init__(self, line, column) 

490 self.value = value 

491 self.int_value = int_value 

492 self.is_integer = int_value is not None 

493 self.representation = representation 

494 

495 def _serialize_to(self, write): 

496 write(self.representation) 

497 write('%') 

498 

499 

500class DimensionToken(Node): 

501 """A :diagram:`dimension-token`. 

502 

503 .. code-block:: text 

504 

505 <representation> <unit> 

506 

507 .. autoattribute:: type 

508 

509 .. attribute:: value 

510 

511 The value numeric as a :class:`float`. 

512 

513 .. attribute:: int_value 

514 

515 The numeric value as an :class:`int` 

516 if the token was syntactically an integer, 

517 or :obj:`None`. 

518 

519 .. attribute:: is_integer 

520 

521 Whether the token’s value was syntactically an integer, as a boolean. 

522 

523 .. attribute:: representation 

524 

525 The CSS representation of the value without the unit, 

526 as a Unicode string. 

527 

528 .. attribute:: unit 

529 

530 The unescaped unit, as a Unicode string. 

531 

532 .. attribute:: lower_unit 

533 

534 Same as :attr:`unit` but normalized to *ASCII lower case*, 

535 see :func:`~webencodings.ascii_lower`. 

536 This is the value to use when comparing to a CSS unit. 

537 

538 .. code-block:: python 

539 

540 if node.type == 'dimension' and node.lower_unit == 'px': 

541 

542 """ 

543 __slots__ = ['value', 'int_value', 'is_integer', 'representation', 

544 'unit', 'lower_unit'] 

545 type = 'dimension' 

546 repr_format = ('<{self.__class__.__name__} ' 

547 '{self.representation}{self.unit}>') 

548 

549 def __init__(self, line, column, value, int_value, representation, unit): 

550 Node.__init__(self, line, column) 

551 self.value = value 

552 self.int_value = int_value 

553 self.is_integer = int_value is not None 

554 self.representation = representation 

555 self.unit = unit 

556 self.lower_unit = ascii_lower(unit) 

557 

558 def _serialize_to(self, write): 

559 write(self.representation) 

560 # Disambiguate with scientific notation 

561 unit = self.unit 

562 if unit in ('e', 'E') or unit.startswith(('e-', 'E-')): 

563 write('\\65 ') 

564 write(serialize_name(unit[1:])) 

565 else: 

566 write(serialize_identifier(unit)) 

567 

568 

569class ParenthesesBlock(Node): 

570 """A :diagram:`()-block`. 

571 

572 .. code-block:: text 

573 

574 '(' <content> ')' 

575 

576 .. autoattribute:: type 

577 

578 .. attribute:: content 

579 

580 The content of the block, as list of :term:`component values`. 

581 The ``(`` and ``)`` markers themselves are not represented in the list. 

582 

583 """ 

584 __slots__ = ['content'] 

585 type = '() block' 

586 repr_format = '<{self.__class__.__name__} ( … )>' 

587 

588 def __init__(self, line, column, content): 

589 Node.__init__(self, line, column) 

590 self.content = content 

591 

592 def _serialize_to(self, write): 

593 write('(') 

594 _serialize_to(self.content, write) 

595 write(')') 

596 

597 

598class SquareBracketsBlock(Node): 

599 """A :diagram:`[]-block`. 

600 

601 .. code-block:: text 

602 

603 '[' <content> ']' 

604 

605 .. autoattribute:: type 

606 

607 .. attribute:: content 

608 

609 The content of the block, as list of :term:`component values`. 

610 The ``[`` and ``]`` markers themselves are not represented in the list. 

611 

612 """ 

613 __slots__ = ['content'] 

614 type = '[] block' 

615 repr_format = '<{self.__class__.__name__} [ … ]>' 

616 

617 def __init__(self, line, column, content): 

618 Node.__init__(self, line, column) 

619 self.content = content 

620 

621 def _serialize_to(self, write): 

622 write('[') 

623 _serialize_to(self.content, write) 

624 write(']') 

625 

626 

627class CurlyBracketsBlock(Node): 

628 """A :diagram:`{}-block`. 

629 

630 .. code-block:: text 

631 

632 '{' <content> '}' 

633 

634 .. autoattribute:: type 

635 

636 .. attribute:: content 

637 

638 The content of the block, as list of :term:`component values`. 

639 The ``[`` and ``]`` markers themselves are not represented in the list. 

640 

641 """ 

642 __slots__ = ['content'] 

643 type = '{} block' 

644 repr_format = '<{self.__class__.__name__} {{ … }}>' 

645 

646 def __init__(self, line, column, content): 

647 Node.__init__(self, line, column) 

648 self.content = content 

649 

650 def _serialize_to(self, write): 

651 write('{') 

652 _serialize_to(self.content, write) 

653 write('}') 

654 

655 

656class FunctionBlock(Node): 

657 """A :diagram:`function-block`. 

658 

659 .. code-block:: text 

660 

661 <name> '(' <arguments> ')' 

662 

663 .. autoattribute:: type 

664 

665 .. attribute:: name 

666 

667 The unescaped name of the function, as a Unicode string. 

668 

669 .. attribute:: lower_name 

670 

671 Same as :attr:`name` but normalized to *ASCII lower case*, 

672 see :func:`~webencodings.ascii_lower`. 

673 This is the value to use when comparing to a CSS function name. 

674 

675 .. attribute:: arguments 

676 

677 The arguments of the function, as list of :term:`component values`. 

678 The ``(`` and ``)`` markers themselves are not represented in the list. 

679 Commas are not special, but represented as :obj:`LiteralToken` objects 

680 in the list. 

681 

682 """ 

683 __slots__ = ['name', 'lower_name', 'arguments'] 

684 type = 'function' 

685 repr_format = '<{self.__class__.__name__} {self.name}( … )>' 

686 

687 def __init__(self, line, column, name, arguments): 

688 Node.__init__(self, line, column) 

689 self.name = name 

690 self.lower_name = ascii_lower(name) 

691 self.arguments = arguments 

692 

693 def _serialize_to(self, write): 

694 write(serialize_identifier(self.name)) 

695 write('(') 

696 _serialize_to(self.arguments, write) 

697 function = self 

698 while isinstance(function, FunctionBlock) and function.arguments: 

699 eof_in_string = ( 

700 isinstance(function.arguments[-1], ParseError) and 

701 function.arguments[-1].kind == 'eof-in-string') 

702 if eof_in_string: 

703 return 

704 function = function.arguments[-1] 

705 write(')') 

706 

707 

708class Declaration(Node): 

709 """A (property or descriptor) :diagram:`declaration`. 

710 

711 .. code-block:: text 

712 

713 <name> ':' <value> 

714 <name> ':' <value> '!important' 

715 

716 .. autoattribute:: type 

717 

718 .. attribute:: name 

719 

720 The unescaped name, as a Unicode string. 

721 

722 .. attribute:: lower_name 

723 

724 Same as :attr:`name` but normalized to *ASCII lower case*, 

725 see :func:`~webencodings.ascii_lower`. 

726 This is the value to use when comparing to 

727 a CSS property or descriptor name. 

728 

729 .. code-block:: python 

730 

731 if node.type == 'declaration' and node.lower_name == 'color': 

732 

733 .. attribute:: value 

734 

735 The declaration value as a list of :term:`component values`: 

736 anything between ``:`` and 

737 the end of the declaration, or ``!important``. 

738 

739 .. attribute:: important 

740 

741 A boolean, true if the declaration had an ``!important`` marker. 

742 It is up to the consumer to reject declarations that do not accept 

743 this flag, such as non-property descriptor declarations. 

744 

745 """ 

746 __slots__ = ['name', 'lower_name', 'value', 'important'] 

747 type = 'declaration' 

748 repr_format = '<{self.__class__.__name__} {self.name}: …>' 

749 

750 def __init__(self, line, column, name, lower_name, value, important): 

751 Node.__init__(self, line, column) 

752 self.name = name 

753 self.lower_name = lower_name 

754 self.value = value 

755 self.important = important 

756 

757 def _serialize_to(self, write): 

758 write(serialize_identifier(self.name)) 

759 write(':') 

760 _serialize_to(self.value, write) 

761 if self.important: 

762 write('!important') 

763 

764 

765class QualifiedRule(Node): 

766 """A :diagram:`qualified rule`. 

767 

768 .. code-block:: text 

769 

770 <prelude> '{' <content> '}' 

771 

772 The interpretation of qualified rules depend on their context. 

773 At the top-level of a stylesheet 

774 or in a conditional rule such as ``@media``, 

775 they are **style rules** where the :attr:`prelude` is Selectors list 

776 and the :attr:`content` is a list of property declarations. 

777 

778 .. autoattribute:: type 

779 

780 .. attribute:: prelude 

781 

782 The rule’s prelude, the part before the {} block, 

783 as a list of :term:`component values`. 

784 

785 .. attribute:: content 

786 

787 The rule’s content, the part inside the {} block, 

788 as a list of :term:`component values`. 

789 

790 """ 

791 __slots__ = ['prelude', 'content'] 

792 type = 'qualified-rule' 

793 repr_format = ('<{self.__class__.__name__} ' 

794 '… {{ … }}>') 

795 

796 def __init__(self, line, column, prelude, content): 

797 Node.__init__(self, line, column) 

798 self.prelude = prelude 

799 self.content = content 

800 

801 def _serialize_to(self, write): 

802 _serialize_to(self.prelude, write) 

803 write('{') 

804 _serialize_to(self.content, write) 

805 write('}') 

806 

807 

808class AtRule(Node): 

809 """An :diagram:`at-rule`. 

810 

811 .. code-block:: text 

812 

813 @<at_keyword> <prelude> '{' <content> '}' 

814 @<at_keyword> <prelude> ';' 

815 

816 The interpretation of at-rules depend on their at-keyword 

817 as well as their context. 

818 Most types of at-rules (ie. at-keyword values) 

819 are only allowed in some context, 

820 and must either end with a {} block or a semicolon. 

821 

822 .. autoattribute:: type 

823 

824 .. attribute:: at_keyword 

825 

826 The unescaped value of the rule’s at-keyword, 

827 without the ``@`` symbol, as a Unicode string. 

828 

829 .. attribute:: lower_at_keyword 

830 

831 Same as :attr:`at_keyword` but normalized to *ASCII lower case*, 

832 see :func:`~webencodings.ascii_lower`. 

833 This is the value to use when comparing to a CSS at-keyword. 

834 

835 .. code-block:: python 

836 

837 if node.type == 'at-rule' and node.lower_at_keyword == 'import': 

838 

839 .. attribute:: prelude 

840 

841 The rule’s prelude, the part before the {} block or semicolon, 

842 as a list of :term:`component values`. 

843 

844 .. attribute:: content 

845 

846 The rule’s content, if any. 

847 The block’s content as a list of :term:`component values` 

848 for at-rules with a {} block, 

849 or :obj:`None` for at-rules ending with a semicolon. 

850 

851 """ 

852 __slots__ = ['at_keyword', 'lower_at_keyword', 'prelude', 'content'] 

853 type = 'at-rule' 

854 repr_format = ('<{self.__class__.__name__} ' 

855 '@{self.at_keyword} … {{ … }}>') 

856 

857 def __init__(self, line, column, 

858 at_keyword, lower_at_keyword, prelude, content): 

859 Node.__init__(self, line, column) 

860 self.at_keyword = at_keyword 

861 self.lower_at_keyword = lower_at_keyword 

862 self.prelude = prelude 

863 self.content = content 

864 

865 def _serialize_to(self, write): 

866 write('@') 

867 write(serialize_identifier(self.at_keyword)) 

868 _serialize_to(self.prelude, write) 

869 if self.content is None: 

870 write(';') 

871 else: 

872 write('{') 

873 _serialize_to(self.content, write) 

874 write('}')