(config_lines, start, end, errors)
return blocks
The function basically do all the work. It reads the config from start, removes blank lines first then when it first
    sees data, it sets the 'indent' variable once for all. All blocks read in a given function call must have the same
    indentation. Sub-blocks are parsed recursively and thus the 'indent' variable is given a new value.
    Once a block is parsed the function will either recurse if the block requires it (see ConfigBlock), or it will go
    to the next block and iterate.
The function basically do all the work. It reads the config from start, removes blank lines first then when it first
    sees data, it sets the 'indent' variable once for all. All blocks read in a given function call must have the same
    indentation. Sub-blocks are parsed recursively and thus the 'indent' variable is given a new value.
    Once a block is parsed the function will either recurse if the block requires it (see ConfigBlock), or it will go
    to the next block and iterate.
The function basically do all the work . It reads the config from start removes blank lines first then when it first sees data it sets the indent variable once for all . All blocks read in a given function call must have the same indentation . Sub - blocks are parsed recursively and thus the indent variable is given a new value . Once a block is parsed the function will either recurse if the block requires it ( see ConfigBlock ) or it will go to the next block and iterate .
def _parse_for_config_blocks(config_lines, start, end, errors):
    """The function basically do all the work. It reads the config from start, removes blank lines first then when it first
    sees data, it sets the 'indent' variable once for all. All blocks read in a given function call must have the same
    indentation. Sub-blocks are parsed recursively and thus the 'indent' variable is given a new value.
    Once a block is parsed the function will either recurse if the block requires it (see ConfigBlock), or it will go
    to the next block and iterate.
    """
    idx = start
    blocks = []

    # Go to the first line with data (see 'is_blank')
    while idx < end:
        if is_blank(config_lines[idx]):
            idx += 1
            continue
        break
    else:
        return blocks

    # All blocks of a same level must have the same indentation. Let's use the first one to compare them
    indent = get_indent(config_lines[idx])

    while idx < end:
        current_line = config_lines[idx]
        if is_blank(current_line):
            idx += 1
            continue
        if not is_at_least_indented(current_line, indent):
            errors.append(ValidatorError("Content is not correctly indented - skipping rest of file", idx))
            # File will not be able to be parsed correctly if indentation is wrong
            return blocks

        cfg_block = ConfigBlock.parse_from_strings(idx, config_lines, indent, errors)
        # Even if there has been an issue when parsing the block, cfg_block.length always point to another block
        # (either a sub-block or not) or to EOF
        idx += cfg_block.length
        blocks.append(cfg_block)

        if cfg_block.should_recurse:
            # new_end points to the next line having the same indent as the cfg_block
            new_end = get_end_of_part(config_lines, idx, indent=indent)
            if new_end is None:
                block_name = cfg_block.param_prop.var_name if cfg_block.param_prop else "?"
                err_string = f"The object {block_name} cannot be parsed correctly, check indentation"
                errors.append(ValidatorError(err_string, idx))
                return blocks
            if new_end > end:
                new_end = end
            blocks += _parse_for_config_blocks(config_lines, idx, new_end, errors)
            idx = new_end

    return blocks
def _parse_for_config_blocks ( config_lines , start , end , errors ) : idx = start blocks = [ ] # Go to the first line with data (see 'is_blank') while idx < end : if is_blank ( config_lines [ idx ] ) : idx += 1 continue break else : return blocks # All blocks of a same level must have the same indentation. Let's use the first one to compare them indent = get_indent ( config_lines [ idx ] ) while idx < end : current_line = config_lines [ idx ] if is_blank ( current_line ) : idx += 1 continue if not is_at_least_indented ( current_line , indent ) : errors . append ( ValidatorError ( "Content is not correctly indented - skipping rest of file" , idx ) ) # File will not be able to be parsed correctly if indentation is wrong return blocks cfg_block = ConfigBlock . parse_from_strings ( idx , config_lines , indent , errors ) # Even if there has been an issue when parsing the block, cfg_block.length always point to another block # (either a sub-block or not) or to EOF idx += cfg_block . length blocks . append ( cfg_block ) if cfg_block . should_recurse : # new_end points to the next line having the same indent as the cfg_block new_end = get_end_of_part ( config_lines , idx , indent = indent ) if new_end is None : block_name = cfg_block . param_prop . var_name if cfg_block . param_prop else "?" err_string = f"The object {block_name} cannot be parsed correctly, check indentation" errors . append ( ValidatorError ( err_string , idx ) ) return blocks if new_end > end : new_end = end blocks += _parse_for_config_blocks ( config_lines , idx , new_end , errors ) idx = new_end return blocks