hyperlink is a pure-Python library that provides a featureful, immutable, and semantically correct URL object, adhering to RFC 3986 and RFC 3987 for URIs and IRIs. It emphasizes correctness and ease of use, providing a robust model for URL parsing, construction, and transformation. The current stable version is 21.0.0, with major releases occurring approximately annually.
Install & Compatibility
Where this runs
tested against v21.0.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.925 runs
installs and imports cleanly · install 0.0s · import 0.048s · 18.9MB
glibcpy 3.10–3.925 runs
installs and imports cleanly · install 1.7s · import 0.043s · 19MB
17MB installed
● package 17MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
URL
✓ from hyperlink import URL
The lower-level URL class, also available as hyperlink.EncodedURL. Requires manual handling of encoding for special characters.
parse
✓ from hyperlink import parse
The recommended top-level convenience function to parse a textual URL into a DecodedURL object.
DecodedURL
✓ from hyperlink import DecodedURL
The recommended URL type for most operations, automatically handling encoding and decoding of components.
This quickstart demonstrates parsing a URL from a string, creating modified copies using the immutable API, navigating paths, accessing query parameters, and constructing a URL from individual components.
from hyperlink import parse
# Parse a URL from text
url = parse(u'http://github.com/python-hyper/hyperlink?utm_source=readthedocs')
print(f"Original URL: {url.to_text()}")
# Create a new URL by replacing components (URL objects are immutable)
secure_url = url.replace(scheme=u'https', port=443)
print(f"Secure URL: {secure_url.to_text()}")
# Navigate to a child path or resolve relative paths
org_url = secure_url.click(u'.') # Clicks to the root of the domain
print(f"Org URL: {org_url.to_text()}")
# Access query parameters
utm_source = secure_url.get(u'utm_source')[0]
print(f"UTM Source: {utm_source}")
# Build a URL from components
new_url_from_parts = URL(scheme=u'https', host=u'example.com', path=[u'api', u'v1'], query=((u'id', u'123'),))
print(f"Built from parts: {new_url_from_parts.to_text()}")
Debug
Known issues
gotchahyperlink's URL objects are immutable. All 'modifying' methods (e.g., `replace()`, `add()`, `drop()`, `set()`) return a *new* URL object with the changes, rather than modifying the object in place.fixAlways assign the result of modifying methods to a new variable (e.g., `new_url = old_url.replace(...)`).
affects: All versions
gotchaThe `hyperlink.parse()` function, by default (since v18.0.0), returns a `DecodedURL` which automatically handles encoding/decoding of URL components. Directly instantiating `hyperlink.URL` (or `hyperlink.EncodedURL`) requires manual handling of URL-reserved characters, which can easily lead to incorrect encoding if not carefully managed.fixFor most common use cases, prefer using `hyperlink.parse()` or `hyperlink.DecodedURL` to leverage automatic encoding/decoding. Use `hyperlink.URL` (or `hyperlink.EncodedURL`) only when explicit control over encoding is required.
affects: v18.0.0 and later
breakingIn version 19.0.0, the library changed how 'equals sign' characters are handled in query parameter *values*. Previously, they were escaped; now, they are not, aligning with standard web form encoding. This could be a breaking change if your system relied on the old escaping behavior for parsing or comparison.fixIf existing code expects the old escaping behavior for query parameter values, it may need to be updated to account for the new standard or apply custom encoding/decoding.
affects: v19.0.0 onwards
breakingIn version 20.0.0, bug fixes related to hidden state on `URL` objects mean that constructor arguments like `rooted` and `uses_netloc` may be ignored if applying them would result in an invalid or unparseable URL. The object prioritizes validity over literal argument adherence in such cases.fixBe aware that the `URL` constructor may internally adjust or ignore certain arguments to maintain RFC compliance and validity. Always inspect the resulting `URL` object if relying on specific argument values.
affects: v20.0.0 onwards
gotchaThe 'URL' class, like other components of the 'hyperlink' library, must be explicitly imported (e.g., 'from hyperlink import URL') or accessed via the module namespace (e.g., 'hyperlink.URL') before it can be instantiated or used.fixEnsure that the 'URL' class is properly imported at the beginning of your script using 'from hyperlink import URL' or use 'hyperlink.URL(...)' after importing the module as 'import hyperlink'.
affects: All versions
gotchaThe `hyperlink.URL` class and other top-level components must be explicitly imported before use (e.g., `from hyperlink import URL`). Attempting to use them without proper import will result in a `NameError`.fixEnsure `from hyperlink import URL` or `import hyperlink` and then use `hyperlink.URL` (or other components) in your script.
affects: All versions
Errors
Common errors & fixes
hyperlink._url.URLParseError: port must not be empty: 'http:'
This error occurs when `hyperlink.parse()` attempts to parse a URL string that is syntactically malformed, specifically when a port is expected but not provided or is invalid within the scheme.
fixEnsure the URL string provided to `hyperlink.parse()` adheres to valid URL syntax, especially concerning scheme, host, and port components. If the URL is external input, validate or sanitize it before parsing.
ValueError: invalid scheme: ' http'. Only alphanumeric, "+", "-", and "." allowed. Did you meant to call URL.from_text()?
This `ValueError` is raised by `hyperlink.parse()` when the URL scheme contains invalid characters, such as leading or trailing whitespace, or characters not permitted in a URL scheme.
fixRemove any invalid characters or whitespace from the URL scheme. If you intend to parse a less strict 'text' format rather than a strict URL, consider using `URL.from_text()` if it better fits your use case, although `hyperlink.parse()` is generally more robust for standard URLs.
TypeError: expected str for text, got b''
The `hyperlink.parse()` method expects a string as input, but it received a bytes-like object or another non-string type.
fixConvert the input to a string (e.g., by decoding a bytes object using `.decode('utf-8')`) before passing it to `hyperlink.parse()`. AttributeError: can't set attribute
The `hyperlink.URL` object is immutable, meaning its attributes (like `host`, `scheme`, `path`) cannot be modified directly after creation. Attempts to assign new values to these attributes will result in an `AttributeError`.
fixTo modify a URL, use the provided fluent methods (e.g., `.replace()`, `.set()`, `.add()`) which return a *new* `URL` object with the desired changes, leaving the original object unchanged. For example, to change the host, use `new_url = old_url.set(host='example.com')`.
Audit
Dependencies
idnarequiredUsed for Internationalized Domain Names (IDNA) processing, replacing Python's built-in facilities for stricter and more correct output.