def preprocess_stylesheet(device_media_type, base_url, stylesheet_rules, url_fetcher,
                          matcher, page_rules, font_config, counter_style,
                          ignore_imports=False):
    """Do what can be done early on stylesheet, before being in a document."""
    for rule in stylesheet_rules:
        if getattr(rule, 'content', None) is None:
            if rule.type == 'error':
                LOGGER.warning(
                    "Parse error at %d:%d: %s",
                    rule.source_line, rule.source_column, rule.message)
            if rule.type != 'at-rule':
                continue
            if rule.lower_at_keyword != 'import':
                LOGGER.warning(
                    "Unknown empty rule %s at %d:%d",
                    rule, rule.source_line, rule.source_column)
                continue

        if rule.type == 'qualified-rule':
            try:
                logger_level = WARNING
                contents = tinycss2.parse_blocks_contents(rule.content)
                selectors_declarations = list(
                    preprocess_declarations(base_url, contents, rule.prelude))

                if selectors_declarations:
                    selectors_declarations = groupby(
                        selectors_declarations, key=lambda x: x[0])
                    for selectors, declarations in selectors_declarations:
                        declarations = [
                            declaration[1] for declaration in declarations]
                        for selector in selectors:
                            matcher.add_selector(selector, declarations)
                            if selector.pseudo_element not in PSEUDO_ELEMENTS:
                                prelude = tinycss2.serialize(rule.prelude)
                                if selector.pseudo_element.startswith('-'):
                                    logger_level = DEBUG
                                    raise cssselect2.SelectorError(
                                        f"'{prelude}', "
                                        'ignored prefixed pseudo-element: '
                                        f'{selector.pseudo_element}')
                                else:
                                    raise cssselect2.SelectorError(
                                        f"'{prelude}', "
                                        'unknown pseudo-element: '
                                        f'{selector.pseudo_element}')
                        ignore_imports = True
                else:
                    ignore_imports = True
            except cssselect2.SelectorError as exc:
                LOGGER.log(logger_level, 'Invalid or unsupported selector, %s', exc)
                continue

        elif rule.type == 'at-rule' and rule.lower_at_keyword == 'import':
            if ignore_imports:
                LOGGER.warning(
                    '@import rule %r not at the beginning of the '
                    'the whole rule was ignored at %d:%d.',
                    tinycss2.serialize(rule.prelude),
                    rule.source_line, rule.source_column)
                continue

            tokens = remove_whitespace(rule.prelude)
            url = None
            if tokens:
                if tokens[0].type == 'string':
                    url = url_join(
                        base_url, tokens[0].value, allow_relative=False,
                        context='@import at %s:%s',
                        context_args=(rule.source_line, rule.source_column))
                else:
                    url_tuple = get_url(tokens[0], base_url)
                    if url_tuple and url_tuple[1][0] == 'external':
                        url = url_tuple[1][1]
            if url is None:
                continue
            media = media_queries.parse_media_query(tokens[1:])
            if media is None:
                LOGGER.warning(
                    'Invalid media type %r '
                    'the whole @import rule was ignored at %d:%d.',
                    tinycss2.serialize(rule.prelude),
                    rule.source_line, rule.source_column)
                continue
            if not media_queries.evaluate_media_query(media, device_media_type):
                continue
            if url is not None:
                try:
                    CSS(
                        url=url, url_fetcher=url_fetcher, media_type=device_media_type,
                        font_config=font_config, counter_style=counter_style,
                        matcher=matcher, page_rules=page_rules)
                except URLFetchingError as exception:
                    LOGGER.error('Failed to load stylesheet at %s : %s', url, exception)
                    LOGGER.debug('Error while loading stylesheet:', exc_info=exception)

        elif rule.type == 'at-rule' and rule.lower_at_keyword == 'media':
            media = media_queries.parse_media_query(rule.prelude)
            if media is None:
                LOGGER.warning(
                    'Invalid media type %r '
                    'the whole @media rule was ignored at %d:%d.',
                    tinycss2.serialize(rule.prelude),
                    rule.source_line, rule.source_column)
                continue
            ignore_imports = True
            if not media_queries.evaluate_media_query(media, device_media_type):
                continue
            content_rules = tinycss2.parse_rule_list(rule.content)
            preprocess_stylesheet(
                device_media_type, base_url, content_rules, url_fetcher, matcher,
                page_rules, font_config, counter_style, ignore_imports=True)

        elif rule.type == 'at-rule' and rule.lower_at_keyword == 'page':
            data = parse_page_selectors(rule)

            if data is None:
                LOGGER.warning(
                    'Unsupported @page selector %r, '
                    'the whole @page rule was ignored at %d:%d.',
                    tinycss2.serialize(rule.prelude),
                    rule.source_line, rule.source_column)
                continue

            ignore_imports = True
            for page_data in data:
                specificity = page_data.pop('specificity')
                page_selector_type = PageSelectorType(**page_data)
                content = tinycss2.parse_blocks_contents(rule.content)
                declarations = list(preprocess_declarations(base_url, content))

                if declarations:
                    selector_list = [(specificity, None, page_selector_type)]
                    page_rules.append((rule, selector_list, declarations))

                for margin_rule in content:
                    if margin_rule.type != 'at-rule' or margin_rule.content is None:
                        continue
                    declarations = list(preprocess_declarations(
                        base_url,
                        tinycss2.parse_blocks_contents(margin_rule.content)))
                    if declarations:
                        selector_list = [(
                            specificity, f'@{margin_rule.lower_at_keyword}',
                            page_selector_type)]
                        page_rules.append((margin_rule, selector_list, declarations))

        elif rule.type == 'at-rule' and rule.lower_at_keyword == 'font-face':
            ignore_imports = True
            content = tinycss2.parse_blocks_contents(rule.content)
            rule_descriptors = dict(
                preprocess_descriptors('font-face', base_url, content))
            for key in ('src', 'font_family'):
                if key not in rule_descriptors:
                    LOGGER.warning(
                        "Missing %s descriptor in '@font-face' rule at %d:%d",
                        key.replace('_', '-'), rule.source_line, rule.source_column)
                    break
            else:
                if font_config is not None:
                    font_config.add_font_face(rule_descriptors, url_fetcher)

        elif rule.type == 'at-rule' and rule.lower_at_keyword == 'counter-style':
            name = counters.parse_counter_style_name(rule.prelude, counter_style)
            if name is None:
                LOGGER.warning(
                    'Invalid counter style name %r, the whole '
                    '@counter-style rule was ignored at %d:%d.',
                    tinycss2.serialize(rule.prelude), rule.source_line,
                    rule.source_column)
                continue

            ignore_imports = True
            content = tinycss2.parse_blocks_contents(rule.content)
            counter = {
                'system': None,
                'negative': None,
                'prefix': None,
                'suffix': None,
                'range': None,
                'pad': None,
                'fallback': None,
                'symbols': None,
                'additive_symbols': None,
            }
            rule_descriptors = preprocess_descriptors(
                'counter-style', base_url, content)

            for descriptor_name, descriptor_value in rule_descriptors:
                counter[descriptor_name] = descriptor_value

            if counter['system'] is None:
                system = (None, 'symbolic', None)
            else:
                system = counter['system']

            if system[0] is None:
                if system[1] in ('cyclic', 'fixed', 'symbolic'):
                    if len(counter['symbols'] or []) < 1:
                        LOGGER.warning(
                            'In counter style %r at %d:%d, '
                            'counter style %r needs at least one symbol',
                            name, rule.source_line, rule.source_column, system[1])
                        continue
                elif system[1] in ('alphabetic', 'numeric'):
                    if len(counter['symbols'] or []) < 2:
                        LOGGER.warning(
                            'In counter style %r at %d:%d, '
                            'counter style %r needs at least two symbols',
                            name, rule.source_line, rule.source_column, system[1])
                        continue
                elif system[1] == 'additive':
                    if len(counter['additive_symbols'] or []) < 2:
                        LOGGER.warning(
                            'In counter style %r at %d:%d, '
                            'counter style "additive" '
                            'needs at least two additive symbols',
                            name, rule.source_line, rule.source_column)
                        continue

            counter_style[name] = counter

        else:
            LOGGER.warning(
                "Unknown rule %s at %d:%d",
                rule, rule.source_line, rule.source_column)
Do what can be done early on stylesheet, before being in a document.
weasyprint/css/__init__.py