Пример #1
0
 def parse_import_rule(self, rule, previous_rules, errors, context):
     if context != 'stylesheet':
         raise ParseError(rule,
             '@import rule not allowed in ' + context)
     for previous_rule in previous_rules:
         if previous_rule.at_keyword not in ('@charset', '@import'):
             if previous_rule.at_keyword:
                 type_ = 'an {0} rule'.format(previous_rule.at_keyword)
             else:
                 type_ = 'a ruleset'
             raise ParseError(previous_rule,
                 '@import rule not allowed after ' + type_)
     head = rule.head
     if not head:
         raise ParseError(rule,
             'expected URI or STRING for @import rule')
     if head[0].type not in ('URI', 'STRING'):
         raise ParseError(rule,
             'expected URI or STRING for @import rule, got '
             + head[0].type)
     uri = head[0].value
     media = self.parse_media(strip_whitespace(head[1:]), errors)
     if rule.body is not None:
         # The position of the ';' token would be best, but we don’t
         # have it anymore here.
         raise ParseError(head[-1], "expected ';', got a block")
     return ImportRule(uri, media, rule.line, rule.column)
Пример #2
0
 def parse_import_rule(self, rule, previous_rules, errors, context):
     if context != 'stylesheet':
         raise ParseError(rule,
             '@import rule not allowed in ' + context)
     for previous_rule in previous_rules:
         if previous_rule.at_keyword not in ('@charset', '@import'):
             if previous_rule.at_keyword:
                 type_ = 'an {0} rule'.format(previous_rule.at_keyword)
             else:
                 type_ = 'a ruleset'
             raise ParseError(previous_rule,
                 '@import rule not allowed after ' + type_)
     head = rule.head
     if not head:
         raise ParseError(rule,
             'expected URI or STRING for @import rule')
     if head[0].type not in ('URI', 'STRING'):
         raise ParseError(rule,
             'expected URI or STRING for @import rule, got '
             + head[0].type)
     uri = head[0].value
     media = self.parse_media(strip_whitespace(head[1:]), errors)
     if rule.body is not None:
         # The position of the ';' token would be best, but we don’t
         # have it anymore here.
         raise ParseError(head[-1], "expected ';', got a block")
     return ImportRule(uri, media, rule.line, rule.column)
Пример #3
0
    def read_at_rule(self, at_keyword_token, tokens):
        """Read an at-rule from a token stream.

        :param at_keyword_token:
            The ATKEYWORD token that starts this at-rule
            You may have read it already to distinguish the rule
            from a ruleset.
        :param tokens:
            An iterator of subsequent tokens. Will be consumed just enough
            for one at-rule.
        :return:
            An unparsed :class:`AtRule`.
        :raises:
            :class:`~.parsing.ParseError` if the head is invalid for the core
            grammar. The body is **not** validated. See :class:`AtRule`.

        """
        # CSS syntax is case-insensitive
        at_keyword = at_keyword_token.value.lower()
        head = []
        # For the ParseError in case `tokens` is empty:
        token = at_keyword_token
        for token in tokens:
            if token.type in '{;':
                break
            # Ignore white space just after the at-keyword.
            else:
                head.append(token)
        # On unexpected end of stylesheet, pretend that a ';' was there
        head = strip_whitespace(head)
        for head_token in head:
            validate_any(head_token, 'at-rule head')
        body = token.content if token.type == '{' else None
        return AtRule(at_keyword, head, body,
                      at_keyword_token.line, at_keyword_token.column)
Пример #4
0
    def read_at_rule(self, at_keyword_token, tokens):
        """Read an at-rule from a token stream.

        :param at_keyword_token:
            The ATKEYWORD token that starts this at-rule
            You may have read it already to distinguish the rule
            from a ruleset.
        :param tokens:
            An iterator of subsequent tokens. Will be consumed just enough
            for one at-rule.
        :return:
            An unparsed :class:`AtRule`.
        :raises:
            :class:`~.parsing.ParseError` if the head is invalid for the core
            grammar. The body is **not** validated. See :class:`AtRule`.

        """
        # CSS syntax is case-insensitive
        at_keyword = at_keyword_token.value.lower()
        head = []
        # For the ParseError in case `tokens` is empty:
        token = at_keyword_token
        for token in tokens:
            if token.type in '{;':
                break
            # Ignore white space just after the at-keyword.
            else:
                head.append(token)
        # On unexpected end of stylesheet, pretend that a ';' was there
        head = strip_whitespace(head)
        for head_token in head:
            validate_any(head_token, 'at-rule head')
        body = token.content if token.type == '{' else None
        return AtRule(at_keyword, head, body,
                      at_keyword_token.line, at_keyword_token.column)
Пример #5
0
    def _selector_as_string(self, selector):
        """
        Returns a selector as a CSS string

        :param selector: A list of tinycss Tokens
        :type selector: list
        :returns: The CSS string for the selector
        :rtype: str
        """
        return ','.join(''.join(token.as_css()
                                for token in strip_whitespace(token_list))
                        for token_list in split_on_comma(selector))
Пример #6
0
    def _selector_as_string(self, selector):
        """
        Returns a selector as a CSS string

        :param selector: A list of tinycss Tokens
        :type selector: list
        :returns: The CSS string for the selector
        :rtype: str
        """
        return ",".join(
            "".join(token.as_css() for token in strip_whitespace(token_list)) for token_list in split_on_comma(selector)
        )
Пример #7
0
    def parse_declaration(self, tokens):
        """Parse a single declaration.

        :param tokens:
            an iterable of at least one token. Should stop at (before)
            the end of the declaration, as marked by a ``;`` or ``}``.
            Empty declarations (ie. consecutive ``;`` with only white space
            in-between) should be skipped earlier and not passed to
            this method.
        :returns:
            a :class:`Declaration`
        :raises:
            :class:`~.parsing.ParseError` if the tokens do not match the
            'declaration' production of the core grammar.

        """
        tokens = iter(tokens)

        name_token = next(tokens)  # assume there is at least one
        if name_token.type == 'IDENT':
            # CSS syntax is case-insensitive
            property_name = name_token.value.lower()
        else:
            raise ParseError(
                name_token,
                'expected a property name, got {0}'.format(name_token.type))

        token = name_token  # In case ``tokens`` is now empty
        for token in tokens:
            if token.type == ':':
                break
            elif token.type != 'S':
                raise ParseError(token,
                                 "expected ':', got {0}".format(token.type))
        else:
            raise ParseError(token, "expected ':'")

        value = strip_whitespace(list(tokens))
        if not value:
            raise ParseError(token, 'expected a property value')
        validate_value(value)
        value, priority = self.parse_value_priority(value)
        return Declaration(property_name, value, priority, name_token.line,
                           name_token.column)
Пример #8
0
    def parse_declaration(self, tokens):
        """Parse a single declaration.

        :param tokens:
            an iterable of at least one token. Should stop at (before)
            the end of the declaration, as marked by a ``;`` or ``}``.
            Empty declarations (ie. consecutive ``;`` with only white space
            in-between) should be skipped earlier and not passed to
            this method.
        :returns:
            a :class:`Declaration`
        :raises:
            :class:`~.parsing.ParseError` if the tokens do not match the
            'declaration' production of the core grammar.

        """
        tokens = iter(tokens)

        name_token = next(tokens)  # assume there is at least one
        if name_token.type == 'IDENT':
            # CSS syntax is case-insensitive
            property_name = name_token.value.lower()
        else:
            raise ParseError(name_token,
                'expected a property name, got {0}'.format(name_token.type))

        token = name_token  # In case ``tokens`` is now empty
        for token in tokens:
            if token.type == ':':
                break
            elif token.type != 'S':
                raise ParseError(
                    token, "expected ':', got {0}".format(token.type))
        else:
            raise ParseError(token, "expected ':'")

        value = strip_whitespace(list(tokens))
        if not value:
            raise ParseError(token, 'expected a property value')
        validate_value(value)
        value, priority = self.parse_value_priority(value)
        return Declaration(
            property_name, value, priority, name_token.line, name_token.column)
Пример #9
0
    def parse_ruleset(self, first_token, tokens):
        """Parse a ruleset: a selector followed by declaration block.

        :param first_token:
            The first token of the ruleset (probably of the selector).
            You may have read it already to distinguish the rule
            from an at-rule.
        :param tokens:
            an iterator of subsequent tokens. Will be consumed just enough
            for one ruleset.
        :return:
            a tuple of a :class:`RuleSet` and an error list.
            The errors are recovered :class:`~.parsing.ParseError` in declarations.
            (Parsing continues from the next declaration on such errors.)
        :raises:
            :class:`~.parsing.ParseError` if the selector is invalid for the
            core grammar.
            Note a that a selector can be valid for the core grammar but
            not for CSS 2.1 or another level.

        """
        selector = []
        for token in chain([first_token], tokens):
            if token.type == '{':
                # Parse/validate once we’ve read the whole rule
                selector = strip_whitespace(selector)
                if not selector:
                    raise ParseError(first_token, 'empty selector')
                for selector_token in selector:
                    validate_any(selector_token, 'selector')
                declarations, errors = self.parse_declaration_list(
                    token.content)
                ruleset = RuleSet(selector, declarations,
                                  first_token.line, first_token.column)
                return ruleset, errors
            else:
                selector.append(token)
        raise ParseError(token, 'no declaration block found for ruleset')
Пример #10
0
    def parse_ruleset(self, first_token, tokens):
        """Parse a ruleset: a selector followed by declaration block.

        :param first_token:
            The first token of the ruleset (probably of the selector).
            You may have read it already to distinguish the rule
            from an at-rule.
        :param tokens:
            an iterator of subsequent tokens. Will be consumed just enough
            for one ruleset.
        :return:
            a tuple of a :class:`RuleSet` and an error list.
            The errors are recovered :class:`~.parsing.ParseError` in declarations.
            (Parsing continues from the next declaration on such errors.)
        :raises:
            :class:`~.parsing.ParseError` if the selector is invalid for the
            core grammar.
            Note a that a selector can be valid for the core grammar but
            not for CSS 2.1 or another level.

        """
        selector = []
        for token in chain([first_token], tokens):
            if token.type == '{':
                # Parse/validate once we’ve read the whole rule
                selector = strip_whitespace(selector)
                if not selector:
                    raise ParseError(first_token, 'empty selector')
                for selector_token in selector:
                    validate_any(selector_token, 'selector')
                declarations, errors = self.parse_declaration_list(
                    token.content)
                ruleset = RuleSet(selector, declarations,
                                  first_token.line, first_token.column)
                return ruleset, errors
            else:
                selector.append(token)
        raise ParseError(token, 'no declaration block found for ruleset')
Пример #11
0
    def parse_declaration_list(self, tokens):
        """Parse a ``;`` separated declaration list.

        You may want to use :meth:`parse_declarations_and_at_rules` (or
        some other method that uses :func:`parse_declaration` directly)
        instead if you have not just declarations in the same context.

        :param tokens:
            an iterable of tokens. Should stop at (before) the end
            of the block, as marked by ``}``.
        :return:
            a tuple of the list of valid :class:`Declaration` and a list
            of :class:`~.parsing.ParseError`

        """
        # split at ';'
        parts = []
        this_part = []
        for token in tokens:
            if token.type == ';':
                parts.append(this_part)
                this_part = []
            else:
                this_part.append(token)
        parts.append(this_part)

        declarations = []
        errors = []
        for tokens in parts:
            tokens = strip_whitespace(tokens)
            if tokens:
                try:
                    declarations.append(self.parse_declaration(tokens))
                except ParseError as exc:
                    errors.append(exc)
                    # Skip the entire declaration
        return declarations, errors
Пример #12
0
    def parse_declaration_list(self, tokens):
        """Parse a ``;`` separated declaration list.

        You may want to use :meth:`parse_declarations_and_at_rules` (or
        some other method that uses :func:`parse_declaration` directly)
        instead if you have not just declarations in the same context.

        :param tokens:
            an iterable of tokens. Should stop at (before) the end
            of the block, as marked by ``}``.
        :return:
            a tuple of the list of valid :class:`Declaration` and a list
            of :class:`~.parsing.ParseError`

        """
        # split at ';'
        parts = []
        this_part = []
        for token in tokens:
            if token.type == ';':
                parts.append(this_part)
                this_part = []
            else:
                this_part.append(token)
        parts.append(this_part)

        declarations = []
        errors = []
        for tokens in parts:
            tokens = strip_whitespace(tokens)
            if tokens:
                try:
                    declarations.append(self.parse_declaration(tokens))
                except ParseError as exc:
                    errors.append(exc)
                    # Skip the entire declaration
        return declarations, errors