Skip to content

Commit 96479da

Browse files
authored
Drop deprecation warnings on the templating module (#3118)
1 parent f99e11f commit 96479da

File tree

2 files changed

+40
-368
lines changed

2 files changed

+40
-368
lines changed

starlette/templating.py

Lines changed: 38 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from __future__ import annotations
22

3-
import warnings
43
from collections.abc import Callable, Mapping, Sequence
54
from os import PathLike
6-
from typing import Any, cast, overload
5+
from typing import TYPE_CHECKING, Any, overload
76

87
from starlette.background import BackgroundTask
98
from starlette.datastructures import URL
@@ -18,12 +17,15 @@
1817
# hence we try to get pass_context (most installs will be >=3.1)
1918
# and fall back to contextfunction,
2019
# adding a type ignore for mypy to let us access an attribute that may not exist
21-
if hasattr(jinja2, "pass_context"):
20+
if TYPE_CHECKING:
2221
pass_context = jinja2.pass_context
23-
else: # pragma: no cover
24-
pass_context = jinja2.contextfunction # type: ignore[attr-defined]
25-
except ModuleNotFoundError: # pragma: no cover
26-
jinja2 = None # type: ignore[assignment]
22+
else:
23+
if hasattr(jinja2, "pass_context"):
24+
pass_context = jinja2.pass_context
25+
else: # pragma: no cover
26+
pass_context = jinja2.contextfunction # type: ignore[attr-defined]
27+
except ImportError as _import_error: # pragma: no cover
28+
raise ImportError("jinja2 must be installed to use Jinja2Templates") from _import_error
2729

2830

2931
class _TemplateResponse(HTMLResponse):
@@ -45,23 +47,22 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
4547
request = self.context.get("request", {})
4648
extensions = request.get("extensions", {})
4749
if "http.response.debug" in extensions: # pragma: no branch
48-
await send(
49-
{
50-
"type": "http.response.debug",
51-
"info": {
52-
"template": self.template,
53-
"context": self.context,
54-
},
55-
}
56-
)
50+
await send({"type": "http.response.debug", "info": {"template": self.template, "context": self.context}})
5751
await super().__call__(scope, receive, send)
5852

5953

6054
class Jinja2Templates:
61-
"""
62-
templates = Jinja2Templates("templates")
55+
"""Jinja2 template renderer.
56+
57+
Example:
58+
```python
59+
from starlette.templating import Jinja2Templates
60+
61+
templates = Jinja2Templates(directory="templates")
6362
64-
return templates.TemplateResponse("index.html", {"request": request})
63+
async def homepage(request: Request) -> Response:
64+
return templates.TemplateResponse(request, "index.html")
65+
```
6566
"""
6667

6768
@overload
@@ -70,7 +71,6 @@ def __init__(
7071
directory: str | PathLike[str] | Sequence[str | PathLike[str]],
7172
*,
7273
context_processors: list[Callable[[Request], dict[str, Any]]] | None = None,
73-
**env_options: Any,
7474
) -> None: ...
7575

7676
@overload
@@ -87,34 +87,17 @@ def __init__(
8787
*,
8888
context_processors: list[Callable[[Request], dict[str, Any]]] | None = None,
8989
env: jinja2.Environment | None = None,
90-
**env_options: Any,
9190
) -> None:
92-
if env_options:
93-
warnings.warn(
94-
"Extra environment options are deprecated. Use a preconfigured jinja2.Environment instead.",
95-
DeprecationWarning,
96-
)
97-
assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates"
9891
assert bool(directory) ^ bool(env), "either 'directory' or 'env' arguments must be passed"
9992
self.context_processors = context_processors or []
10093
if directory is not None:
101-
self.env = self._create_env(directory, **env_options)
94+
loader = jinja2.FileSystemLoader(directory)
95+
self.env = jinja2.Environment(loader=loader)
10296
elif env is not None: # pragma: no branch
10397
self.env = env
10498

10599
self._setup_env_defaults(self.env)
106100

107-
def _create_env(
108-
self,
109-
directory: str | PathLike[str] | Sequence[str | PathLike[str]],
110-
**env_options: Any,
111-
) -> jinja2.Environment:
112-
loader = jinja2.FileSystemLoader(directory)
113-
env_options.setdefault("loader", loader)
114-
env_options.setdefault("autoescape", True)
115-
116-
return jinja2.Environment(**env_options)
117-
118101
def _setup_env_defaults(self, env: jinja2.Environment) -> None:
119102
@pass_context
120103
def url_for(
@@ -131,7 +114,6 @@ def url_for(
131114
def get_template(self, name: str) -> jinja2.Template:
132115
return self.env.get_template(name)
133116

134-
@overload
135117
def TemplateResponse(
136118
self,
137119
request: Request,
@@ -141,66 +123,23 @@ def TemplateResponse(
141123
headers: Mapping[str, str] | None = None,
142124
media_type: str | None = None,
143125
background: BackgroundTask | None = None,
144-
) -> _TemplateResponse: ...
145-
146-
@overload
147-
def TemplateResponse(
148-
self,
149-
name: str,
150-
context: dict[str, Any] | None = None,
151-
status_code: int = 200,
152-
headers: Mapping[str, str] | None = None,
153-
media_type: str | None = None,
154-
background: BackgroundTask | None = None,
155126
) -> _TemplateResponse:
156-
# Deprecated usage
157-
...
158-
159-
def TemplateResponse(self, *args: Any, **kwargs: Any) -> _TemplateResponse:
160-
if args:
161-
if isinstance(args[0], str): # the first argument is template name (old style)
162-
warnings.warn(
163-
"The `name` is not the first parameter anymore. "
164-
"The first parameter should be the `Request` instance.\n"
165-
'Replace `TemplateResponse(name, {"request": request})` by `TemplateResponse(request, name)`.',
166-
DeprecationWarning,
167-
)
168-
169-
name = args[0]
170-
context = args[1] if len(args) > 1 else kwargs.get("context", {})
171-
status_code = args[2] if len(args) > 2 else kwargs.get("status_code", 200)
172-
headers = args[3] if len(args) > 3 else kwargs.get("headers")
173-
media_type = args[4] if len(args) > 4 else kwargs.get("media_type")
174-
background = args[5] if len(args) > 5 else kwargs.get("background")
175-
176-
if "request" not in context:
177-
raise ValueError('context must include a "request" key')
178-
request = context["request"]
179-
else: # the first argument is a request instance (new style)
180-
request = args[0]
181-
name = args[1] if len(args) > 1 else kwargs["name"]
182-
context = args[2] if len(args) > 2 else kwargs.get("context", {})
183-
status_code = args[3] if len(args) > 3 else kwargs.get("status_code", 200)
184-
headers = args[4] if len(args) > 4 else kwargs.get("headers")
185-
media_type = args[5] if len(args) > 5 else kwargs.get("media_type")
186-
background = args[6] if len(args) > 6 else kwargs.get("background")
187-
else: # all arguments are kwargs
188-
if "request" not in kwargs:
189-
warnings.warn(
190-
"The `TemplateResponse` now requires the `request` argument.\n"
191-
'Replace `TemplateResponse(name, {"context": context})` by `TemplateResponse(request, name)`.',
192-
DeprecationWarning,
193-
)
194-
if "request" not in kwargs.get("context", {}):
195-
raise ValueError('context must include a "request" key')
196-
197-
context = kwargs.get("context", {})
198-
request = kwargs.get("request", context.get("request"))
199-
name = cast(str, kwargs["name"])
200-
status_code = kwargs.get("status_code", 200)
201-
headers = kwargs.get("headers")
202-
media_type = kwargs.get("media_type")
203-
background = kwargs.get("background")
127+
"""
128+
Render a template and return an HTML response.
129+
130+
Args:
131+
request: The incoming request instance.
132+
name: The template file name to render.
133+
context: Variables to pass to the template.
134+
status_code: HTTP status code for the response.
135+
headers: Additional headers to include in the response.
136+
media_type: Media type for the response.
137+
background: Background task to run after response is sent.
138+
139+
Returns:
140+
An HTML response with the rendered template content.
141+
"""
142+
context = context or {}
204143

205144
context.setdefault("request", request)
206145
for context_processor in self.context_processors:

0 commit comments

Comments
 (0)