Tilde-decode custom page path variables, refs #1732

Wildcard custom pages (e.g. templates/pages/{topic}.html) passed the raw
tilde-encoded path segment straight into the template context, so a URL
like /topic_hello~20world rendered "hello~20world" instead of "hello
world" - and ids containing spaces or slashes never matched user data.

Tilde-decode the captured groups, matching how the database/table/row
url_vars are already decoded elsewhere in the router.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
TowyTowy 2026-07-15 11:31:37 +02:00
commit 0a55fcbd6a
2 changed files with 27 additions and 1 deletions

View file

@ -3001,7 +3001,15 @@ class DatasetteRouter:
for regex, wildcard_template in page_routes:
match = regex.match(route_path)
if match is not None:
context.update(match.groupdict())
# Tilde-decode captured path variables so templates see
# the real value - e.g. "hello world" not "hello~20world"
# https://github.com/simonw/datasette/issues/1732
context.update(
{
key: tilde_decode(value)
for key, value in match.groupdict().items()
}
)
template = wildcard_template
break

View file

@ -92,6 +92,24 @@ def test_custom_route_pattern(custom_pages_client, path, expected):
assert response.text.strip() == expected
@pytest.mark.parametrize(
"path,expected",
[
# Tilde-encoded path variables should reach the template decoded
# https://github.com/simonw/datasette/issues/1732
("/topic_hello~20world", "Topic page for hello world"),
("/topic_caf~C3~A9", "Topic page for café"),
("/topic_a~2Fb/x~20y", "Slug: x y, Topic: a/b"),
],
)
def test_custom_route_pattern_decodes_path_variables(
custom_pages_client, path, expected
):
response = custom_pages_client.get(path)
assert response.status == 200
assert response.text.strip() == expected
def test_custom_route_pattern_404(custom_pages_client):
response = custom_pages_client.get("/route_OhNo")
assert response.status == 404