Skip to content

Commit

Permalink
feat: add minjinja (#2250)
Browse files Browse the repository at this point in the history
* feat: add minjinja

* feat: add minjinja, more tests

* feat: add minjinja, added url_for tests

* feat: add minjinja, added context tests, seems we have something to deal with escapes

* feat: add minjinja, added working csrf token tests

* fix: not a fix but demonstrates that using | safe filter on url_for we can pass the builtin_functions tests, need to dig

* fix: revert and dont use safe in tests

* fix: change tests to mimic what minijinja would expect

* fix: remove undeeded Environment config

* fix: pin anyio because private imports broke CI

* fix: add docs for minijinja
fix: add tests for docs/examples/templating

* fix: tests for running templates

* fix: poetry lock

* fix: anyio unpin

* fix: some tests didnt use proper docs examples
fix: seems mako prints a \n at end of templates if the html conatins one

* fix: added more tabs for minijinja
fix: typos in tabs names

* fix: removed useless debug in mako examples

* fix: auto rewite of the mako template by linters, tests will fail

* fix: mako tests contains a \n at the end of the generated string

---------

Co-authored-by: Na'aman Hirschfeld <[email protected]>
  • Loading branch information
euri10 and Goldziher authored Sep 8, 2023
1 parent 3ef2a2c commit bb08a53
Show file tree
Hide file tree
Showing 27 changed files with 672 additions and 94 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ repos:
jsbeautifier,
mako,
mongomock_motor,
minijinja,
msgspec,
multidict,
opentelemetry-instrumentation-asgi,
Expand Down
3 changes: 1 addition & 2 deletions docs/examples/templating/engine_instance_jinja.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.template.config import TemplateConfig

template_config = TemplateConfig(engine=JinjaTemplateEngine)
template_config.engine_instance.engine.globals["foo"] = "bar"
template_config = TemplateConfig(engine=JinjaTemplateEngine, directory="templates")
3 changes: 1 addition & 2 deletions docs/examples/templating/engine_instance_mako.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from litestar.contrib.mako import MakoTemplateEngine
from litestar.template.config import TemplateConfig

template_config = TemplateConfig(engine=MakoTemplateEngine)
template_config.engine_instance.engine.has_template("foo")
template_config = TemplateConfig(engine=MakoTemplateEngine, directory="templates")
4 changes: 4 additions & 0 deletions docs/examples/templating/engine_instance_minijinja.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from litestar.contrib.minijnja import MiniJinjaTemplateEngine
from litestar.template.config import TemplateConfig

template_config = TemplateConfig(engine=MiniJinjaTemplateEngine, directory="templates")
2 changes: 1 addition & 1 deletion docs/examples/templating/returning_templates_jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def index(name: str) -> Template:
app = Litestar(
route_handlers=[index],
template_config=TemplateConfig(
directory=Path("templates"),
directory=Path(__file__).parent / "templates",
engine=JinjaTemplateEngine,
),
)
2 changes: 1 addition & 1 deletion docs/examples/templating/returning_templates_mako.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def index(name: str) -> Template:
app = Litestar(
route_handlers=[index],
template_config=TemplateConfig(
directory=Path("templates"),
directory=Path(__file__).parent / "templates",
engine=MakoTemplateEngine,
),
)
20 changes: 20 additions & 0 deletions docs/examples/templating/returning_templates_minijinja.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pathlib import Path

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


@get(path="/")
def index(name: str) -> Template:
return Template(template_name="hello.html.minijinja", context={"name": name})


app = Litestar(
route_handlers=[index],
template_config=TemplateConfig(
directory=Path(__file__).parent / "templates",
engine=MiniJinjaTemplateEngine,
),
)
13 changes: 13 additions & 0 deletions docs/examples/templating/template_engine_minijinja.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pathlib import Path

from litestar import Litestar
from litestar.contrib.minijnja import MiniJinjaTemplateEngine
from litestar.template.config import TemplateConfig

app = Litestar(
route_handlers=[],
template_config=TemplateConfig(
directory=Path("templates"),
engine=MiniJinjaTemplateEngine,
),
)
2 changes: 1 addition & 1 deletion docs/examples/templating/template_functions_jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def register_template_callables(engine: JinjaTemplateEngine) -> None:


template_config = TemplateConfig(
directory=Path("templates"),
directory=Path(__file__).parent / "templates",
engine=JinjaTemplateEngine,
engine_callback=register_template_callables,
)
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/templating/template_functions_mako.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def register_template_callables(engine: MakoTemplateEngine) -> None:


template_config = TemplateConfig(
directory=Path("templates"),
directory=Path(__file__).parent / "templates",
engine=MakoTemplateEngine,
engine_callback=register_template_callables,
)
Expand Down
34 changes: 34 additions & 0 deletions docs/examples/templating/template_functions_minijinja.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import functools
from pathlib import Path
from typing import Any, Dict

from litestar import Litestar, get
from litestar.contrib.minijnja import MiniJinjaTemplateEngine, minijinja_from_state
from litestar.response import Template
from litestar.template.config import TemplateConfig


def my_template_function(ctx: Dict[str, Any]) -> str:
return ctx.get("my_context_key", "nope")


def register_template_callables(engine: MiniJinjaTemplateEngine) -> None:
engine.register_template_callable(
key="check_context_key",
template_callable=functools.partial(minijinja_from_state, my_template_function),
)


template_config = TemplateConfig(
directory=Path(__file__).parent / "templates",
engine=MiniJinjaTemplateEngine,
engine_callback=register_template_callables,
)


@get("/")
def index() -> Template:
return Template(template_name="index.html.minijinja")


app = Litestar(route_handlers=[index], template_config=template_config)
1 change: 1 addition & 0 deletions docs/examples/templating/templates/hello.html.minijinja
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello <strong>{{ name }}</strong>
1 change: 1 addition & 0 deletions docs/examples/templating/templates/index.html.minijinja
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<strong>check_context_key: </strong>{{ check_context_key() }}
2 changes: 1 addition & 1 deletion docs/migration/flask.rst
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ Templates
Flask comes with the `Jinja <https://jinja.palletsprojects.com/en/3.1.x/>`_ templating
engine built-in. You can use Jinja with Litestar as well, but you’ll need to install it
explicitly. You can do by installing Litestar with ``pip install litestar[jinja]``.
In addition to Jinja, Litestar supports `Mako <https://www.makotemplates.org/>`_ templates as well.
In addition to Jinja, Litestar supports `Mako <https://www.makotemplates.org/>`_ and `Minijinja <https:/mitsuhiko/minijinja/tree/main/minijinja-py>`_ templates as well.

.. tab-set::
.. tab-item:: Flask
Expand Down
73 changes: 66 additions & 7 deletions docs/usage/templating.rst
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
Templating
==========

Litestar has built-in support for both the `Jinja2 <https://jinja.palletsprojects.com/en/3.0.x/>`_
and `Mako <https://www.makotemplates.org/>`_ template engines, as well as abstractions to
Litestar has built-in support for `Jinja2 <https://jinja.palletsprojects.com/en/3.0.x/>`_
, `Mako <https://www.makotemplates.org/>`_ and `Minijinja <https:/mitsuhiko/minijinja/tree/main/minijinja-py>`_ template engines, as well as abstractions to
make use of any template engine you wish.

Template engines
----------------

To stay lightweight, a Litestar installation does not include the *Jinja* or *Mako*
To stay lightweight, a Litestar installation does not include the *Jinja*, *Mako* or *Minijinja*
libraries themselves. Before you can start using them, you have to install it via the
respective extra:


* ``pip install litestar[jinja]`` for Jinja2
* ``pip install litestar[mako]`` for Mako
* ``pip install litestar[minijinja]`` for Minijinja

.. tip::

Expand All @@ -41,6 +42,12 @@ To register one of the built-in template engines you simply need to pass it to t
.. literalinclude:: /examples/templating/template_engine_mako.py
:language: python

.. tab-item:: MiniJinja
:sync: minijinja

.. literalinclude:: /examples/templating/template_engine_minijinja.py
:language: python

.. note::

The ``directory`` parameter passed to :class:`TemplateConfig <litestar.template.TemplateConfig>`
Expand Down Expand Up @@ -112,12 +119,18 @@ If you need to access the template engine instance, you can do so via the
.. literalinclude:: /examples/templating/engine_instance_jinja.py
:language: python

.. tab-item:: mako
.. tab-item:: Mako
:sync: mako

.. literalinclude:: /examples/templating/engine_instance_mako.py
:language: python

.. tab-item:: MiniJinja
:sync: minijinja

.. literalinclude:: /examples/templating/engine_instance_minijinja.py
:language: python

Template responses
------------------

Expand All @@ -132,12 +145,17 @@ your route handlers:
.. literalinclude:: /examples/templating/returning_templates_jinja.py
:language: python

.. tab-item:: mako
.. tab-item:: Mako
:sync: mako

.. literalinclude:: /examples/templating/returning_templates_mako.py
:language: python

.. tab-item:: MiniJinja
:sync: minijinja

.. literalinclude:: /examples/templating/returning_templates_minijinja.py
:language: python

* ``name`` is the name of the template file within on of the specified directories. If
no file with that name is found, a :class:`TemplateNotFoundException <.exceptions.TemplateNotFoundException>`
Expand Down Expand Up @@ -176,7 +194,7 @@ Accessing ``app.state.key`` for example would look like this:
</html>


.. tab-item:: mako
.. tab-item:: Mako
:sync: mako

.. code-block:: html
Expand All @@ -191,6 +209,19 @@ Accessing ``app.state.key`` for example would look like this:
</html>


.. tab-item:: MiniJinja
:sync: minijinja

.. code-block:: html

<html>
<body>
<div>
<span>My state value: {{request.app.state.some_key}}</span>
</div>
</body>
</html>


Adding CSRF inputs
^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -221,7 +252,7 @@ With that in place, you can now insert the CSRF input field inside an HTML form:
</body>
</html>

.. tab-item:: mako
.. tab-item:: Mako
:sync: mako

.. code-block:: html
Expand All @@ -240,6 +271,24 @@ With that in place, you can now insert the CSRF input field inside an HTML form:
</body>
</html>

.. tab-item:: MiniJinja
:sync: minijinja

.. code-block:: html

<html>
<body>
<div>
<form action="https://myserverurl.com/some-endpoint" method="post">
{{ csrf_input }}
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname">
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname">
</form>
</div>
</body>
</html>


The input holds a CSRF token as its value and is hidden so users cannot see or interact with it. The token is sent
Expand Down Expand Up @@ -319,6 +368,16 @@ the call method. For example:
:language: html
:caption: templates/index.html.mako

.. tab-item:: Minijinja
:sync: minijinja

.. literalinclude:: /examples/templating/template_functions_minijinja.py
:caption: template_functions.py
:language: python

.. literalinclude:: /examples/templating/templates/index.html.minijinja
:language: html
:caption: templates/index.html.minijinja

Run the example with ``uvicorn template_functions:app`` , visit http://127.0.0.1:8000, and
you'll see
Expand Down
Loading

0 comments on commit bb08a53

Please sign in to comment.