Skip to content

templater

templates_directory = pathlib.Path(__file__).parent / 'templates' module-attribute

get_jinja2_environment(input_file_path=None) cached

Create cached Jinja2 environment with custom filters and template loaders.

Why

Template rendering is called multiple times per render. Caching environment prevents repeated filesystem scans. Loader hierarchy enables user template overrides by checking input file directory before built-in templates.

Parameters:

  • input_file_path (Path | None, default: None ) –

    Path to input file for user template override resolution.

Returns:

  • Environment

    Configured Jinja2 environment with filters and loaders.

Source code in src/rendercv/renderer/templater/templater.py
@functools.lru_cache(maxsize=1)
def get_jinja2_environment(
    input_file_path: pathlib.Path | None = None,
) -> jinja2.Environment:
    """Create cached Jinja2 environment with custom filters and template loaders.

    Why:
        Template rendering is called multiple times per render. Caching environment
        prevents repeated filesystem scans. Loader hierarchy enables user template
        overrides by checking input file directory before built-in templates.

    Args:
        input_file_path: Path to input file for user template override resolution.

    Returns:
        Configured Jinja2 environment with filters and loaders.
    """
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(
            [
                (  # To allow users to override the templates:
                    input_file_path.parent if input_file_path else pathlib.Path.cwd()
                ),
                templates_directory,
            ]
        ),
        trim_blocks=True,
        lstrip_blocks=True,
    )
    env.filters["clean_url"] = clean_url
    env.filters["strip"] = lambda string: string.strip()
    return env

render_full_template(rendercv_model, file_type)

Render complete CV document by assembling preamble, header, and sections.

Why

CV generation requires consistent structure across formats. This orchestrates model processing, template rendering for each component, and assembly into final document following proper order.

Example
typst_document = render_full_template(rendercv_model, "typst")
# Returns complete .typ file with preamble, header, and all sections

markdown_document = render_full_template(rendercv_model, "markdown")
# Returns complete .md file with header and all sections

Parameters:

  • rendercv_model (RenderCVModel) –

    CV model to render.

  • file_type (Literal['typst', 'markdown']) –

    Output format for template selection and processing.

Returns:

  • str

    Complete rendered document as string.

Source code in src/rendercv/renderer/templater/templater.py
def render_full_template(
    rendercv_model: RenderCVModel, file_type: Literal["typst", "markdown"]
) -> str:
    """Render complete CV document by assembling preamble, header, and sections.

    Why:
        CV generation requires consistent structure across formats. This orchestrates
        model processing, template rendering for each component, and assembly into
        final document following proper order.

    Example:
        ```py
        typst_document = render_full_template(rendercv_model, "typst")
        # Returns complete .typ file with preamble, header, and all sections

        markdown_document = render_full_template(rendercv_model, "markdown")
        # Returns complete .md file with header and all sections
        ```

    Args:
        rendercv_model: CV model to render.
        file_type: Output format for template selection and processing.

    Returns:
        Complete rendered document as string.
    """
    extension = {
        "typst": "typ",
        "markdown": "md",
    }[file_type]

    rendercv_model = process_model(rendercv_model, file_type)

    header = render_single_template(
        file_type,
        f"Header.j2.{extension}",
        rendercv_model,
    )
    if file_type == "typst":
        preamble = render_single_template(
            file_type,
            f"Preamble.j2.{extension}",
            rendercv_model,
        )
        code = f"{preamble}\n\n{header}\n"
    else:
        code = f"{header}\n"

    for rendercv_section in rendercv_model.cv.rendercv_sections:
        section_beginning = render_single_template(
            file_type,
            f"SectionBeginning.j2.{extension}",
            rendercv_model,
            section_title=rendercv_section.title,
            snake_case_section_title=rendercv_section.snake_case_title,
            entry_type=rendercv_section.entry_type,
        )
        section_ending = render_single_template(
            file_type,
            f"SectionEnding.j2.{extension}",
            rendercv_model,
            entry_type=rendercv_section.entry_type,
        )
        entry_codes = []
        for entry in rendercv_section.entries:
            entry_code = render_single_template(
                file_type,
                f"entries/{rendercv_section.entry_type}.j2.{extension}",
                rendercv_model,
                entry=entry,
            )
            entry_codes.append(entry_code)
        entries_code = "\n\n".join(entry_codes)
        section_code = f"{section_beginning}\n{entries_code}\n{section_ending}"
        code += f"\n{section_code}"

    return code

render_html(rendercv_model, markdown)

Convert Markdown to HTML and wrap with full HTML template.

Why

HTML output requires both content conversion (Markdown to HTML body) and document structure (head, CSS, metadata). Separate function handles HTML- specific workflow distinct from Typst/Markdown direct generation.

Example
markdown_content = render_full_template(rendercv_model, "markdown")
html_document = render_html(rendercv_model, markdown_content)
# Returns complete HTML with <head>, CSS, and converted Markdown body

Parameters:

  • rendercv_model (RenderCVModel) –

    CV model for template context.

  • markdown (str) –

    Markdown content to convert.

Returns:

  • str

    Complete HTML document.

Source code in src/rendercv/renderer/templater/templater.py
def render_html(rendercv_model: RenderCVModel, markdown: str) -> str:
    """Convert Markdown to HTML and wrap with full HTML template.

    Why:
        HTML output requires both content conversion (Markdown to HTML body) and
        document structure (head, CSS, metadata). Separate function handles HTML-
        specific workflow distinct from Typst/Markdown direct generation.

    Example:
        ```py
        markdown_content = render_full_template(rendercv_model, "markdown")
        html_document = render_html(rendercv_model, markdown_content)
        # Returns complete HTML with <head>, CSS, and converted Markdown body
        ```

    Args:
        rendercv_model: CV model for template context.
        markdown: Markdown content to convert.

    Returns:
        Complete HTML document.
    """
    html_body = markdown_to_html(markdown)
    return render_single_template(
        "html", "Full.html", rendercv_model, html_body=html_body
    )

render_single_template(file_type, relative_template_path, rendercv_model, **kwargs)

Render single Jinja2 template with user override support. Arbitrary keyword arguments may be passed to the template as additional template variables.

Why

Users can override built-in templates by placing custom templates in theme folder alongside input file. Typst templates check theme-specific location first, falling back to built-in templates if not found.

Example
header = render_single_template("typst", "Header.j2.typ", rendercv_model)
# First checks for classic/Header.j2.typ in input file directory
# Falls back to built-in typst/Header.j2.typ if not found

section = render_single_template(
    "typst",
    "SectionBeginning.j2.typ",
    rendercv_model,
    section_title="Experience",
)

Parameters:

  • file_type (Literal['markdown', 'typst', 'html']) –

    Format for template directory selection.

  • relative_template_path (str) –

    Template file path relative to format directory.

  • rendercv_model (RenderCVModel) –

    CV model providing template context.

Returns:

  • str

    Rendered template as string.

Source code in src/rendercv/renderer/templater/templater.py
def render_single_template(
    file_type: Literal["markdown", "typst", "html"],
    relative_template_path: str,
    rendercv_model: RenderCVModel,
    **kwargs,
) -> str:
    """Render single Jinja2 template with user override support. Arbitrary keyword
    arguments may be passed to the template as additional template variables.

    Why:
        Users can override built-in templates by placing custom templates in
        theme folder alongside input file. Typst templates check theme-specific
        location first, falling back to built-in templates if not found.

    Example:
        ```py
        header = render_single_template("typst", "Header.j2.typ", rendercv_model)
        # First checks for classic/Header.j2.typ in input file directory
        # Falls back to built-in typst/Header.j2.typ if not found

        section = render_single_template(
            "typst",
            "SectionBeginning.j2.typ",
            rendercv_model,
            section_title="Experience",
        )
        ```

    Args:
        file_type: Format for template directory selection.
        relative_template_path: Template file path relative to format directory.
        rendercv_model: CV model providing template context.

    Returns:
        Rendered template as string.
    """
    jinja2_environment = get_jinja2_environment(rendercv_model._input_file_path)
    template = None
    if file_type == "typst":
        # Try user's own Typst templates first:
        with contextlib.suppress(jinja2.TemplateNotFound):
            template = jinja2_environment.get_template(
                f"{rendercv_model.design.theme}/{relative_template_path}"
            )

    if template is None:
        template = jinja2_environment.get_template(
            f"{file_type}/{relative_template_path}"
        )

    return template.render(
        cv=rendercv_model.cv,
        design=rendercv_model.design,
        locale=rendercv_model.locale,
        settings=rendercv_model.settings,
        **kwargs,
    )