Skip to content

rendercv.data

The rendercv.data package contains the necessary classes and functions for

  • Parsing and validating a YAML input file
  • Computing some properties based on a YAML input file (like converting ISO dates to plain English, URLs of social networks, etc.)
  • Generating a JSON Schema for RenderCV's data format
  • Generating a sample YAML input file

The validators and data format of RenderCV are written using Pydantic.

BulletEntry

Bases: RenderCVBaseModelWithExtraKeys

This class is the data model of BulletEntry.

Source code in rendercv/data/models/entry_types.py
class BulletEntry(RenderCVBaseModelWithExtraKeys):
    """This class is the data model of `BulletEntry`."""

    bullet: str = pydantic.Field(
        title="Bullet",
        description="The bullet of the BulletEntry.",
    )

    def make_keywords_bold(self, keywords: list[str]) -> "BulletEntry":
        """Make the given keywords bold in the `bullet` field.

        Args:
            keywords: The keywords to make bold.

        Returns:
            A BulletEntry with the keywords made bold in the `bullet` field.
        """
        self.bullet = make_keywords_bold_in_a_string(self.bullet, keywords)
        return self

make_keywords_bold(keywords)

Make the given keywords bold in the bullet field.

Parameters:

  • keywords (list[str]) –

    The keywords to make bold.

Returns:

  • BulletEntry

    A BulletEntry with the keywords made bold in the bullet field.

Source code in rendercv/data/models/entry_types.py
def make_keywords_bold(self, keywords: list[str]) -> "BulletEntry":
    """Make the given keywords bold in the `bullet` field.

    Args:
        keywords: The keywords to make bold.

    Returns:
        A BulletEntry with the keywords made bold in the `bullet` field.
    """
    self.bullet = make_keywords_bold_in_a_string(self.bullet, keywords)
    return self

CurriculumVitae

Bases: RenderCVBaseModelWithExtraKeys

This class is the data model of the cv field.

Source code in rendercv/data/models/curriculum_vitae.py
class CurriculumVitae(RenderCVBaseModelWithExtraKeys):
    """This class is the data model of the `cv` field."""

    name: Optional[str] = pydantic.Field(
        default=None,
        title="Name",
        description="The name of the person.",
    )
    location: Optional[str] = pydantic.Field(
        default=None,
        title="Location",
        description="The location of the person.",
    )
    email: Optional[pydantic.EmailStr] = pydantic.Field(
        default=None,
        title="Email",
        description="The email address of the person.",
    )
    photo: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="Photo",
        description="Path to the photo of the person, relatie to the input file.",
    )
    phone: Optional[pydantic_phone_numbers.PhoneNumber] = pydantic.Field(
        default=None,
        title="Phone",
        description="The phone number of the person, including the country code.",
    )
    website: Optional[pydantic.HttpUrl] = pydantic.Field(
        default=None,
        title="Website",
        description="The website of the person.",
    )
    social_networks: Optional[list[SocialNetwork]] = pydantic.Field(
        default=None,
        title="Social Networks",
        description="The social networks of the person.",
    )
    sections_input: Sections = pydantic.Field(
        default=None,
        title="Sections",
        description="The sections of the CV.",
        # This is an alias to allow users to use `sections` in the YAML file:
        # `sections` key is preserved for RenderCV's internal use.
        alias="sections",
    )

    @pydantic.field_validator("photo")
    @classmethod
    def update_photo_path(cls, value: Optional[pathlib.Path]) -> Optional[pathlib.Path]:
        """Cast `photo` to Path and make the path absolute"""
        if value:
            from .rendercv_data_model import INPUT_FILE_DIRECTORY

            if INPUT_FILE_DIRECTORY is not None:
                profile_picture_parent_folder = INPUT_FILE_DIRECTORY
            else:
                profile_picture_parent_folder = pathlib.Path.cwd()

            return profile_picture_parent_folder / str(value)

        return value

    @pydantic.field_validator("name")
    @classmethod
    def update_curriculum_vitae(cls, value: str, info: pydantic.ValidationInfo) -> str:
        """Update the `curriculum_vitae` dictionary."""
        if value:
            curriculum_vitae[info.field_name] = value  # type: ignore

        return value

    @functools.cached_property
    def connections(self) -> list[dict[str, Optional[str]]]:
        """Return all the connections of the person as a list of dictionaries and cache
        `connections` as an attribute of the instance. The connections are used in the
        header of the CV.

        Returns:
            The connections of the person.
        """

        connections: list[dict[str, Optional[str]]] = []

        if self.location is not None:
            connections.append(
                {
                    "typst_icon": "location-dot",
                    "url": None,
                    "clean_url": None,
                    "placeholder": self.location,
                }
            )

        if self.email is not None:
            connections.append(
                {
                    "typst_icon": "envelope",
                    "url": f"mailto:{self.email}",
                    "clean_url": self.email,
                    "placeholder": self.email,
                }
            )

        if self.phone is not None:
            phone_placeholder = computers.format_phone_number(self.phone)
            connections.append(
                {
                    "typst_icon": "phone",
                    "url": self.phone,
                    "clean_url": phone_placeholder,
                    "placeholder": phone_placeholder,
                }
            )

        if self.website is not None:
            website_placeholder = computers.make_a_url_clean(str(self.website))
            connections.append(
                {
                    "typst_icon": "link",
                    "url": str(self.website),
                    "clean_url": website_placeholder,
                    "placeholder": website_placeholder,
                }
            )

        if self.social_networks is not None:
            icon_dictionary = {
                "LinkedIn": "linkedin",
                "GitHub": "github",
                "GitLab": "gitlab",
                "Instagram": "instagram",
                "Mastodon": "mastodon",
                "ORCID": "orcid",
                "StackOverflow": "stack-overflow",
                "ResearchGate": "researchgate",
                "YouTube": "youtube",
                "Google Scholar": "graduation-cap",
                "Telegram": "telegram",
            }
            for social_network in self.social_networks:
                clean_url = computers.make_a_url_clean(social_network.url)
                connection = {
                    "typst_icon": icon_dictionary[social_network.network],
                    "url": social_network.url,
                    "clean_url": clean_url,
                    "placeholder": social_network.username,
                }

                if social_network.network == "StackOverflow":
                    username = social_network.username.split("/")[1]
                    connection["placeholder"] = username
                if social_network.network == "Google Scholar":
                    connection["placeholder"] = "Google Scholar"

                connections.append(connection)  # type: ignore

        return connections

    @functools.cached_property
    def sections(self) -> list[SectionBase]:
        """Compute the sections of the CV based on the input sections.

        The original `sections` input is a dictionary where the keys are the section titles
        and the values are the list of entries in that section. This function converts the
        input sections to a list of `SectionBase` objects. This makes it easier to work with
        the sections in the rest of the code.

        Returns:
            The computed sections.
        """
        sections: list[SectionBase] = []

        if self.sections_input is not None:
            for title, entries in self.sections_input.items():
                formatted_title = computers.dictionary_key_to_proper_section_title(
                    title
                )

                # The first entry can be used because all the entries in the section are
                # already validated with the `validate_a_section` function:
                entry_type_name, _ = get_entry_type_name_and_section_validator(
                    entries[0],  # type: ignore
                    entry_types=entry_types.available_entry_models,
                )

                # SectionBase is used so that entries are not validated again:
                section = SectionBase(
                    title=formatted_title,
                    entry_type=entry_type_name,
                    entries=entries,
                )
                sections.append(section)

        return sections

connections: list[dict[str, Optional[str]]] cached property

Return all the connections of the person as a list of dictionaries and cache connections as an attribute of the instance. The connections are used in the header of the CV.

Returns:

  • list[dict[str, Optional[str]]]

    The connections of the person.

sections: list[SectionBase] cached property

Compute the sections of the CV based on the input sections.

The original sections input is a dictionary where the keys are the section titles and the values are the list of entries in that section. This function converts the input sections to a list of SectionBase objects. This makes it easier to work with the sections in the rest of the code.

Returns:

update_photo_path(value) classmethod

Cast photo to Path and make the path absolute

Source code in rendercv/data/models/curriculum_vitae.py
@pydantic.field_validator("photo")
@classmethod
def update_photo_path(cls, value: Optional[pathlib.Path]) -> Optional[pathlib.Path]:
    """Cast `photo` to Path and make the path absolute"""
    if value:
        from .rendercv_data_model import INPUT_FILE_DIRECTORY

        if INPUT_FILE_DIRECTORY is not None:
            profile_picture_parent_folder = INPUT_FILE_DIRECTORY
        else:
            profile_picture_parent_folder = pathlib.Path.cwd()

        return profile_picture_parent_folder / str(value)

    return value

update_curriculum_vitae(value, info) classmethod

Update the curriculum_vitae dictionary.

Source code in rendercv/data/models/curriculum_vitae.py
@pydantic.field_validator("name")
@classmethod
def update_curriculum_vitae(cls, value: str, info: pydantic.ValidationInfo) -> str:
    """Update the `curriculum_vitae` dictionary."""
    if value:
        curriculum_vitae[info.field_name] = value  # type: ignore

    return value

EducationEntry

Bases: EntryBase, EducationEntryBase

This class is the data model of EducationEntry. EducationEntry class is created by combining the EntryBase and EducationEntryBase classes to have the fields in the correct order.

Source code in rendercv/data/models/entry_types.py
class EducationEntry(EntryBase, EducationEntryBase):
    """This class is the data model of `EducationEntry`. `EducationEntry` class is
    created by combining the `EntryBase` and `EducationEntryBase` classes to have the
    fields in the correct order.
    """

ExperienceEntry

Bases: EntryBase, ExperienceEntryBase

This class is the data model of ExperienceEntry. ExperienceEntry class is created by combining the EntryBase and ExperienceEntryBase classes to have the fields in the correct order.

Source code in rendercv/data/models/entry_types.py
class ExperienceEntry(EntryBase, ExperienceEntryBase):
    """This class is the data model of `ExperienceEntry`. `ExperienceEntry` class is
    created by combining the `EntryBase` and `ExperienceEntryBase` classes to have the
    fields in the correct order.
    """

Locale

Bases: RenderCVBaseModelWithoutExtraKeys

This class is the data model of the locale catalog. The values of each field updates the locale dictionary.

Source code in rendercv/data/models/locale.py
class Locale(RenderCVBaseModelWithoutExtraKeys):
    """This class is the data model of the locale catalog. The values of each field
    updates the `locale` dictionary.
    """

    language: pydantic_extra_types.language_code.LanguageAlpha2 = pydantic.Field(
        default="en",  # type: ignore
        title="Language",
        description=(
            "The language as an ISO 639 alpha-2 code. It is used for hyphenation"
            " patterns."
        ),
    )
    phone_number_format: Optional[Literal["national", "international", "E164"]] = (
        pydantic.Field(
            default="national",
            title="Phone Number Format",
            description=(
                "If 'national', phone numbers are formatted without the country code."
                " If 'international', phone numbers are formatted with the country"
                ' code. The default value is "national"'
            ),
        )
    )
    page_numbering_template: str = pydantic.Field(
        default="NAME - Page PAGE_NUMBER of TOTAL_PAGES",
        title="Page Numbering Template",
        description=(
            "The template of the page numbering. The following placeholders can be"
            " used:\n- NAME: The name of the person\n- PAGE_NUMBER: The current page"
            " number\n- TOTAL_PAGES: The total number of pages\n- TODAY: Today's date"
            ' with `locale.date_template`\nThe default value is "NAME -'
            ' Page PAGE_NUMBER of TOTAL_PAGES".'
        ),
    )
    last_updated_date_template: str = pydantic.Field(
        default="Last updated in TODAY",
        title="Last Updated Date Template",
        description=(
            "The template of the last updated date. The following placeholders can be"
            " used:\n- TODAY: Today's date with `locale.date_template`\nThe"
            ' default value is "Last updated in TODAY".'
        ),
    )
    date_template: Optional[str] = pydantic.Field(
        default="MONTH_ABBREVIATION YEAR",
        title="Date Template",
        description=(
            "The template of the date. The following placeholders can be"
            " used:\n-FULL_MONTH_NAME: Full name of the month\n- MONTH_ABBREVIATION:"
            " Abbreviation of the month\n- MONTH: Month as a number\n-"
            " MONTH_IN_TWO_DIGITS: Month as a number in two digits\n- YEAR: Year as a"
            " number\n- YEAR_IN_TWO_DIGITS: Year as a number in two digits\nThe"
            ' default value is "MONTH_ABBREVIATION YEAR".'
        ),
    )
    month: Optional[str] = pydantic.Field(
        default="month",
        title='Translation of "Month"',
        description='Translation of the word "month" in the locale.',
    )
    months: Optional[str] = pydantic.Field(
        default="months",
        title='Translation of "Months"',
        description='Translation of the word "months" in the locale.',
    )
    year: Optional[str] = pydantic.Field(
        default="year",
        title='Translation of "Year"',
        description='Translation of the word "year" in the locale.',
    )
    years: Optional[str] = pydantic.Field(
        default="years",
        title='Translation of "Years"',
        description='Translation of the word "years" in the locale.',
    )
    present: Optional[str] = pydantic.Field(
        default="present",
        title='Translation of "Present"',
        description='Translation of the word "present" in the locale.',
    )
    to: Optional[str] = pydantic.Field(
        default="–",  # NOQA: RUF001
        title='Translation of "To"',
        description=(
            "The word or character used to indicate a range in the locale (e.g.,"
            ' "2020 - 2021").'
        ),
    )
    abbreviations_for_months: Optional[
        Annotated[list[str], at.Len(min_length=12, max_length=12)]
    ] = pydantic.Field(
        # Month abbreviations are taken from
        # https://web.library.yale.edu/cataloging/months:
        default=[
            "Jan",
            "Feb",
            "Mar",
            "Apr",
            "May",
            "June",
            "July",
            "Aug",
            "Sept",
            "Oct",
            "Nov",
            "Dec",
        ],
        title="Abbreviations of Months",
        description="Abbreviations of the months in the locale.",
    )
    full_names_of_months: Optional[
        Annotated[list[str], at.Len(min_length=12, max_length=12)]
    ] = pydantic.Field(
        default=[
            "January",
            "February",
            "March",
            "April",
            "May",
            "June",
            "July",
            "August",
            "September",
            "October",
            "November",
            "December",
        ],
        title="Full Names of Months",
        description="Full names of the months in the locale.",
    )

    @pydantic.field_validator(
        "month",
        "months",
        "year",
        "years",
        "present",
        "abbreviations_for_months",
        "to",
        "full_names_of_months",
        "phone_number_format",
        "date_template",
    )
    @classmethod
    def update_locale(cls, value: str, info: pydantic.ValidationInfo) -> str:
        """Update the `locale` dictionary."""
        if value:
            locale[info.field_name] = value  # type: ignore

        return value

update_locale(value, info) classmethod

Update the locale dictionary.

Source code in rendercv/data/models/locale.py
@pydantic.field_validator(
    "month",
    "months",
    "year",
    "years",
    "present",
    "abbreviations_for_months",
    "to",
    "full_names_of_months",
    "phone_number_format",
    "date_template",
)
@classmethod
def update_locale(cls, value: str, info: pydantic.ValidationInfo) -> str:
    """Update the `locale` dictionary."""
    if value:
        locale[info.field_name] = value  # type: ignore

    return value

NormalEntry

Bases: EntryBase, NormalEntryBase

This class is the data model of NormalEntry. NormalEntry class is created by combining the EntryBase and NormalEntryBase classes to have the fields in the correct order.

Source code in rendercv/data/models/entry_types.py
class NormalEntry(EntryBase, NormalEntryBase):
    """This class is the data model of `NormalEntry`. `NormalEntry` class is created by
    combining the `EntryBase` and `NormalEntryBase` classes to have the fields in the
    correct order.
    """

OneLineEntry

Bases: RenderCVBaseModelWithExtraKeys

This class is the data model of OneLineEntry.

Source code in rendercv/data/models/entry_types.py
class OneLineEntry(RenderCVBaseModelWithExtraKeys):
    """This class is the data model of `OneLineEntry`."""

    label: str = pydantic.Field(
        title="Name",
        description="The label of the OneLineEntry.",
    )
    details: str = pydantic.Field(
        title="Details",
        description="The details of the OneLineEntry.",
    )

    def make_keywords_bold(self, keywords: list[str]) -> "OneLineEntry":
        """Make the given keywords bold in the `details` field.

        Args:
            keywords: The keywords to make bold.

        Returns:
            A OneLineEntry with the keywords made bold in the `details` field.
        """
        self.details = make_keywords_bold_in_a_string(self.details, keywords)
        return self

make_keywords_bold(keywords)

Make the given keywords bold in the details field.

Parameters:

  • keywords (list[str]) –

    The keywords to make bold.

Returns:

  • OneLineEntry

    A OneLineEntry with the keywords made bold in the details field.

Source code in rendercv/data/models/entry_types.py
def make_keywords_bold(self, keywords: list[str]) -> "OneLineEntry":
    """Make the given keywords bold in the `details` field.

    Args:
        keywords: The keywords to make bold.

    Returns:
        A OneLineEntry with the keywords made bold in the `details` field.
    """
    self.details = make_keywords_bold_in_a_string(self.details, keywords)
    return self

PublicationEntry

Bases: EntryWithDate, PublicationEntryBase

This class is the data model of PublicationEntry. PublicationEntry class is created by combining the EntryWithDate and PublicationEntryBase classes to have the fields in the correct order.

Source code in rendercv/data/models/entry_types.py
class PublicationEntry(EntryWithDate, PublicationEntryBase):
    """This class is the data model of `PublicationEntry`. `PublicationEntry` class is
    created by combining the `EntryWithDate` and `PublicationEntryBase` classes to have
    the fields in the correct order.
    """

RenderCommandSettings

Bases: RenderCVBaseModelWithoutExtraKeys

This class is the data model of the render command's settings.

Source code in rendercv/data/models/rendercv_settings.py
class RenderCommandSettings(RenderCVBaseModelWithoutExtraKeys):
    """This class is the data model of the `render` command's settings."""

    design: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="`design` Field's YAML File",
        description=(
            "The file path to the yaml file containing the `design` field separately."
        ),
    )

    rendercv_settings: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="`rendercv_settings` Field's YAML File",
        description=(
            "The file path to the yaml file containing the `rendercv_settings` field"
            " separately."
        ),
    )

    locale: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="`locale` Field's YAML File",
        description=(
            "The file path to the yaml file containing the `locale` field separately."
        ),
    )

    output_folder_name: str = pydantic.Field(
        default="rendercv_output",
        title="Output Folder Name",
        description=(
            "The name of the folder where the output files will be saved."
            f" {file_path_placeholder_description_without_default}\nThe default value"
            ' is "rendercv_output".'
        ),
    )

    pdf_path: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="PDF Path",
        description=(
            "The path to copy the PDF file to. If it is not provided, the PDF file will"
            f" not be copied. {file_path_placeholder_description}"
        ),
    )

    typst_path: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="Typst Path",
        description=(
            "The path to copy the Typst file to. If it is not provided, the Typst file"
            f" will not be copied. {file_path_placeholder_description}"
        ),
    )

    html_path: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="HTML Path",
        description=(
            "The path to copy the HTML file to. If it is not provided, the HTML file"
            f" will not be copied. {file_path_placeholder_description}"
        ),
    )

    png_path: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="PNG Path",
        description=(
            "The path to copy the PNG file to. If it is not provided, the PNG file will"
            f" not be copied. {file_path_placeholder_description}"
        ),
    )

    markdown_path: Optional[pathlib.Path] = pydantic.Field(
        default=None,
        title="Markdown Path",
        description=(
            "The path to copy the Markdown file to. If it is not provided, the Markdown"
            f" file will not be copied. {file_path_placeholder_description}"
        ),
    )

    dont_generate_html: bool = pydantic.Field(
        default=False,
        title="Don't Generate HTML",
        description=(
            "A boolean value to determine whether the HTML file will be generated. The"
            " default value is False."
        ),
    )

    dont_generate_markdown: bool = pydantic.Field(
        default=False,
        title="Don't Generate Markdown",
        description=(
            "A boolean value to determine whether the Markdown file will be generated."
            ' The default value is "false".'
        ),
    )

    dont_generate_png: bool = pydantic.Field(
        default=False,
        title="Don't Generate PNG",
        description=(
            "A boolean value to determine whether the PNG file will be generated. The"
            " default value is False."
        ),
    )

    watch: bool = pydantic.Field(
        default=False,
        title="Re-run RenderCV When the Input File is Updated",
        description=(
            "A boolean value to determine whether to re-run RenderCV when the input"
            'file is updated. The default value is "false".'
        ),
    )

    @pydantic.field_validator(
        "output_folder_name",
        mode="before",
    )
    @classmethod
    def replace_placeholders(cls, value: str) -> str:
        """Replaces the placeholders in a string with the corresponding values."""
        return replace_placeholders(value)

    @pydantic.field_validator(
        "design",
        "locale",
        "rendercv_settings",
        "pdf_path",
        "typst_path",
        "html_path",
        "png_path",
        "markdown_path",
        mode="before",
    )
    @classmethod
    def convert_string_to_path(cls, value: Optional[str]) -> Optional[pathlib.Path]:
        """Converts a string to a `pathlib.Path` object by replacing the placeholders
        with the corresponding values. If the path is not an absolute path, it is
        converted to an absolute path by prepending the current working directory.
        """
        if value is None:
            return None

        return convert_string_to_path(value)

replace_placeholders(value) classmethod

Replaces the placeholders in a string with the corresponding values.

Source code in rendercv/data/models/rendercv_settings.py
@pydantic.field_validator(
    "output_folder_name",
    mode="before",
)
@classmethod
def replace_placeholders(cls, value: str) -> str:
    """Replaces the placeholders in a string with the corresponding values."""
    return replace_placeholders(value)

convert_string_to_path(value) classmethod

Converts a string to a pathlib.Path object by replacing the placeholders with the corresponding values. If the path is not an absolute path, it is converted to an absolute path by prepending the current working directory.

Source code in rendercv/data/models/rendercv_settings.py
@pydantic.field_validator(
    "design",
    "locale",
    "rendercv_settings",
    "pdf_path",
    "typst_path",
    "html_path",
    "png_path",
    "markdown_path",
    mode="before",
)
@classmethod
def convert_string_to_path(cls, value: Optional[str]) -> Optional[pathlib.Path]:
    """Converts a string to a `pathlib.Path` object by replacing the placeholders
    with the corresponding values. If the path is not an absolute path, it is
    converted to an absolute path by prepending the current working directory.
    """
    if value is None:
        return None

    return convert_string_to_path(value)

RenderCVDataModel

Bases: RenderCVBaseModelWithoutExtraKeys

This class binds both the CV and the design information together.

Source code in rendercv/data/models/rendercv_data_model.py
class RenderCVDataModel(RenderCVBaseModelWithoutExtraKeys):
    """This class binds both the CV and the design information together."""

    # `cv` is normally required, but don't enforce it in JSON Schema to allow
    # `design` or `locale` fields to have individual YAML files.
    model_config = pydantic.ConfigDict(json_schema_extra={"required": []})
    cv: CurriculumVitae = pydantic.Field(
        title="Curriculum Vitae",
        description="The data of the CV.",
    )
    design: RenderCVDesign = pydantic.Field(
        default=ClassicThemeOptions(theme="classic"),
        title="Design",
        description=(
            "The design information of the CV. The default is the classic theme."
        ),
    )
    locale: Locale = pydantic.Field(
        default=None,  # type: ignore
        title="Locale Catalog",
        description=(
            "The locale catalog of the CV to allow the support of multiple languages."
        ),
    )
    rendercv_settings: RenderCVSettings = pydantic.Field(
        default=RenderCVSettings(),
        title="RenderCV Settings",
        description="The settings of the RenderCV.",
    )

    @pydantic.model_validator(mode="before")
    @classmethod
    def update_paths(
        cls, model, info: pydantic.ValidationInfo
    ) -> Optional[RenderCVSettings]:
        """Update the paths in the RenderCV settings."""
        global INPUT_FILE_DIRECTORY  # NOQA: PLW0603

        context = info.context
        if context:
            input_file_directory = context.get("input_file_directory", None)
            INPUT_FILE_DIRECTORY = input_file_directory
        else:
            INPUT_FILE_DIRECTORY = None

        return model

    @pydantic.field_validator("locale", mode="before")
    @classmethod
    def update_locale(cls, value) -> Locale:
        """Update the output folder name in the RenderCV settings."""
        # Somehow, we need this for `test_if_local_catalog_resets` to pass.
        if value is None:
            return Locale()

        return value

update_paths(model, info) classmethod

Update the paths in the RenderCV settings.

Source code in rendercv/data/models/rendercv_data_model.py
@pydantic.model_validator(mode="before")
@classmethod
def update_paths(
    cls, model, info: pydantic.ValidationInfo
) -> Optional[RenderCVSettings]:
    """Update the paths in the RenderCV settings."""
    global INPUT_FILE_DIRECTORY  # NOQA: PLW0603

    context = info.context
    if context:
        input_file_directory = context.get("input_file_directory", None)
        INPUT_FILE_DIRECTORY = input_file_directory
    else:
        INPUT_FILE_DIRECTORY = None

    return model

update_locale(value) classmethod

Update the output folder name in the RenderCV settings.

Source code in rendercv/data/models/rendercv_data_model.py
@pydantic.field_validator("locale", mode="before")
@classmethod
def update_locale(cls, value) -> Locale:
    """Update the output folder name in the RenderCV settings."""
    # Somehow, we need this for `test_if_local_catalog_resets` to pass.
    if value is None:
        return Locale()

    return value

RenderCVSettings

Bases: RenderCVBaseModelWithoutExtraKeys

This class is the data model of the RenderCV settings.

Source code in rendercv/data/models/rendercv_settings.py
class RenderCVSettings(RenderCVBaseModelWithoutExtraKeys):
    """This class is the data model of the RenderCV settings."""

    date: datetime.date = pydantic.Field(
        default=datetime.date.today(),
        title="Date",
        description=(
            "The date that will be used everywhere (e.g., in the output file names,"
            " last updated date, computation of time spans for the events that are"
            " currently happening, etc.). The default value is the current date."
        ),
    )
    render_command: Optional[RenderCommandSettings] = pydantic.Field(
        default=None,
        title="Render Command Settings",
        description=(
            "RenderCV's `render` command settings. They are the same as the command"
            " line arguments. CLI arguments have higher priority than the settings in"
            " the input file."
        ),
    )
    bold_keywords: list[str] = pydantic.Field(
        default=[],
        title="Bold Keywords",
        description=(
            "The keywords that will be bold in the output. The default value is an"
            " empty list."
        ),
    )

    @pydantic.field_validator("date")
    @classmethod
    def mock_today(cls, value: datetime.date) -> datetime.date:
        """Mocks the current date for testing."""

        global DATE_INPUT  # NOQA: PLW0603

        DATE_INPUT = value

        return value

mock_today(value) classmethod

Mocks the current date for testing.

Source code in rendercv/data/models/rendercv_settings.py
@pydantic.field_validator("date")
@classmethod
def mock_today(cls, value: datetime.date) -> datetime.date:
    """Mocks the current date for testing."""

    global DATE_INPUT  # NOQA: PLW0603

    DATE_INPUT = value

    return value

SocialNetwork

Bases: RenderCVBaseModelWithoutExtraKeys

This class is the data model of a social network.

Source code in rendercv/data/models/curriculum_vitae.py
class SocialNetwork(RenderCVBaseModelWithoutExtraKeys):
    """This class is the data model of a social network."""

    network: SocialNetworkName = pydantic.Field(
        title="Social Network",
        description="Name of the social network.",
    )
    username: str = pydantic.Field(
        title="Username",
        description="The username of the social network. The link will be generated.",
    )

    @pydantic.field_validator("username")
    @classmethod
    def check_username(cls, username: str, info: pydantic.ValidationInfo) -> str:
        """Check if the username is provided correctly."""
        if "network" not in info.data:
            # the network is either not provided or not one of the available social
            # networks. In this case, don't check the username, since Pydantic will
            # raise an error for the network.
            return username

        network = info.data["network"]

        return validate_a_social_network_username(username, network)

    @pydantic.model_validator(mode="after")  # type: ignore
    def check_url(self) -> "SocialNetwork":
        """Validate the URL of the social network."""
        if self.network == "Mastodon":
            # All the other social networks have valid URLs. Mastodon URLs contain both
            # the username and the domain. So, we need to validate if the url is valid.
            validate_url(self.url)

        return self

    @functools.cached_property
    def url(self) -> str:
        """Return the URL of the social network and cache `url` as an attribute of the
        instance.
        """
        if self.network == "Mastodon":
            # Split domain and username
            _, username, domain = self.username.split("@")
            url = f"https://{domain}/@{username}"
        else:
            url_dictionary = {
                "LinkedIn": "https://linkedin.com/in/",
                "GitHub": "https://github.com/",
                "GitLab": "https://gitlab.com/",
                "Instagram": "https://instagram.com/",
                "ORCID": "https://orcid.org/",
                "StackOverflow": "https://stackoverflow.com/users/",
                "ResearchGate": "https://researchgate.net/profile/",
                "YouTube": "https://youtube.com/@",
                "Google Scholar": "https://scholar.google.com/citations?user=",
                "Telegram": "https://t.me/",
            }
            url = url_dictionary[self.network] + self.username

        return url

url: str cached property

Return the URL of the social network and cache url as an attribute of the instance.

check_username(username, info) classmethod

Check if the username is provided correctly.

Source code in rendercv/data/models/curriculum_vitae.py
@pydantic.field_validator("username")
@classmethod
def check_username(cls, username: str, info: pydantic.ValidationInfo) -> str:
    """Check if the username is provided correctly."""
    if "network" not in info.data:
        # the network is either not provided or not one of the available social
        # networks. In this case, don't check the username, since Pydantic will
        # raise an error for the network.
        return username

    network = info.data["network"]

    return validate_a_social_network_username(username, network)

check_url()

Validate the URL of the social network.

Source code in rendercv/data/models/curriculum_vitae.py
@pydantic.model_validator(mode="after")  # type: ignore
def check_url(self) -> "SocialNetwork":
    """Validate the URL of the social network."""
    if self.network == "Mastodon":
        # All the other social networks have valid URLs. Mastodon URLs contain both
        # the username and the domain. So, we need to validate if the url is valid.
        validate_url(self.url)

    return self

create_a_sample_data_model(name='John Doe', theme='classic')

Return a sample data model for new users to start with.

Parameters:

  • name (str, default: 'John Doe' ) –

    The name of the person. Defaults to "John Doe".

Returns:

Source code in rendercv/data/generator.py
def create_a_sample_data_model(
    name: str = "John Doe", theme: str = "classic"
) -> models.RenderCVDataModel:
    """Return a sample data model for new users to start with.

    Args:
        name: The name of the person. Defaults to "John Doe".

    Returns:
        A sample data model.
    """
    # Check if the theme is valid:
    if theme not in models.available_theme_options:
        available_themes_string = ", ".join(models.available_theme_options.keys())
        message = (
            f"The theme should be one of the following: {available_themes_string}!"
            f' The provided theme is "{theme}".'
        )
        raise ValueError(message)

    # read the sample_content.yaml file
    sample_content = pathlib.Path(__file__).parent / "sample_content.yaml"
    sample_content_dictionary = reader.read_a_yaml_file(sample_content)
    cv = models.CurriculumVitae(**sample_content_dictionary)

    # Update the name:
    name = name.encode().decode("unicode-escape")
    cv.name = name

    design = models.available_theme_options[theme](theme=theme)

    return models.RenderCVDataModel(cv=cv, design=design)

create_a_sample_yaml_input_file(input_file_path=None, name='John Doe', theme='classic')

Create a sample YAML input file and return it as a string. If the input file path is provided, then also save the contents to the file.

Parameters:

  • input_file_path (Optional[Path], default: None ) –

    The path to save the input file. Defaults to None.

  • name (str, default: 'John Doe' ) –

    The name of the person. Defaults to "John Doe".

  • theme (str, default: 'classic' ) –

    The theme of the CV. Defaults to "classic".

Returns:

  • str

    The sample YAML input file as a string.

Source code in rendercv/data/generator.py
def create_a_sample_yaml_input_file(
    input_file_path: Optional[pathlib.Path] = None,
    name: str = "John Doe",
    theme: str = "classic",
) -> str:
    """Create a sample YAML input file and return it as a string. If the input file path
    is provided, then also save the contents to the file.

    Args:
        input_file_path: The path to save the input file. Defaults to None.
        name: The name of the person. Defaults to "John Doe".
        theme: The theme of the CV. Defaults to "classic".

    Returns:
        The sample YAML input file as a string.
    """
    data_model = create_a_sample_data_model(name=name, theme=theme)

    # Instead of getting the dictionary with data_model.model_dump() directly, we
    # convert it to JSON and then to a dictionary. Because the YAML library we are
    # using sometimes has problems with the dictionary returned by model_dump().

    # We exclude "cv.sections" because the data model automatically generates them.
    # The user's "cv.sections" input is actually "cv.sections_input" in the data
    # model. It is shown as "cv.sections" in the YAML file because an alias is being
    # used. If"cv.sections" were not excluded, the automatically generated
    # "cv.sections" would overwrite the "cv.sections_input". "cv.sections" are
    # automatically generated from "cv.sections_input" to make the templating
    # process easier. "cv.sections_input" exists for the convenience of the user.
    data_model_as_json = data_model.model_dump_json(
        exclude_none=True, by_alias=True, exclude={"cv": {"sections"}}
    )
    data_model_as_dictionary = json.loads(data_model_as_json)

    yaml_string = dictionary_to_yaml(data_model_as_dictionary)

    if input_file_path is not None:
        input_file_path.write_text(yaml_string, encoding="utf-8")

    return yaml_string

generate_json_schema()

Generate the JSON schema of RenderCV.

JSON schema is generated for the users to make it easier for them to write the input file. The JSON Schema of RenderCV is saved in the root directory of the repository and distributed to the users with the JSON Schema Store.

Returns:

  • dict

    The JSON schema of RenderCV.

Source code in rendercv/data/generator.py
def generate_json_schema() -> dict:
    """Generate the JSON schema of RenderCV.

    JSON schema is generated for the users to make it easier for them to write the input
    file. The JSON Schema of RenderCV is saved in the root directory of the repository
    and distributed to the users with the
    [JSON Schema Store](https://www.schemastore.org/).

    Returns:
        The JSON schema of RenderCV.
    """

    class RenderCVSchemaGenerator(pydantic.json_schema.GenerateJsonSchema):
        def generate(self, schema, mode="validation"):  # type: ignore
            json_schema = super().generate(schema, mode=mode)

            # Basic information about the schema:
            json_schema["title"] = "RenderCV"
            json_schema["description"] = "RenderCV data model."
            json_schema["$id"] = (
                "https://raw.githubusercontent.com/rendercv/rendercv/main/schema.json"
            )
            json_schema["$schema"] = "http://json-schema.org/draft-07/schema#"

            # Loop through $defs and remove docstring descriptions and fix optional
            # fields
            for _, value in json_schema["$defs"].items():
                # If a type is optional, then Pydantic sets the type to a list of two
                # types, one of which is null. The null type can be removed since we
                # already have the required field. Moreover, we would like to warn
                # users if they provide null values. They can remove the fields if they
                # don't want to provide them.
                null_type_dict = {
                    "type": "null",
                }
                for _, field in value["properties"].items():
                    if "anyOf" in field:
                        if null_type_dict in field["anyOf"]:
                            field["anyOf"].remove(null_type_dict)

                        field["oneOf"] = field["anyOf"]
                        del field["anyOf"]

            # # Currently, YAML extension in VS Code doesn't work properly with the
            # # `ListOfEntries` objects. For the best user experience, we will update
            # # the JSON Schema. If YAML extension in VS Code starts to work properly,
            # # then we should remove the following code for the correct JSON Schema.
            # ListOfEntriesForJsonSchema = list[models.Entry]
            # list_of_entries_json_schema = pydantic.TypeAdapter(
            #     ListOfEntriesForJsonSchema
            # ).json_schema()
            # del list_of_entries_json_schema["$defs"]

            # json_schema["$defs"]["CurriculumVitae"]["properties"]["sections"]["oneOf"][
            #     0
            # ]["additionalProperties"] = list_of_entries_json_schema

            # # Loop through json_schema["$defs"] and update keys:
            # # Make all ANYTHING__KEY to KEY
            # for key in list(json_schema["$defs"]):
            #     new_key = key.split("__")[-1]
            #     json_schema["$defs"][new_key] = json_schema["$defs"][key]

            return json_schema

    return models.RenderCVDataModel.model_json_schema(
        schema_generator=RenderCVSchemaGenerator
    )

generate_json_schema_file(json_schema_path)

Generate the JSON schema of RenderCV and save it to a file.

Parameters:

  • json_schema_path (Path) –

    The path to save the JSON schema.

Source code in rendercv/data/generator.py
def generate_json_schema_file(json_schema_path: pathlib.Path):
    """Generate the JSON schema of RenderCV and save it to a file.

    Args:
        json_schema_path: The path to save the JSON schema.
    """
    schema = generate_json_schema()
    schema_json = json.dumps(schema, indent=2, ensure_ascii=False)
    json_schema_path.write_text(schema_json, encoding="utf-8")

format_date(date, date_template=None)

Formats a Date object to a string in the following format: "Jan 2021". The month names are taken from the locale dictionary from the rendercv.data_models.models module.

Example

format_date(Date(2024, 5, 1))
will return

"May 2024"

Parameters:

  • date (date) –

    The date to format.

  • date_template (Optional[str], default: None ) –

    The template of the date string. If not provided, the default date style from the locale dictionary will be used.

Returns:

  • str

    The formatted date.

Source code in rendercv/data/models/computers.py
def format_date(date: Date, date_template: Optional[str] = None) -> str:
    """Formats a `Date` object to a string in the following format: "Jan 2021". The
    month names are taken from the `locale` dictionary from the
    `rendercv.data_models.models` module.

    Example:
        ```python
        format_date(Date(2024, 5, 1))
        ```
        will return

        `"May 2024"`

    Args:
        date: The date to format.
        date_template: The template of the date string. If not provided, the default date
            style from the `locale` dictionary will be used.

    Returns:
        The formatted date.
    """
    full_month_names = locale["full_names_of_months"]
    short_month_names = locale["abbreviations_for_months"]

    month = int(date.strftime("%m"))
    year = date.strftime(format="%Y")

    placeholders = {
        "FULL_MONTH_NAME": full_month_names[month - 1],
        "MONTH_ABBREVIATION": short_month_names[month - 1],
        "MONTH_IN_TWO_DIGITS": f"{month:02d}",
        "YEAR_IN_TWO_DIGITS": str(year[-2:]),
        "MONTH": str(month),
        "YEAR": str(year),
    }
    if date_template is None:
        date_template = locale["date_template"]  # type: ignore

    assert isinstance(date_template, str)

    for placeholder, value in placeholders.items():
        date_template = date_template.replace(placeholder, value)  # type: ignore

    return date_template

get_date_input()

Return the date input.

Returns:

  • date

    The date input.

Source code in rendercv/data/models/computers.py
def get_date_input() -> Date:
    """Return the date input.

    Returns:
        The date input.
    """
    from .rendercv_settings import DATE_INPUT

    return DATE_INPUT

make_a_url_clean(url)

Make a URL clean by removing the protocol, www, and trailing slashes.

Example

make_a_url_clean("https://www.example.com/")
returns "example.com"

Parameters:

  • url (str) –

    The URL to make clean.

Returns:

  • str

    The clean URL.

Source code in rendercv/data/models/computers.py
def make_a_url_clean(url: str) -> str:
    """Make a URL clean by removing the protocol, www, and trailing slashes.

    Example:
        ```python
        make_a_url_clean("https://www.example.com/")
        ```
        returns
        `"example.com"`

    Args:
        url: The URL to make clean.

    Returns:
        The clean URL.
    """
    url = url.replace("https://", "").replace("http://", "")
    if url.endswith("/"):
        url = url[:-1]

    return url

make_keywords_bold_in_a_string(string, keywords)

Make the given keywords bold in the given string.

Source code in rendercv/data/models/entry_types.py
def make_keywords_bold_in_a_string(string: str, keywords: list[str]) -> str:
    """Make the given keywords bold in the given string."""
    replacement_map = {keyword: f"**{keyword}**" for keyword in keywords}
    for keyword, replacement in replacement_map.items():
        string = string.replace(keyword, replacement)

    return string

get_error_message_and_location_and_value_from_a_custom_error(error_string)

Look at a string and figure out if it's a custom error message that has been sent from rendercv.data.reader.read_input_file. If it is, then return the custom message, location, and the input value.

This is done because sometimes we raise an error about a specific field in the model validation level, but Pydantic doesn't give us the exact location of the error because it's a model-level error. So, we raise a custom error with three string arguments: message, location, and input value. Those arguments then combined into a string by Python. This function is used to parse that custom error message and return the three values.

Parameters:

  • error_string (str) –

    The error message.

Returns:

  • tuple[Optional[str], Optional[str], Optional[str]]

    The custom message, location, and the input value.

Source code in rendercv/data/reader.py
def get_error_message_and_location_and_value_from_a_custom_error(
    error_string: str,
) -> tuple[Optional[str], Optional[str], Optional[str]]:
    """Look at a string and figure out if it's a custom error message that has been
    sent from `rendercv.data.reader.read_input_file`. If it is, then return the custom
    message, location, and the input value.

    This is done because sometimes we raise an error about a specific field in the model
    validation level, but Pydantic doesn't give us the exact location of the error
    because it's a model-level error. So, we raise a custom error with three string
    arguments: message, location, and input value. Those arguments then combined into a
    string by Python. This function is used to parse that custom error message and
    return the three values.

    Args:
        error_string: The error message.

    Returns:
        The custom message, location, and the input value.
    """
    pattern = r"""\(['"](.*)['"], '(.*)', '(.*)'\)"""
    match = re.search(pattern, error_string)
    if match:
        return match.group(1), match.group(2), match.group(3)
    return None, None, None

parse_validation_errors(exception)

Take a Pydantic validation error, parse it, and return a list of error dictionaries that contain the error messages, locations, and the input values.

Pydantic's ValidationError object is a complex object that contains a lot of information about the error. This function takes a ValidationError object and extracts the error messages, locations, and the input values.

Parameters:

  • exception (ValidationError) –

    The Pydantic validation error object.

Returns:

  • list[dict[str, str]]

    A list of error dictionaries that contain the error messages, locations, and the

  • list[dict[str, str]]

    input values.

Source code in rendercv/data/reader.py
def parse_validation_errors(
    exception: pydantic.ValidationError,
) -> list[dict[str, str]]:
    """Take a Pydantic validation error, parse it, and return a list of error
    dictionaries that contain the error messages, locations, and the input values.

    Pydantic's `ValidationError` object is a complex object that contains a lot of
    information about the error. This function takes a `ValidationError` object and
    extracts the error messages, locations, and the input values.

    Args:
        exception: The Pydantic validation error object.

    Returns:
        A list of error dictionaries that contain the error messages, locations, and the
        input values.
    """
    # This dictionary is used to convert the error messages that Pydantic returns to
    # more user-friendly messages.
    error_dictionary: dict[str, str] = {
        "Input should be 'present'": (
            "This is not a valid date! Please use either YYYY-MM-DD, YYYY-MM, or YYYY"
            ' format or "present"!'
        ),
        "Input should be a valid integer, unable to parse string as an integer": (
            "This is not a valid date! Please use either YYYY-MM-DD, YYYY-MM, or YYYY"
            " format!"
        ),
        "String should match pattern '\\d{4}-\\d{2}(-\\d{2})?'": (
            "This is not a valid date! Please use either YYYY-MM-DD, YYYY-MM, or YYYY"
            " format!"
        ),
        "String should match pattern '\\b10\\..*'": (
            'A DOI prefix should always start with "10.". For example,'
            ' "10.1109/TASC.2023.3340648".'
        ),
        "URL scheme should be 'http' or 'https'": "This is not a valid URL!",
        "Field required": "This field is required!",
        "value is not a valid phone number": "This is not a valid phone number!",
        "month must be in 1..12": "The month must be between 1 and 12!",
        "day is out of range for month": "The day is out of range for the month!",
        "Extra inputs are not permitted": (
            "This field is unknown for this object! Please remove it."
        ),
        "Input should be a valid string": "This field should be a string!",
        "Input should be a valid list": (
            "This field should contain a list of items but it doesn't!"
        ),
        "value is not a valid color: string not recognised as a valid color": (
            "This is not a valid color! Here are some examples of valid colors:"
            ' "red", "#ff0000", "rgb(255, 0, 0)", "hsl(0, 100%, 50%)"'
        ),
    }

    unwanted_texts = ["value is not a valid email address: ", "Value error, "]

    # Check if this is a section error. If it is, we need to handle it differently.
    # This is needed because how dm.validate_section_input function raises an exception.
    # This is done to tell the user which which EntryType RenderCV excepts to see.
    errors = exception.errors()
    for error_object in errors.copy():
        if (
            "There are problems with the entries." in error_object["msg"]
            and "ctx" in error_object
        ):
            location = error_object["loc"]
            ctx_object = error_object["ctx"]
            if "error" in ctx_object:
                inner_error_object = ctx_object["error"]
                if hasattr(inner_error_object, "__cause__"):
                    cause_object = inner_error_object.__cause__
                    cause_object_errors = cause_object.errors()
                    for cause_error_object in cause_object_errors:
                        # we use [1:] to avoid `entries` location. It is a location for
                        # RenderCV's own data model, not the user's data model.
                        cause_error_object["loc"] = tuple(
                            list(location) + list(cause_error_object["loc"][1:])
                        )
                    errors.extend(cause_object_errors)

    # some locations are not really the locations in the input file, but some
    # information about the model coming from Pydantic. We need to remove them.
    # (e.g. avoid stuff like .end_date.literal['present'])
    unwanted_locations = ["tagged-union", "list", "literal", "int", "constrained-str"]
    for error_object in errors:
        location = [str(location_element) for location_element in error_object["loc"]]
        new_location = [str(location_element) for location_element in location]
        for location_element in location:
            for unwanted_location in unwanted_locations:
                if unwanted_location in location_element:
                    new_location.remove(location_element)
        error_object["loc"] = new_location  # type: ignore

    # Parse all the errors and create a new list of errors.
    new_errors: list[dict[str, str]] = []
    for error_object in errors:
        message = error_object["msg"]
        location = ".".join(error_object["loc"])  # type: ignore
        input = error_object["input"]

        # Check if this is a custom error message:
        custom_message, custom_location, custom_input_value = (
            get_error_message_and_location_and_value_from_a_custom_error(message)
        )
        if custom_message is not None:
            message = custom_message
            if custom_location:
                # If the custom location is not empty, then add it to the location.
                location = f"{location}.{custom_location}"
            input = custom_input_value

        # Don't show unwanted texts in the error message:
        for unwanted_text in unwanted_texts:
            message = message.replace(unwanted_text, "")

        # Convert the error message to a more user-friendly message if it's in the
        # error_dictionary:
        if message in error_dictionary:
            message = error_dictionary[message]

        # Special case for end_date because Pydantic returns multiple end_date errors
        # since it has multiple valid formats:
        if "end_date" in location:
            message = (
                "This is not a valid end date! Please use either YYYY-MM-DD, YYYY-MM,"
                ' or YYYY format or "present"!'
            )

        # If the input is a dictionary or a list (the model itself fails to validate),
        # then don't show the input. It looks confusing and it is not helpful.
        if isinstance(input, dict | list):
            input = ""

        new_error = {
            "loc": tuple(location.split(".")),
            "msg": message,
            "input": str(input),
        }

        # if new_error is not in new_errors, then add it to new_errors
        if new_error not in new_errors:
            new_errors.append(new_error)

    return new_errors

read_a_yaml_file(file_path_or_contents)

Read a YAML file and return its content as a dictionary. The YAML file can be given as a path to the file or as the contents of the file as a string.

Parameters:

  • file_path_or_contents (Path | str) –

    The path to the YAML file or the contents of the YAML file as a string.

Returns:

  • dict

    The content of the YAML file as a dictionary.

Source code in rendercv/data/reader.py
def read_a_yaml_file(file_path_or_contents: pathlib.Path | str) -> dict:
    """Read a YAML file and return its content as a dictionary. The YAML file can be
    given as a path to the file or as the contents of the file as a string.

    Args:
        file_path_or_contents: The path to the YAML file or the contents of the YAML
            file as a string.

    Returns:
        The content of the YAML file as a dictionary.
    """
    try:
        import ruamel.yaml
    except Exception as e:
        from .. import _parial_install_error_message

        raise ImportError(_parial_install_error_message) from e

    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 FileNotFoundError(message)

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

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

    yaml_as_a_dictionary: dict = ruamel.yaml.YAML().load(file_content)

    if yaml_as_a_dictionary is None:
        message = "The input file is empty!"
        raise ValueError(message)

    return yaml_as_a_dictionary

read_input_file(file_path_or_contents)

Read the input file (YAML or JSON) and return them as an instance of RenderCVDataModel, which is a Pydantic data model of RenderCV's data format.

Parameters:

  • file_path_or_contents (Path | str) –

    The path to the input file or the contents of the input file as a string.

Returns:

Source code in rendercv/data/reader.py
def read_input_file(
    file_path_or_contents: pathlib.Path | str,
) -> models.RenderCVDataModel:
    """Read the input file (YAML or JSON) and return them as an instance of
    `RenderCVDataModel`, which is a Pydantic data model of RenderCV's data format.

    Args:
        file_path_or_contents: The path to the input file or the contents of the input
            file as a string.

    Returns:
        The data model.
    """
    input_as_dictionary = read_a_yaml_file(file_path_or_contents)

    return validate_input_dictionary_and_return_the_data_model(input_as_dictionary)

validate_input_dictionary_and_return_the_data_model(input_dictionary, context=None)

Validate the input dictionary by creating an instance of RenderCVDataModel, which is a Pydantic data model of RenderCV's data format.

Parameters:

  • input_dictionary (dict) –

    The input dictionary.

  • context (Optional[dict], default: None ) –

    The context dictionary that is used to validate the input dictionary. It's used to send the input file path with the context object, but it's not required.

Returns:

Source code in rendercv/data/reader.py
def validate_input_dictionary_and_return_the_data_model(
    input_dictionary: dict,
    context: Optional[dict] = None,
) -> models.RenderCVDataModel:
    """Validate the input dictionary by creating an instance of `RenderCVDataModel`,
    which is a Pydantic data model of RenderCV's data format.

    Args:
        input_dictionary: The input dictionary.
        context: The context dictionary that is used to validate the input dictionary.
            It's used to send the input file path with the context object, but it's not
            required.

    Returns:
        The data model.
    """
    # Validate the parsed dictionary by creating an instance of RenderCVDataModel:
    data_model = models.RenderCVDataModel.model_validate(
        input_dictionary, context=context
    )

    # If the `bold_keywords` field is provided in the `rendercv_settings`, make the
    # given keywords bold in the `cv.sections` field:
    if data_model.rendercv_settings and data_model.rendercv_settings.bold_keywords:
        data_model.cv.sections_input = make_given_keywords_bold_in_sections(
            data_model.cv.sections_input,
            data_model.rendercv_settings.bold_keywords,
        )

    return data_model