Skip to content

htmy.jinja

JinjaContextFactory = Callable[[Context], Mapping[str, Any]] module-attribute

A function that builds additional Jinja context entries from an htmy rendering context.

DefaultSlots

Bases: ContextAware

Context-aware holder of default named slots for JinjaTemplate.

The JinjaTemplate component looks up the DefaultSlots instance from the htmy rendering context. If found, JinjaTemplate renders each slot and passes the rendered values as markupsafe.Markup strings to the Jinja template.

Source code in htmy/jinja.py
class DefaultSlots(ContextAware):
    """
    Context-aware holder of default named slots for `JinjaTemplate`.

    The `JinjaTemplate` component looks up the `DefaultSlots` instance from the `htmy` rendering
    context. If found, `JinjaTemplate` renders each slot and passes the rendered values as
    `markupsafe.Markup` strings to the Jinja template.
    """

    __slots__ = ("_slots",)

    def __init__(self, slots: Mapping[str, Component] | None = None) -> None:
        """
        Initialization.

        Arguments:
            slots: Optional mapping of slot names to `htmy` components.
        """
        self._slots = {} if slots is None else slots

    @property
    def slots(self) -> Mapping[str, Component]:
        """Mapping of slot names to `htmy` components."""
        return self._slots

slots property

Mapping of slot names to htmy components.

__init__(slots=None)

Initialization.

Parameters:

Name Type Description Default
slots Mapping[str, Component] | None

Optional mapping of slot names to htmy components.

None
Source code in htmy/jinja.py
def __init__(self, slots: Mapping[str, Component] | None = None) -> None:
    """
    Initialization.

    Arguments:
        slots: Optional mapping of slot names to `htmy` components.
    """
    self._slots = {} if slots is None else slots

JinjaTemplate

Component that renders a Jinja2 template.

The template source (a JinjaTemplates instance) must be in the htmy rendering context. The looked up template source is used to load and render the template.

If a DefaultSlots instance is found in the htmy rendering context, its slots are rendered and included in the slots context variable passed to Jinja. slots passed directly to this component take precedence over DefaultSlots slots when both contain a slot with the same name.

Jinja context creation precedence: jinja_context, _build_context(), make_context, and finally the reserved slots key.

Source code in htmy/jinja.py
class JinjaTemplate:
    """
    Component that renders a Jinja2 template.

    The template source (a `JinjaTemplates` instance) must be in the `htmy` rendering context.
    The looked up template source is used to load and render the template.

    If a `DefaultSlots` instance is found in the `htmy` rendering context, its slots are rendered
    and included in the `slots` context variable passed to Jinja. `slots` passed directly to this
    component take precedence over `DefaultSlots` slots when both contain a slot with the same name.

    Jinja context creation precedence: `jinja_context`, `_build_context()`, `make_context`, and
    finally the reserved `slots` key.
    """

    __slots__ = ("_jinja_context", "_make_context", "_slots", "_template_name")

    def __init__(
        self,
        template_name: str,
        *,
        jinja_context: Mapping[str, Any] | None = None,
        make_context: JinjaContextFactory | None = None,
        slots: Mapping[str, Component] | None = None,
    ) -> None:
        """
        Initialization.

        Arguments:
            template_name: The name of the template to render.
            jinja_context: Optional mapping passed to Jinja as the template context.
            make_context: Optional callable that receives the `htmy` rendering context and
                returns additional Jinja context entries. It runs after `_build_context()`,
                but before slot rendering, so it can not override the reserved `slots` key.
            slots: Optional named slots to be rendered in the template. Values are `htmy` components,
                they are rendered before being passed to Jinja as `markupsafe.Markup` to avoid
                double-escaping, content is not re-escaped by Jinja's autoescape. Slots are available
                through the `slots` key in the Jinja context and accessed as `{{ slots.<name> }}`.
        """
        self._template_name = template_name
        self._jinja_context = jinja_context
        self._make_context = make_context
        self._slots = slots

    def _build_context(self, htmy_context: Context, /) -> dict[str, Any]:
        """
        Returns the Jinja rendering context for this component.

        The returned dictionary is owned by the caller, so it can be modified in place.

        Arguments:
            htmy_context: The `htmy` rendering context.
        """
        return {} if self._jinja_context is None else dict(self._jinja_context)

    async def htmy(self, context: Context) -> Component:
        """Renders the component."""
        templates = JinjaTemplates.from_context(context)
        renderer = RendererContext.from_context(context)
        default_slots = DefaultSlots.from_context(context, None)

        template = templates.get_template(self._template_name)
        slots: dict[str, Markup] = {}

        jinja_context = self._build_context(context)
        if self._make_context is not None:
            jinja_context.update(self._make_context(context))

        if default_slots is not None:
            for name, component in default_slots.slots.items():
                slots[name] = Markup(await renderer.render(component, context))  # noqa: S704

        if self._slots is not None:
            for name, component in self._slots.items():
                slots[name] = Markup(await renderer.render(component, context))  # noqa: S704

        jinja_context["slots"] = slots

        if template.environment.is_async:
            rendered = await template.render_async(**jinja_context)
        else:
            rendered = await anyio.to_thread.run_sync(lambda: template.render(**jinja_context))

        return SafeStr(rendered)

__init__(template_name, *, jinja_context=None, make_context=None, slots=None)

Initialization.

Parameters:

Name Type Description Default
template_name str

The name of the template to render.

required
jinja_context Mapping[str, Any] | None

Optional mapping passed to Jinja as the template context.

None
make_context JinjaContextFactory | None

Optional callable that receives the htmy rendering context and returns additional Jinja context entries. It runs after _build_context(), but before slot rendering, so it can not override the reserved slots key.

None
slots Mapping[str, Component] | None

Optional named slots to be rendered in the template. Values are htmy components, they are rendered before being passed to Jinja as markupsafe.Markup to avoid double-escaping, content is not re-escaped by Jinja's autoescape. Slots are available through the slots key in the Jinja context and accessed as {{ slots.<name> }}.

None
Source code in htmy/jinja.py
def __init__(
    self,
    template_name: str,
    *,
    jinja_context: Mapping[str, Any] | None = None,
    make_context: JinjaContextFactory | None = None,
    slots: Mapping[str, Component] | None = None,
) -> None:
    """
    Initialization.

    Arguments:
        template_name: The name of the template to render.
        jinja_context: Optional mapping passed to Jinja as the template context.
        make_context: Optional callable that receives the `htmy` rendering context and
            returns additional Jinja context entries. It runs after `_build_context()`,
            but before slot rendering, so it can not override the reserved `slots` key.
        slots: Optional named slots to be rendered in the template. Values are `htmy` components,
            they are rendered before being passed to Jinja as `markupsafe.Markup` to avoid
            double-escaping, content is not re-escaped by Jinja's autoescape. Slots are available
            through the `slots` key in the Jinja context and accessed as `{{ slots.<name> }}`.
    """
    self._template_name = template_name
    self._jinja_context = jinja_context
    self._make_context = make_context
    self._slots = slots

htmy(context) async

Renders the component.

Source code in htmy/jinja.py
async def htmy(self, context: Context) -> Component:
    """Renders the component."""
    templates = JinjaTemplates.from_context(context)
    renderer = RendererContext.from_context(context)
    default_slots = DefaultSlots.from_context(context, None)

    template = templates.get_template(self._template_name)
    slots: dict[str, Markup] = {}

    jinja_context = self._build_context(context)
    if self._make_context is not None:
        jinja_context.update(self._make_context(context))

    if default_slots is not None:
        for name, component in default_slots.slots.items():
            slots[name] = Markup(await renderer.render(component, context))  # noqa: S704

    if self._slots is not None:
        for name, component in self._slots.items():
            slots[name] = Markup(await renderer.render(component, context))  # noqa: S704

    jinja_context["slots"] = slots

    if template.environment.is_async:
        rendered = await template.render_async(**jinja_context)
    else:
        rendered = await anyio.to_thread.run_sync(lambda: template.render(**jinja_context))

    return SafeStr(rendered)

JinjaTemplateSource

Bases: Protocol

Protocol for Jinja2 template sources such as jinja2.Environment.

Source code in htmy/jinja.py
class JinjaTemplateSource(Protocol):
    """Protocol for Jinja2 template sources such as `jinja2.Environment`."""

    def get_template(self, name: str) -> Template:
        """Returns the Jinja2 template with the given name."""
        ...

get_template(name)

Returns the Jinja2 template with the given name.

Source code in htmy/jinja.py
def get_template(self, name: str) -> Template:
    """Returns the Jinja2 template with the given name."""
    ...

JinjaTemplates

Bases: ContextAware

Context-aware Jinja2 template source that can be placed in the htmy rendering context.

The JinjaTemplate component looks up the JinjaTemplates instance from the htmy rendering context to find and render templates.

Source code in htmy/jinja.py
class JinjaTemplates(ContextAware):
    """
    Context-aware Jinja2 template source that can be placed in the `htmy` rendering context.

    The `JinjaTemplate` component looks up the `JinjaTemplates` instance from the `htmy` rendering
    context to find and render templates.
    """

    __slots__ = ("_source",)

    def __init__(self, source: JinjaTemplateSource) -> None:
        """
        Initialization.

        Arguments:
            source: A Jinja2 template source, e.g. a `jinja2.Environment` instance.
        """
        self._source = source

    @property
    def source(self) -> JinjaTemplateSource:
        """The wrapped Jinja2 template source."""
        return self._source

    def get_template(self, name: str) -> Template:
        """Returns the Jinja2 template with the given name."""
        return self._source.get_template(name)

source property

The wrapped Jinja2 template source.

__init__(source)

Initialization.

Parameters:

Name Type Description Default
source JinjaTemplateSource

A Jinja2 template source, e.g. a jinja2.Environment instance.

required
Source code in htmy/jinja.py
def __init__(self, source: JinjaTemplateSource) -> None:
    """
    Initialization.

    Arguments:
        source: A Jinja2 template source, e.g. a `jinja2.Environment` instance.
    """
    self._source = source

get_template(name)

Returns the Jinja2 template with the given name.

Source code in htmy/jinja.py
def get_template(self, name: str) -> Template:
    """Returns the Jinja2 template with the given name."""
    return self._source.get_template(name)