Skip to content

Templating

Air loves Jinja!

A common pattern is to use a Jinja template as the project base and then use Air Tags for individual content.

JinjaRenderer

JinjaRenderer(directory, context_processors=None, env=None)

Template renderer to make Jinja easier in Air.

Parameters:

Name Type Description Default
directory str | PathLike[str] | Sequence[str | PathLike[str]]

The template directory where Jinja templates for the project are stored.

required
context_processors list[Callable[[Request], dict[str, Any]]] | None

A list of Jinja-style context processors, functions that automatically injects variables or functions into the template context so they're available in every rendered template without passing them explicitly.

None
env Environment | None

The env is the central Jinja object that holds configuration, filters, globals, and template loading settings, and is responsible for compiling and rendering templates.

None

Example:

# Instantiate the render callable
jinja = JinjaRenderer('templates')

# Use for returning Jinja from views
@app.get('/')
async def home(request: Request):
    return jinja(
        request,
        'home.html',
        context={'id': 5}
     )

    # Can also pass in kwargs, which will be added to the context:
    return jinja(
        request,
        'home.html',
        name='Parmesan'
    )

    # Will render Air Tags sent into Jinja context
    return jinja(
        request,
        'home.html',
        content=air.Article(air.P('Cheddar'))
    )
Source code in src/air/templating.py
79
80
81
82
83
84
85
86
def __init__(
    self,
    directory: str | PathLike[str] | Sequence[str | PathLike[str]],
    context_processors: list[Callable[[StarletteRequest], dict[str, Any]]] | None = None,
    env: jinja2.Environment | None = None,
) -> None:
    """Initialize with template directory path"""
    self.templates = Jinja2Templates(directory=directory, context_processors=context_processors, env=env)

__call__

__call__(request, name, context=None, **kwargs)

Render template with request and context. If an Air Tag is found in the context, try to render it.

Returns:

Type Description
_TemplateResponse

A TemplateResponse with the rendered template.

Source code in src/air/templating.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def __call__(
    self,
    request: Request,
    name: str,
    context: dict[Any, Any] | None = None,
    **kwargs: Any,
) -> _TemplateResponse:
    """Render template with request and context. If an Air Tag
    is found in the context, try to render it.

    Returns:
        A TemplateResponse with the rendered template.
    """
    if context is None:
        context = {}
    if kwargs:
        context |= kwargs

    # Attempt to render any Tags in the context
    context = {k: _jinja_context_item(v) for k, v in context.items()}
    return self.templates.TemplateResponse(request=request, name=name, context=context)

Renderer

Renderer(
    directory,
    context_processors=None,
    env=None,
    package=None,
)

Template/Tag renderer to make composing pluggable functions easier.

Parameters:

Name Type Description Default
directory str | PathLike[str] | Sequence[str | PathLike[str]]

The template directory where Jinja templates for the project are stored.

required
context_processors list[Callable[[Request], dict[str, Any]]] | None

A list of Jinja-style context processors, functions that automatically injects variables or functions into the template context so they're available in every rendered template without passing them explicitly.

None
env Environment | None

The env is the central Jinja object that holds configuration, filters, globals, and template loading settings, and is responsible for compiling and rendering templates.

None

Example:

import air

app = air.Air()

# Instantiate the render callable
render = air.Renderer('templates')

# Use for returning Jinja from views
@app.get('/')
async def home(request: Request):
    return render(
        name='home.html',
        request=request,
        context={'id': 5}
     )


    # Will render name of Air Tags
    return render(
        request,
        'components.home',
        context={'id': 5}
    )


    # Will render callables to HTML
    return render(
        air.layouts.mvpcss,
        air.Title("Test Page"),
        air.H1("Hello, World")
    )
Source code in src/air/templating.py
158
159
160
161
162
163
164
165
166
167
def __init__(
    self,
    directory: str | PathLike[str] | Sequence[str | PathLike[str]],
    context_processors: list[Callable[[StarletteRequest], dict[str, Any]]] | None = None,
    env: jinja2.Environment | None = None,
    package: str | None = None,
) -> None:
    """Initialize with template directory path"""
    self.templates = Jinja2Templates(directory=directory, context_processors=context_processors, env=env)
    self.package = package

__call__

__call__(
    name, *children, request=None, context=None, **kwargs
)

Render template with request and context. If an Air Tag is found in the context, try to render it.

Returns:

Type Description
str | _TemplateResponse

Rendered string or TemplateResponse depending on the template type.

Raises:

Type Description
TypeError

If callable result is neither a string nor has a render method.

ValueError

If no callable or Jinja template is found.

Source code in src/air/templating.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def __call__(
    self,
    name: str | Callable,
    *children: Any,
    request: Request | None = None,
    context: dict[Any, Any] | None = None,
    **kwargs: Any,
) -> str | _TemplateResponse:
    """Render template with request and context. If an Air Tag
    is found in the context, try to render it.

    Returns:
        Rendered string or TemplateResponse depending on the template type.

    Raises:
        TypeError: If callable result is neither a string nor has a render method.
        ValueError: If no callable or Jinja template is found.
    """
    context = self._prepare_context(context, kwargs)

    if callable(name):
        assert not isinstance(name, str)
        result = name(**context)
        if isinstance(result, str):
            return result
        if hasattr(result, "render"):
            return result.render()
        msg = "'name' must be a string or a callable(returning a string, or an object with a render() method."
        raise TypeError(msg)

    if not isinstance(name, str):
        msg = "'name' must be a string or a callable(returning a string, or an object with a render() method."
        raise TypeError(msg)

    if name.endswith((".html", ".jinja")):
        return self._render_template(name, request, context)

    if "." in name:
        return self._render_tag_callable(name, children, request, context)

    msg = "No callable or Jinja template found."
    raise ValueError(msg)