From e0521fd6948dad51a804a320a70174514ef1320e Mon Sep 17 00:00:00 2001 From: itzzdev09 Date: Sat, 26 Sep 2026 20:52:00 +0530 Subject: [PATCH] Accept file:// URLs per RFC 8089 url() rejected file:// URLs like file:///var/www because their authority (netloc) is empty or "localhost", neither of which _validate_netloc accepts as a hostname. RFC 8089 defines both an empty and a "localhost" authority as valid for a local file URL. A file URL with a non-local authority (file://example.com/path) still goes through the normal hostname check, unchanged. --- src/validators/url.py | 28 ++++++++++++++++++---------- tests/test_url.py | 13 +++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/validators/url.py b/src/validators/url.py index a4277e1c..f8d14e66 100644 --- a/src/validators/url.py +++ b/src/validators/url.py @@ -46,6 +46,7 @@ def _validate_scheme(value: str): value # fmt: off in { + "file", "ftp", "ftps", "git", @@ -240,18 +241,25 @@ def url( except ValueError: return False + # RFC 8089: a "file" URL's authority is commonly empty or "localhost", + # neither of which is a hostname `_validate_netloc` would accept. + is_local_file = scheme == "file" and netloc in ("", "localhost") + return ( validate_scheme(scheme) - and _validate_netloc( - netloc, - skip_ipv6_addr, - skip_ipv4_addr, - may_have_port, - simple_host, - consider_tld, - private, - rfc_1034, - rfc_2782, + and ( + is_local_file + or _validate_netloc( + netloc, + skip_ipv6_addr, + skip_ipv4_addr, + may_have_port, + simple_host, + consider_tld, + private, + rfc_1034, + rfc_2782, + ) ) and _validate_optionals(path, query, fragment, strict_query) ) diff --git a/tests/test_url.py b/tests/test_url.py index 2001a1d5..26495842 100644 --- a/tests/test_url.py +++ b/tests/test_url.py @@ -219,3 +219,16 @@ def test_returns_failed_validation_on_invalid_url(value: str): def test_returns_failed_validation_on_invalid_private_url(value: str, private: Optional[bool]): """Test returns failed validation on invalid private url.""" assert isinstance(url(value, private=private), ValidationError) + + +@pytest.mark.parametrize( + "value", + [ + "file:///var/www", + "file:///C:/Windows", + "file://localhost/var/www", + ], +) +def test_returns_string_for_valid_file_url(value: str): + """Test returns true on valid file url. Ref: RFC 8089.""" + assert url(value)