Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(template): add template_str for rendering from strings #2689

Merged
merged 29 commits into from
Nov 17, 2023
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7fe586a
feat(template): add ``template_str`` for rendering from strings
JacobCoffee Nov 17, 2023
ad97176
fix: match media type options for template files
JacobCoffee Nov 17, 2023
9ebf9ab
fix: warn of precedence
JacobCoffee Nov 17, 2023
56cfbed
feat: add mako
JacobCoffee Nov 17, 2023
f93715d
fix: media type setting wrong
JacobCoffee Nov 17, 2023
77ea25c
feat: minijinja
JacobCoffee Nov 17, 2023
93cb42c
chore: remove testing method
JacobCoffee Nov 17, 2023
681e87d
chore: apply @alc-alc suggestion
JacobCoffee Nov 17, 2023
aea9916
docs: add usage documentation
JacobCoffee Nov 17, 2023
21c5a72
tests: add tests
JacobCoffee Nov 17, 2023
511ae87
ci: mypy
JacobCoffee Nov 17, 2023
d9a7115
fix(tests): caught bug that was running minijinja tests as jinja
JacobCoffee Nov 17, 2023
5f94080
docs: properly link to HTMX page
JacobCoffee Nov 17, 2023
cfca8b8
ci(coverage): fill in missing coverage
JacobCoffee Nov 17, 2023
5fe1718
tests: test catching ``None`` template types
JacobCoffee Nov 17, 2023
eca6c14
Merge branch 'main' into 2687-template-str
JacobCoffee Nov 17, 2023
6f5042e
tests: fix none type tests
JacobCoffee Nov 17, 2023
f6e2439
Update litestar/response/template.py
JacobCoffee Nov 17, 2023
baeaffd
fix: raise error if both template_name and template_str are defined
JacobCoffee Nov 17, 2023
8a3521d
docs: remove stale statement about precedence
JacobCoffee Nov 17, 2023
d111657
ci(mypy): make happy the type checking goddess
JacobCoffee Nov 17, 2023
4d30716
Merge branch 'main' into 2687-template-str
JacobCoffee Nov 17, 2023
8092dad
chore: apply suggestion from @guacs
JacobCoffee Nov 17, 2023
c218e4e
chore: apply suggestion from @peterschutt
JacobCoffee Nov 17, 2023
8226e6e
fix: remove unneeded check
JacobCoffee Nov 17, 2023
8ad618b
test: rework test to get new error raised
JacobCoffee Nov 17, 2023
e653101
ci(typing): make mypy happy-ish
JacobCoffee Nov 17, 2023
2dbc4f1
refactor: narrowing away optional template engine
peterschutt Nov 17, 2023
6ae576d
ci: apply pre-commit
JacobCoffee Nov 17, 2023
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
41 changes: 20 additions & 21 deletions litestar/response/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import itertools
from mimetypes import guess_type
from pathlib import PurePath
from typing import TYPE_CHECKING, Any, Iterable
from typing import TYPE_CHECKING, Any, Iterable, cast

from litestar.constants import SCOPE_STATE_CSRF_TOKEN_KEY
from litestar.enums import MediaType
Expand Down Expand Up @@ -62,6 +62,9 @@ def __init__(
the media type based on the template name. If this fails, fall back to ``text/plain``.
status_code: A value for the response HTTP status code.
"""
if not (template_name or template_str):
raise ValueError("Either template_name or template_str must be provided.")

JacobCoffee marked this conversation as resolved.
Show resolved Hide resolved
if template_name and template_str:
raise ValueError("Either template_name or template_str must be provided, not both.")

Expand Down Expand Up @@ -117,31 +120,32 @@ def to_asgi_response(
alternative="request.app",
)

if not request.app.template_engine:
if not (template_engine := request.app.template_engine):
raise ImproperlyConfiguredException("Template engine is not configured")

headers = {**headers, **self.headers} if headers is not None else self.headers
cookies = self.cookies if cookies is None else itertools.chain(self.cookies, cookies)

media_type = self.media_type or media_type
if not media_type and self.template_name:
suffixes = PurePath(self.template_name).suffixes
for suffix in suffixes:
if _type := guess_type(f"name{suffix}")[0]:
media_type = _type
break
if not media_type:
if self.template_name:
suffixes = PurePath(self.template_name).suffixes
for suffix in suffixes:
if _type := guess_type(f"name{suffix}")[0]:
media_type = _type
break
else:
media_type = MediaType.TEXT
else:
media_type = MediaType.TEXT
media_type = MediaType.HTML
JacobCoffee marked this conversation as resolved.
Show resolved Hide resolved

context = self.create_template_context(request)

if self.template_str is not None:
body = self._render_from_string(self.template_str, request)
media_type = media_type or MediaType.HTML
body = template_engine.render_string(self.template_str, context)
else:
if not self.template_name and not self.template_str:
raise ValueError("Either template_name or template_str must be provided")

template = request.app.template_engine.get_template(self.template_name)
context = self.create_template_context(request)
# cast to str b/c we know that either template_name cannot be None if template_str is None
template = template_engine.get_template(cast("str", self.template_name))
JacobCoffee marked this conversation as resolved.
Show resolved Hide resolved
body = template.render(**context).encode(self.encoding)

return ASGIResponse(
Expand All @@ -156,8 +160,3 @@ def to_asgi_response(
media_type=media_type,
status_code=self.status_code or status_code,
)

def _render_from_string(self, template_str: str, request: Request) -> bytes:
"""Render the template from a string."""
context = self.create_template_context(request)
return request.app.template_engine.render_string(template_str, context).encode(self.encoding)
Loading