Add an is_url_safe() function
- ID
3bc2ade- date
2025-12-05 13:17:49+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
a9bcefa- message
Add an `is_url_safe()` function Closes #3- changed files
4 files, 35 additions, 1 deletion
Changed files
CHANGELOG.md (1112) → CHANGELOG.md (1221)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40e8dbe..c4f077d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+## v10 - 2025-12-05
+
+Add a new `is_url_safe()` function for checking if a path can be safely used in a URL.
+
## v9 - 2025-12-05
This adds three models to `chives.media`: `ImageEntity`, `VideoEntity`, and `ImageEntity`.
src/chives/__init__.py (390) → src/chives/__init__.py (391)
diff --git a/src/chives/__init__.py b/src/chives/__init__.py
index a6f3dde..4a7fae3 100644
--- a/src/chives/__init__.py
+++ b/src/chives/__init__.py
@@ -11,4 +11,4 @@ I share across multiple sites.
"""
-__version__ = "9"
+__version__ = "10"
src/chives/urls.py (3204) → src/chives/urls.py (3470)
diff --git a/src/chives/urls.py b/src/chives/urls.py
index 7f161b0..93589d1 100644
--- a/src/chives/urls.py
+++ b/src/chives/urls.py
@@ -1,5 +1,6 @@
"""Code for manipulating and tidying URLs."""
+from pathlib import Path
import re
from typing import TypedDict
@@ -9,6 +10,8 @@ import hyperlink
__all__ = [
"clean_youtube_url",
+ "is_mastodon_host",
+ "is_url_safe",
"parse_mastodon_post_url",
"parse_tumblr_post_url",
]
@@ -126,3 +129,11 @@ def parse_tumblr_post_url(url: str) -> tuple[str, str]:
return u.host.replace(".tumblr.com", ""), u.path[1]
raise ValueError("Cannot parse Tumblr URL!") # pragma: no cover
+
+
+def is_url_safe(path: str | Path) -> bool:
+ """
+ Returns True if a path is safe to use in a URL, False otherwise.
+ """
+ p = str(path)
+ return not ("?" in p or "#" in p or "%" in p)
tests/test_urls.py (3685) → tests/test_urls.py (4206)
diff --git a/tests/test_urls.py b/tests/test_urls.py
index 5666796..593903c 100644
--- a/tests/test_urls.py
+++ b/tests/test_urls.py
@@ -1,11 +1,14 @@
"""Tests for `chives.urls`."""
+from pathlib import Path
+
import pytest
from vcr.cassette import Cassette
from chives.urls import (
clean_youtube_url,
is_mastodon_host,
+ is_url_safe,
parse_mastodon_post_url,
parse_tumblr_post_url,
)
@@ -128,3 +131,19 @@ class TestIsMastodonHost:
Other websites are not Mastodon servers.
"""
assert not is_mastodon_host(host)
+
+
+class TestIsUrlSafe:
+ """
+ Tests for `is_url_safe`.
+ """
+
+ @pytest.mark.parametrize("path", ["example.txt", Path("a/b/cat.jpg")])
+ def test_safe(self, path: str | Path) -> None:
+ """Paths which are URL safe."""
+ assert is_url_safe(path)
+
+ @pytest.mark.parametrize("path", ["is it?", Path("cat%c.jpg"), "a#b"])
+ def test_unsafe(self, path: str | Path) -> None:
+ """Paths which are not URL safe."""
+ assert not is_url_safe(path)