Skip to content

Commit

Permalink
Merge pull request #40 from volfpeter/jinja-error-rendering-fix
Browse files Browse the repository at this point in the history
Jinja error rendering fix
  • Loading branch information
volfpeter authored Nov 5, 2024
2 parents 1480f05 + 4051c3d commit 2fcc0aa
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 9 deletions.
18 changes: 15 additions & 3 deletions fasthx/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,9 @@ def hx(
self._make_render_function(template, make_context=make_context, prefix=prefix),
render_error=None
if error_template is None
else self._make_render_function(error_template, make_context=make_context, prefix=prefix),
else self._make_render_function(
error_template, make_context=make_context, prefix=prefix, error_renderer=True
),
no_data=self.no_data or no_data,
)

Expand Down Expand Up @@ -341,7 +343,9 @@ def page(
self._make_render_function(template, make_context=make_context, prefix=prefix),
render_error=None
if error_template is None
else self._make_render_function(error_template, make_context=make_context, prefix=prefix),
else self._make_render_function(
error_template, make_context=make_context, prefix=prefix, error_renderer=True
),
)

def _make_render_function(
Expand All @@ -350,6 +354,7 @@ def _make_render_function(
*,
make_context: JinjaContextFactory,
prefix: str | None,
error_renderer: bool = False,
) -> HTMLRenderer[Any]:
"""
Creates an `HTMLRenderer` with the given configuration.
Expand All @@ -358,10 +363,16 @@ def _make_render_function(
template: The template the renderer function should use.
make_context: The Jinja rendering context factory to use.
prefix: Optional template name prefix.
error_renderer: Whether this is an error renderer creation.
"""

def render(result: Any, *, context: dict[str, Any], request: Request) -> str | Response:
template_name = self._resolve_template_name(template, prefix=prefix, request=request)
template_name = self._resolve_template_name(
template,
error=result if error_renderer else None,
prefix=prefix,
request=request,
)
return self._make_response(
template_name,
jinja_context=make_context(route_result=result, route_context=context),
Expand Down Expand Up @@ -413,6 +424,7 @@ def _resolve_template_name(
Arguments:
template: The template selector.
error: The error raised by the route.
prefix: Optional template name prefix.
request: The current request.
Expand Down
2 changes: 1 addition & 1 deletion fasthx/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,5 @@ def get_component(self, request: Request, error: Exception | None) -> str:
...


ComponentSelector: TypeAlias = str | RequestComponentSelector[T]
ComponentSelector: TypeAlias = T | RequestComponentSelector[T]
"""Type alias for known component selectors."""
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "fasthx"
version = "2.0.1"
version = "2.0.2"
description = "FastAPI data APIs with HTMX support."
authors = ["Peter Volf <[email protected]>"]
readme = "README.md"
Expand Down
20 changes: 16 additions & 4 deletions tests/test_jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self, data: dict[str, Any], *, response: Response) -> None:


@pytest.fixture
def jinja_app() -> FastAPI:
def jinja_app() -> FastAPI: # noqa: C901
app = FastAPI()

jinja = Jinja(Jinja2Templates("tests/templates"))
Expand Down Expand Up @@ -88,6 +88,7 @@ def header_with_no_default() -> User:
return billy

@app.get("/error")
@app.get("/error/{kind}")
@jinja.hx(
TemplateHeader("X-Component", {}), # No rendering if there's no exception.
error_template=TemplateHeader(
Expand All @@ -98,20 +99,29 @@ def header_with_no_default() -> User:
),
no_data=True,
)
def error(response: Response) -> None:
def error(response: Response, kind: str | None = None) -> None:
if kind:
# Unhandled error type to see if we get HTTP 500
raise ValueError(kind)

raise RenderedError({"a": 1, "b": 2}, response=response)

@app.get("/error-page")
@app.get("/error-page/{kind}")
@jinja.page(
TemplateHeader("X-Component", {}), # No rendering if there's no exception.
error_template=TemplateHeader(
"X-Error-Component",
{},
default="hello-world.jinja",
error=(RenderedError, TypeError, ValueError), # Test error tuple
error=(RenderedError, TypeError, SyntaxError), # Test error tuple
),
)
def error_page(response: Response) -> None:
def error_page(response: Response, kind: str | None = None) -> None:
if kind:
# Unhandled error type to see if we get HTTP 500
raise ValueError(kind)

raise RenderedError({"a": 1, "b": 2}, response=response)

@app.get("/global-no-data")
Expand Down Expand Up @@ -194,8 +204,10 @@ def jinja_client(jinja_app: FastAPI) -> TestClient:
("/htmx-only", {"HX-Request": "false"}, 400, "", {}),
# hx() error rendering
("/error", {"HX-Request": "true"}, 456, "Hello World!", {}),
("/error/value-error", {"HX-Request": "true"}, 500, "", {}),
# page() error rendering
("/error-page", None, 456, "Hello World!", {}),
("/error-page/value-error", None, 500, "None", {}),
# Globally disabled data responses
("/global-no-data", None, 400, "", {}),
),
Expand Down

0 comments on commit 2fcc0aa

Please sign in to comment.