templates.env.filters['marked'] = marked_filter
```
+
+## Using custom jinja2.Environment instance
+
+Starlette also accepts a preconfigured [`jinja2.Environment`](https://jinja.palletsprojects.com/en/3.0.x/api/#api) instance.
+
+
+```python
+import jinja2
+from starlette.templating import Jinja2Templates
+
+env = jinja2.Environment(...)
+templates = Jinja2Templates(env=env)
+```
+
+
## Context processors
A context processor is a function that returns a dictionary to be merged into a template context.
import typing
+import warnings
from os import PathLike
from starlette.background import BackgroundTask
return templates.TemplateResponse("index.html", {"request": request})
"""
+ @typing.overload
def __init__(
self,
directory: typing.Union[
- str, PathLike, typing.Sequence[typing.Union[str, PathLike]]
+ str,
+ PathLike,
+ typing.Sequence[typing.Union[str, PathLike]],
],
+ *,
context_processors: typing.Optional[
typing.List[typing.Callable[[Request], typing.Dict[str, typing.Any]]]
] = None,
**env_options: typing.Any,
) -> None:
+ ...
+
+ @typing.overload
+ def __init__(
+ self,
+ *,
+ env: "jinja2.Environment",
+ context_processors: typing.Optional[
+ typing.List[typing.Callable[[Request], typing.Dict[str, typing.Any]]]
+ ] = None,
+ ) -> None:
+ ...
+
+ def __init__(
+ self,
+ directory: typing.Union[
+ str, PathLike, typing.Sequence[typing.Union[str, PathLike]], None
+ ] = None,
+ *,
+ context_processors: typing.Optional[
+ typing.List[typing.Callable[[Request], typing.Dict[str, typing.Any]]]
+ ] = None,
+ env: typing.Optional["jinja2.Environment"] = None,
+ **env_options: typing.Any,
+ ) -> None:
+ if env_options:
+ warnings.warn(
+ "Extra environment options are deprecated. Use a preconfigured jinja2.Environment instead.", # noqa: E501
+ DeprecationWarning,
+ )
assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates"
- self.env = self._create_env(directory, **env_options)
+ assert directory or env, "either 'directory' or 'env' arguments must be passed"
self.context_processors = context_processors or []
+ if directory is not None:
+ self.env = self._create_env(directory, **env_options)
+ elif env is not None:
+ self.env = env
def _create_env(
self,
import os
from pathlib import Path
+import jinja2
import pytest
from starlette.applications import Starlette
assert response.text == "<html><a href='http://testserver/b'></a> b</html>"
assert response.template.name == "template_b.html"
assert set(response.context.keys()) == {"request"}
+
+
+def test_templates_require_directory_or_environment():
+ with pytest.raises(
+ AssertionError, match="either 'directory' or 'env' arguments must be passed"
+ ):
+ Jinja2Templates() # type: ignore[call-overload]
+
+
+def test_templates_with_directory(tmpdir):
+ path = os.path.join(tmpdir, "index.html")
+ with open(path, "w") as file:
+ file.write("Hello")
+
+ templates = Jinja2Templates(directory=str(tmpdir))
+ template = templates.get_template("index.html")
+ assert template.render({}) == "Hello"
+
+
+def test_templates_with_environment(tmpdir):
+ path = os.path.join(tmpdir, "index.html")
+ with open(path, "w") as file:
+ file.write("Hello")
+
+ env = jinja2.Environment(loader=jinja2.FileSystemLoader(str(tmpdir)))
+ templates = Jinja2Templates(env=env)
+ template = templates.get_template("index.html")
+ assert template.render({}) == "Hello"
+
+
+def test_templates_with_environment_options_emit_warning(tmpdir):
+ with pytest.warns(DeprecationWarning):
+ Jinja2Templates(str(tmpdir), autoescape=True)