Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ available in this package.
import asyncio
from datetime import datetime

import datastar_py
from datastar_py import ServerSentEventGenerator as SSE, attribute_generator as data
from datastar_py.quart import datastar_response, read_signals
from quart import Quart
Expand All @@ -43,7 +44,7 @@ def index():
return f"""
<html>
<head>
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script>
<script type="module" src="{datastar_py.url('v1.0.0-RC.7')}"></script>
Comment thread
kdheepak marked this conversation as resolved.
</head>
<body {data.init("@get('/updates')")}>
<span id="currentTime"></span><br>
Expand Down
30 changes: 29 additions & 1 deletion src/datastar_py/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
from __future__ import annotations

import json
import re
from collections.abc import Mapping
from typing import Any

from .attributes import action_generator, attribute_generator
from .sse import SSE_HEADERS, ServerSentEventGenerator

__all__ = ["SSE_HEADERS", "ServerSentEventGenerator", "action_generator", "attribute_generator"]
__all__ = [
"SSE_HEADERS",
"ServerSentEventGenerator",
"action_generator",
"attribute_generator",
"url",
]


def url(version: str = "") -> str:
Comment thread
kdheepak marked this conversation as resolved.
"""Return the jsDelivr URL for the Datastar JavaScript module.

Accepts major, minor, or exact versions with an optional ``v`` prefix.
Major, minor, and omitted versions can resolve to the latest release.
"""
if not isinstance(version, str):
raise TypeError("version must be a string or None")
ref = ""
if version != "":
if not re.fullmatch(
r"v?[0-9]+(?:\.[0-9]+(?:\.[0-9]+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)?)?",
version.strip(),
):
raise ValueError(
"version must be a valid jsdelivr compatible string, such as 'v1', '1.0', or '1.0.4'"
)
ref = f"@v{version.strip().removeprefix('v')}"
return f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js"


def _read_signals(
Expand Down
57 changes: 57 additions & 0 deletions tests/test_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import pytest

import datastar_py


@pytest.mark.parametrize(
("version", "ref"),
(
("", ""),
("1", "@v1"),
("v1", "@v1"),
("1.0", "@v1.0"),
("v1.0", "@v1.0"),
("1.0.4", "@v1.0.4"),
("v1.0.4", "@v1.0.4"),
("1.0.0-RC.7", "@v1.0.0-RC.7"),
("v1.0.0-RC.7", "@v1.0.0-RC.7"),
),
)
def test_url_versions(version, ref):
assert datastar_py.url(version) == (
f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js"
)


def test_url_default():
assert datastar_py.url() == (
"https://cdn.jsdelivr.net/gh/starfederation/datastar/bundles/datastar.js"
)


@pytest.mark.parametrize(
"version",
(
"v",
"v1.",
"1..0",
"1.0.4.1",
"1-RC.7",
"1.0-RC.7",
"1.0.4-",
"1.0.4-RC.",
"latest",
"v1/something",
"v1?x=1",
"v1#fragment",
),
)
def test_url_rejects_invalid_versions(version):
with pytest.raises(ValueError, match="version"):
datastar_py.url(version)


@pytest.mark.parametrize("version", (1, 1.0, True, b"v1", [], {}))
def test_url_rejects_invalid_types(version):
with pytest.raises(TypeError, match="version must be a string or None"):
datastar_py.url(version)
Loading