Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
(PY_CLASS, "abc.Collection"),
(PY_CLASS, "advanced_alchemy.utils.dataclass.Empty"),
(PY_CLASS, "jinja2.environment.Environment"),
(PY_CLASS, "minijinja.Environment"),
(PY_CLASS, "pydantic.BaseModel"),
(PY_CLASS, "pydantic.generics.GenericModel"),
(PY_CLASS, "pydantic.main.BaseModel"),
Expand Down Expand Up @@ -189,6 +190,8 @@
(PY_CLASS, "P.kwargs"),
(PY_CLASS, "litestar.contrib.jinja.P"),
(PY_CLASS, "litestar.contrib.mako.P"),
(PY_CLASS, "MiniJinjaTemplate"),
(PY_CLASS, "litestar.plugins.minijinja.MiniJinjaTemplate"),
(PY_CLASS, "JWTDecodeOptions"),
(PY_CLASS, "litestar.template.base.P"),
(PY_CLASS, "typing.Self"),
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/plugins/flash_messages/minijinja.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from litestar import Litestar
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.template.config import TemplateConfig

template_config = TemplateConfig(engine=MiniJinjaTemplateEngine, directory="templates")
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/templating/engine_instance_minijinja.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.template.config import TemplateConfig

template_config = TemplateConfig(engine=MiniJinjaTemplateEngine, directory="templates")
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pathlib import Path

from litestar import Litestar, get
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response import Template
from litestar.template.config import TemplateConfig

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/templating/template_engine_minijinja.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path

from litestar import Litestar
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.template.config import TemplateConfig

app = Litestar(
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/templating/template_functions_minijinja.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path

from litestar import Litestar, get
from litestar.contrib.minijinja import MiniJinjaTemplateEngine, StateProtocol
from litestar.plugins.minijinja import MiniJinjaTemplateEngine, StateProtocol
from litestar.response import Template
from litestar.template.config import TemplateConfig

Expand Down
1 change: 1 addition & 0 deletions docs/reference/plugins/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ plugins
attrs
flash_messages
htmx
minijinja
opentelemetry
problem_details
prometheus
Expand Down
6 changes: 6 additions & 0 deletions docs/reference/plugins/minijinja.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
=========
minijinja
=========

.. automodule:: litestar.plugins.minijinja
:members:
2 changes: 1 addition & 1 deletion litestar/plugins/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def on_app_init(self, app_config: AppConfig) -> AppConfig:
raise litestar.exceptions.ImproperlyConfiguredException("Flash messages require a session middleware.")
template_callable: Callable[[Any], Any] = get_flashes
with suppress(MissingDependencyException):
from litestar.contrib.minijinja import MiniJinjaTemplateEngine, _transform_state
from litestar.plugins.minijinja import MiniJinjaTemplateEngine, _transform_state

if isinstance(self.config.template_config.engine_instance, MiniJinjaTemplateEngine):
template_callable = _transform_state(get_flashes)
Expand Down
24 changes: 15 additions & 9 deletions litestar/contrib/minijinja.py → litestar/plugins/minijinja.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations
"""MiniJinja template engine integration for Litestar."""

import functools
from pathlib import Path
Expand Down Expand Up @@ -41,15 +41,17 @@ def pass_state(func: C) -> C: ...


class StateProtocol(Protocol):
auto_escape: str | None
current_block: str | None
auto_escape: "str | None"
current_block: "str | None"
env: Environment
name: str

def lookup(self, key: str) -> Any | None: ...
def lookup(self, key: str) -> "Any | None": ...


def _transform_state(func: TemplateCallableType[Mapping[str, Any], P, T]) -> TemplateCallableType[StateProtocol, P, T]:
def _transform_state(
func: TemplateCallableType["Mapping[str, Any]", P, T],
) -> TemplateCallableType[StateProtocol, P, T]:
"""Transform a template callable to receive a ``StateProtocol`` instance as first argument.

This is for wrapping callables like ``url_for()`` that receive a mapping as first argument so they can be used
Expand Down Expand Up @@ -96,7 +98,11 @@ def render(self, *args: Any, **kwargs: Any) -> str:
class MiniJinjaTemplateEngine(TemplateEngineProtocol["MiniJinjaTemplate", StateProtocol]):
"""The engine instance."""

def __init__(self, directory: Path | list[Path] | None = None, engine_instance: Environment | None = None) -> None:
def __init__(
self,
directory: "Path | list[Path] | None" = None,
engine_instance: "Environment | None" = None,
) -> None:
"""Minijinja based TemplateEngine.

Args:
Expand Down Expand Up @@ -169,14 +175,14 @@ def register_template_callable(
None
"""

def is_decorated(func: Callable) -> bool:
def is_decorated(func: "Callable") -> bool:
return hasattr(func, "__wrapped__") or func.__name__ not in globals()

if not is_decorated(template_callable):
template_callable = _transform_state(template_callable) # type: ignore[arg-type] # pragma: no cover
self.engine.add_global(key, pass_state(template_callable))

def render_string(self, template_string: str, context: Mapping[str, Any]) -> str:
def render_string(self, template_string: str, context: "Mapping[str, Any]") -> str:
"""Render a template from a string with the given context.

Args:
Expand All @@ -189,7 +195,7 @@ def render_string(self, template_string: str, context: Mapping[str, Any]) -> str
return self.engine.render_str(template_string, **context)

@classmethod
def from_environment(cls, minijinja_environment: Environment) -> MiniJinjaTemplateEngine:
def from_environment(cls, minijinja_environment: "Environment") -> "MiniJinjaTemplateEngine":
"""Create a MiniJinjaTemplateEngine from an existing minijinja Environment instance.

Args:
Expand Down
46 changes: 0 additions & 46 deletions tests/unit/test_contrib/test_minijinja.py

This file was deleted.

2 changes: 1 addition & 1 deletion tests/unit/test_plugins/test_flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
from litestar import Litestar, Request, get, post
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.exceptions import ImproperlyConfiguredException
from litestar.middleware.rate_limit import RateLimitConfig
from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin, flash
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response import Redirect, Template
from litestar.template import TemplateConfig, TemplateEngineProtocol
from litestar.testing import create_test_client
Expand Down
91 changes: 91 additions & 0 deletions tests/unit/test_plugins/test_minijinja.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""Functional tests for litestar.plugins.minijinja."""

from pathlib import Path

import pytest
from minijinja import Environment

from litestar.exceptions import ImproperlyConfiguredException, TemplateNotFoundException
from litestar.plugins.minijinja import MiniJinjaTemplateEngine, _transform_state


def test_minijinja_template_engine_instantiation_error(tmp_path: Path) -> None:
with pytest.raises(ImproperlyConfiguredException):
MiniJinjaTemplateEngine(directory=tmp_path, engine_instance=Environment())

with pytest.raises(ImproperlyConfiguredException):
MiniJinjaTemplateEngine()


def test_minijinja_template_engine_instantiated_with_engine() -> None:
engine = Environment()
template_engine = MiniJinjaTemplateEngine(engine_instance=engine)
assert template_engine.engine is engine


def test_minijinja_template_engine_instantiated_with_directory(tmp_path: Path) -> None:
template_dir = tmp_path / "templates"
template_dir.mkdir()
(template_dir / "hello.html").write_text("Hello {{ name }}!")

template_engine = MiniJinjaTemplateEngine(directory=template_dir)
assert isinstance(template_engine.engine, Environment)


def test_minijinja_template_engine_instantiated_with_directory_list(tmp_path: Path) -> None:
first = tmp_path / "first"
second = tmp_path / "second"
first.mkdir()
second.mkdir()
(second / "hello.html").write_text("Hi {{ name }}!")

template_engine = MiniJinjaTemplateEngine(directory=[first, second])
template = template_engine.get_template("hello.html")
assert template.render(name="World") == "Hi World!"


def test_minijinja_template_render_raises_template_not_found(tmp_path: Path) -> None:
template_dir = tmp_path / "templates"
template_dir.mkdir()
template_engine = MiniJinjaTemplateEngine(directory=template_dir)
with pytest.raises(TemplateNotFoundException):
tmpl = template_engine.get_template("not_found.html")
tmpl.render()


def test_minijinja_template_render_string() -> None:
template_engine = MiniJinjaTemplateEngine(engine_instance=Environment())
rendered = template_engine.render_string("template as a {{ value }}", context={"value": "string"})
assert rendered == "template as a string"


def test_minijinja_from_environment() -> None:
engine = Environment()
template_engine = MiniJinjaTemplateEngine.from_environment(engine)
assert template_engine.engine is engine


def test_transform_state_callable_invoked_with_template_context() -> None:
captured: dict[str, object] = {}

def callable_taking_context(context: object, value: str) -> str:
captured["context"] = context
return value.upper()

wrapped = _transform_state(callable_taking_context)
engine = Environment()
engine.add_global("shout", wrapped)
rendered = engine.render_str("{{ shout('hi') }}")
assert rendered == "HI"
assert "context" in captured


def test_register_template_callable_wraps_undecorated_callable() -> None:
template_engine = MiniJinjaTemplateEngine(engine_instance=Environment())

def shout(_context: object, value: str) -> str:
return value.upper()

template_engine.register_template_callable("shout", shout)
rendered = template_engine.render_string("{{ shout('hi') }}", context={})
assert rendered == "HI"
2 changes: 1 addition & 1 deletion tests/unit/test_template/test_built_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from litestar import get
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response.template import Template
from litestar.template.config import TemplateConfig
from litestar.testing import create_test_client
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_template/test_builtin_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from litestar import get
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response.template import Template
from litestar.status_codes import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR
from litestar.template.config import TemplateConfig
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_template/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from litestar import MediaType, get
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response.template import Template
from litestar.template.config import TemplateConfig
from litestar.testing import create_test_client
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_template/test_csrf_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from litestar.config.csrf import CSRFConfig
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.middleware.csrf import generate_csrf_token
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response.template import Template
from litestar.template.config import TemplateConfig
from litestar.testing import create_test_client
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_template/test_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from litestar import Litestar, MediaType, get
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
from litestar.exceptions import ImproperlyConfiguredException
from litestar.plugins.minijinja import MiniJinjaTemplateEngine
from litestar.response.template import Template
from litestar.template import TemplateEngineProtocol
from litestar.template.config import TemplateConfig
Expand Down