Skip to content

Commit

Permalink
Starlette support.
Browse files Browse the repository at this point in the history
  • Loading branch information
pelme committed Aug 25, 2024
1 parent c9e8b1e commit db5ac8f
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## next
- Support passing htpy elements directly to Starlette responses. Document Starlette support. [PR #50](https://github.com/pelme/htpy/pull/50).

## 24.8.2 - 2024-08-23
- Added support for passing data between components via Context. See the [Usage
docs](usage.md#passing-data-with-context) for more information. [PR #48](https://github.com/pelme/htpy/pull/48).
Expand Down
21 changes: 21 additions & 0 deletions docs/starlette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Usage with Starlette

htpy is easy to integrate with Starlette. Since FastAPI is built upon Starlette, htpy can also be used with FastAPI to generate HTML.

To return HTML contents, pass a htpy element to Starlette's `HTMLResponse`:

```py
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import HTMLResponse

from htpy import h1

app = Starlette()


@app.route("/")
def index(request: Request) -> HTMLResponse:
return HTMLResponse(h1["Hi Starlette!"])

```
12 changes: 12 additions & 0 deletions examples/starlette_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import HTMLResponse

from htpy import h1

app = Starlette()


@app.route("/")
def index(request: Request) -> HTMLResponse:
return HTMLResponse(h1["Hi Starlette!"])
6 changes: 6 additions & 0 deletions htpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ def _iter_context(self, ctx: dict[Context[t.Any], t.Any]) -> Iterator[str]:
def __repr__(self) -> str:
return f"<{self.__class__.__name__} '{self}'>"

# Allow starlette Response.render to directly render this element without
# explicitly casting to str:
# https://github.com/encode/starlette/blob/5ed55c441126687106109a3f5e051176f88cd3e6/starlette/responses.py#L44-L49
def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes:
return str(self).encode(encoding, errors)

# Avoid having Django "call" a htpy element that is injected into a
# template. Setting do_not_call_in_templates will prevent Django from doing
# an extra call:
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ nav:
- common-patterns.md
- static-typing.md
- django.md
- starlette.md
- streaming.md
- html2htpy.md
- faq.md
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ optional-dependencies.dev = [
"django",
"django-stubs",
"jinja2",
"starlette",
"httpx",
]
optional-dependencies.docs = [
"mkdocs-material==9.5.12",
Expand Down
32 changes: 32 additions & 0 deletions tests/test_starlette.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import annotations

import typing as t

from starlette.applications import Starlette
from starlette.responses import HTMLResponse
from starlette.routing import Route
from starlette.testclient import TestClient

from htpy import h1

if t.TYPE_CHECKING:
from starlette.requests import Request


def sync(request: Request) -> HTMLResponse:
return HTMLResponse(h1["Hello, sync world!"])


client = TestClient(
Starlette(
debug=True,
routes=[
Route("/sync", sync),
],
)
)


def test_html_response() -> None:
response = client.get("/sync")
assert response.content == b"<h1>Hello, sync world!</h1>"

0 comments on commit db5ac8f

Please sign in to comment.