def grid_template(tokens):
    """``grid-template-columns`` and ``grid-template-rows`` validation."""
    return_tokens = []
    if len(tokens) == 1 and get_keyword(tokens[0]) == 'none':
        return 'none'
    if get_keyword(tokens[0]) == 'subgrid':
        return_tokens.append('subgrid')
        subgrid_tokens = []
        for token in tokens[1:]:
            line_names = _line_names(token)
            if line_names is not None:
                subgrid_tokens.append(line_names)
                continue
            function = parse_function(token)
            if function:
                name, args = function
                if name == 'repeat' and len(args) >= 2:
                    if (args[0].type == 'number' and
                            args[0].is_integer and args[0].value >= 1):
                        number = args[0].int_value
                    elif get_keyword(args[0]) == 'auto-fill':
                        number = 'auto-fill'
                    else:
                        return
                    line_names_list = []
                    for arg in args[1:]:
                        line_names = _line_names(arg)
                        if line_names is not None:
                            line_names_list.append(line_names)
                    subgrid_tokens.append(
                        ('repeat()', number, tuple(line_names_list)))
                    continue
            return
        return_tokens.append(tuple(subgrid_tokens))
    else:
        includes_auto_repeat = False
        includes_track = False
        last_is_line_name = False
        for token in tokens:
            line_names = _line_names(token)
            if line_names is not None:
                if last_is_line_name:
                    return
                last_is_line_name = True
                return_tokens.append(line_names)
                continue
            fixed_size = _fixed_size(token)
            if fixed_size:
                if not last_is_line_name:
                    return_tokens.append(())
                last_is_line_name = False
                return_tokens.append(fixed_size)
                continue
            track_size = _track_size(token)
            if track_size:
                if not last_is_line_name:
                    return_tokens.append(())
                last_is_line_name = False
                return_tokens.append(track_size)
                includes_track = True
                continue
            function = parse_function(token)
            if function:
                name, args = function
                if name == 'repeat' and len(args) >= 2:
                    if (args[0].type == 'number' and
                            args[0].is_integer and args[0].value >= 1):
                        number = args[0].int_value
                    elif get_keyword(args[0]) in ('auto-fill', 'auto-fit'):
                        # auto-repeat
                        if includes_auto_repeat:
                            return
                        number = args[0].value
                        includes_auto_repeat = True
                    else:
                        return
                    names_and_sizes = []
                    repeat_last_is_line_name = False
                    for arg in args[1:]:
                        line_names = _line_names(arg)
                        if line_names is not None:
                            if repeat_last_is_line_name:
                                return
                            names_and_sizes.append(line_names)
                            repeat_last_is_line_name = True
                            continue
                        # fixed-repead
                        fixed_size = _fixed_size(arg)
                        if fixed_size:
                            if not repeat_last_is_line_name:
                                names_and_sizes.append(())
                            repeat_last_is_line_name = False
                            names_and_sizes.append(fixed_size)
                            continue
                        # track-repeat
                        track_size = _track_size(arg)
                        if track_size:
                            includes_track = True
                            if not repeat_last_is_line_name:
                                names_and_sizes.append(())
                            repeat_last_is_line_name = False
                            names_and_sizes.append(track_size)
                            continue
                        return
                    if not last_is_line_name:
                        return_tokens.append(())
                    last_is_line_name = False
                    if not repeat_last_is_line_name:
                        names_and_sizes.append(())
                    return_tokens.append(
                        ('repeat()', number, tuple(names_and_sizes)))
                    continue
            return
        if includes_auto_repeat and includes_track:
            return
        if not last_is_line_name:
            return_tokens.append(())
    return tuple(return_tokens)
``grid-template-columns`` and ``grid-template-rows`` validation.
weasyprint/css/validation/properties.py