Skip to content

yaml_reader

read_yaml(file_path_or_contents, read_type=None)

Parse YAML/JSON content from file path or string.

Why

Validation errors must point to exact YAML locations. CommentedMap preserves source coordinates that map Pydantic errors back to input lines, enabling user-friendly error tables showing exactly where mistakes occur in the input file.

Example
data = read_yaml(pathlib.Path("cv.yaml"))
name = data["cv"]["name"]  # Regular dict access
# Line info also available: data.lc.data["cv"][0] = (line, col)

Parameters:

  • file_path_or_contents (Path | str) –

    File path or raw YAML string.

  • read_type (Literal['safe'] | None, default: None ) –

    Parsing mode passed to ruamel.yaml.

Returns:

  • CommentedMap

    Dictionary with line/column metadata for error reporting.

Source code in src/rendercv/schema/yaml_reader.py
def read_yaml(
    file_path_or_contents: pathlib.Path | str,
    read_type: Literal["safe"] | None = None,
) -> CommentedMap:
    """Parse YAML/JSON content from file path or string.

    Why:
        Validation errors must point to exact YAML locations. CommentedMap
        preserves source coordinates that map Pydantic errors back to input
        lines, enabling user-friendly error tables showing exactly where
        mistakes occur in the input file.

    Example:
        ```py
        data = read_yaml(pathlib.Path("cv.yaml"))
        name = data["cv"]["name"]  # Regular dict access
        # Line info also available: data.lc.data["cv"][0] = (line, col)
        ```

    Args:
        file_path_or_contents: File path or raw YAML string.
        read_type: Parsing mode passed to ruamel.yaml.

    Returns:
        Dictionary with line/column metadata for error reporting.
    """
    if isinstance(file_path_or_contents, pathlib.Path):
        # Check if the file exists:
        if not file_path_or_contents.exists():
            message = f"The input file `{file_path_or_contents}` doesn't exist!"
            raise RenderCVUserError(message)

        # Check the file extension:
        accepted_extensions = [".yaml", ".yml", ".json", ".json5"]
        if file_path_or_contents.suffix not in accepted_extensions:
            message = (
                "The input file should have one of the following extensions:"
                f" {', '.join(accepted_extensions)}. The input file is"
                f" {file_path_or_contents.name}."
            )
            raise RenderCVUserError(message)

        file_content = file_path_or_contents.read_text(encoding="utf-8")
    else:
        file_content = file_path_or_contents

    yaml = ruamel.yaml.YAML(typ=read_type)

    # Disable ISO date parsing, keep it as a string:
    yaml.constructor.yaml_constructors["tag:yaml.org,2002:timestamp"] = (
        lambda loader, node: loader.construct_scalar(node)
    )

    yaml_as_dictionary: CommentedMap = yaml.load(file_content)

    if yaml_as_dictionary is None:
        message = "The input file is empty!"
        raise RenderCVUserError(message)

    if isinstance(yaml_as_dictionary, str):
        message = (
            "You probably meant to pass a path to the YAML file, but you passed as a"
            " string and RenderCV interpreted it as the contents of the YAML file."
            f" Pass the path using `pathlib.Path({file_path_or_contents})`."
        )
        raise RenderCVInternalError(message)

    return yaml_as_dictionary