Skip to content

Expose ammonia's url_relative policy via url_relative kwarg#131

Merged
messense merged 1 commit into
messense:mainfrom
gghez:expose-url-relative
Jun 22, 2026
Merged

Expose ammonia's url_relative policy via url_relative kwarg#131
messense merged 1 commit into
messense:mainfrom
gghez:expose-url-relative

Conversation

@gghez

@gghez gghez commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Closes #129.

Adds a url_relative keyword argument to clean() and Cleaner() mapping to
ammonia's UrlRelative enum, so relative URLs in href / src /
<object data=...> can be passed through (default), denied, rewritten against a
base or root URL, or rewritten by a custom callback.

API

Python value ammonia
None (default) / "pass_through" PassThrough
"deny" Deny
("rewrite_with_base", base_url) RewriteWithBase
("rewrite_with_root", root_url, path) RewriteWithRoot
`Callable[[str], str None]`
nh3.clean('<a href="/foo">x</a>', url_relative="deny")
# '<a rel="noopener noreferrer">x</a>'

nh3.clean('<a href="/foo">x</a>', url_relative=("rewrite_with_base", "https://example.com"))
# '<a href="https://example.com/foo" rel="noopener noreferrer">x</a>'

nh3.clean('<img src="/a.png">', url_relative=lambda url: f"https://cdn.example.com{url}")
# '<img src="https://cdn.example.com/a.png">'

Notes

  • Default is unchanged (None ⇒ ammonia's PassThrough); no behaviour change for existing callers.
  • Input is validated eagerly at construction: ValueError for an unknown mode
    string, malformed tuple, or unparseable URL; TypeError for an unsupported type.
  • A custom callback that raises (or returns a non-str/non-None value) strips
    the URL and reports the error via sys.unraisablehook, keeping clean()
    infallible — mirroring attribute_filter's error handling.

Tests

Adds 9 tests (deny / rewrite_with_base / rewrite_with_root / custom replace /
custom strip / custom raising / explicit pass_through / invalid inputs /
reusable Cleaner), plus doctest examples on clean. nh3.pyi stubs updated.

Adds a `url_relative` keyword argument to `clean()` and `Cleaner()` that maps
to ammonia's `UrlRelative` enum, so relative URLs in href/src/<object data> can
be passed through (default), denied, rewritten against a base or root URL, or
rewritten by a custom callback.

Accepted values:
- None (default) / "pass_through" -> PassThrough
- "deny" -> Deny
- ("rewrite_with_base", base_url) -> RewriteWithBase
- ("rewrite_with_root", root_url, path) -> RewriteWithRoot
- callable (url) -> str | None -> Custom

Input is validated eagerly at construction (ValueError for bad mode/URL/tuple,
TypeError for unsupported types). A custom callback that raises or returns a
non-str/non-None value strips the URL and reports the error via
sys.unraisablehook, keeping clean() infallible.

Closes messense#129
@messense
messense merged commit 788cee2 into messense:main Jun 22, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose ammonia's url_relative policy (Deny / RewriteWithBase / RewriteWithRoot / Custom)

2 participants