diff --git a/src/hyperlink/_url.py b/src/hyperlink/_url.py index 8797b5c..308d3e6 100644 --- a/src/hyperlink/_url.py +++ b/src/hyperlink/_url.py @@ -1259,11 +1259,10 @@ def __hash__(self): # type: () -> int return hash( ( - self.__class__, self.scheme, self.userinfo, self.host, - self.path, + () if self.path in _ROOT_PATHS else self.path, self.query, self.fragment, self.port, @@ -2379,20 +2378,7 @@ def __ne__(self, other): def __hash__(self): # type: () -> int - return hash( - ( - self.__class__, - self.scheme, - self.userinfo, - self.host, - self.path, - self.query, - self.fragment, - self.port, - self.rooted, - self.uses_netloc, - ) - ) + return hash(self.normalize().to_uri()) # # Begin Twisted Compat Code asURI = to_uri diff --git a/src/hyperlink/test/test_decoded_url.py b/src/hyperlink/test/test_decoded_url.py index 4845257..202c029 100644 --- a/src/hyperlink/test/test_decoded_url.py +++ b/src/hyperlink/test/test_decoded_url.py @@ -172,6 +172,33 @@ def test_equality_and_hashability(self): assert len(durl_map) == 3 + def test_hash_inherited_equality(self): + # type: () -> None + class InheritedURL(DecodedURL): + pass + + url = DecodedURL.from_text("http://example.com/a") + inherited = InheritedURL.from_text("http://example.com/a") + assert url == inherited + assert hash(url) == hash(inherited) + assert {url: "value"}[inherited] == "value" + + def test_hash_equivalent_normalized_urls(self): + # type: () -> None + pairs = [ + ("http://example.com", "http://example.com/"), + ("http://example.com/a", "HTTP://EXAMPLE.COM/a"), + ("http://example.com/a", "http://example.com/b/../a"), + ("http://example.com/~", "http://example.com/%7E"), + ] + for left, right in pairs: + left_url = DecodedURL.from_text(left) + right_url = DecodedURL.from_text(right) + assert left_url == right_url + assert hash(left_url) == hash(right_url) + assert len({left_url, right_url}) == 1 + assert {left_url: "value"}[right_url] == "value" + def test_replace_roundtrip(self): # type: () -> None durl = DecodedURL.from_text(TOTAL_URL) diff --git a/src/hyperlink/test/test_url.py b/src/hyperlink/test/test_url.py index 37c9172..bfbfc29 100644 --- a/src/hyperlink/test/test_url.py +++ b/src/hyperlink/test/test_url.py @@ -1295,6 +1295,26 @@ def test_hash(self): assert hash(URL()) == hash(URL()) # slightly more sanity + def test_hash_inherited_equality(self): + # type: () -> None + class InheritedURL(URL): + pass + + url = URL.from_text("http://example.com/a") + inherited = InheritedURL.from_text("http://example.com/a") + assert url == inherited + assert hash(url) == hash(inherited) + assert {url: "value"}[inherited] == "value" + + def test_hash_equivalent_root_paths(self): + # type: () -> None + without_slash = URL.from_text("http://example.com") + with_slash = URL.from_text("http://example.com/") + assert without_slash == with_slash + assert hash(without_slash) == hash(with_slash) + assert len({without_slash, with_slash}) == 1 + assert {without_slash: "value"}[with_slash] == "value" + def test_dir(self): # type: () -> None url = URL()