From 9b1bdf583b3c857524283bd4d5ea66b5aecb8855 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Sat, 7 Aug 2021 10:26:06 +0800 Subject: [PATCH] Move NameTuple method out of class Suppport for defining methods on a typing.NameTuple was added in 3.6.1, so this fixes compatibility to 3.6.0. --- news/10280.bugfix.rst | 1 + src/pip/_internal/models/link.py | 52 ++++++++++++++++---------------- 2 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 news/10280.bugfix.rst diff --git a/news/10280.bugfix.rst b/news/10280.bugfix.rst new file mode 100644 index 00000000000..1439469639c --- /dev/null +++ b/news/10280.bugfix.rst @@ -0,0 +1 @@ +Fix 3.6.0 compatibility in link comparison logic. diff --git a/src/pip/_internal/models/link.py b/src/pip/_internal/models/link.py index eb656fa0143..9ef1ca36865 100644 --- a/src/pip/_internal/models/link.py +++ b/src/pip/_internal/models/link.py @@ -256,33 +256,33 @@ class _CleanResult(NamedTuple): subdirectory: str hashes: Dict[str, str] - @classmethod - def from_link(cls, link: Link) -> "_CleanResult": - parsed = link._parsed_url - netloc = parsed.netloc.rsplit("@", 1)[-1] - # According to RFC 8089, an empty host in file: means localhost. - if parsed.scheme == "file" and not netloc: - netloc = "localhost" - fragment = urllib.parse.parse_qs(parsed.fragment) - if "egg" in fragment: - logger.debug("Ignoring egg= fragment in %s", link) - try: - # If there are multiple subdirectory values, use the first one. - # This matches the behavior of Link.subdirectory_fragment. - subdirectory = fragment["subdirectory"][0] - except (IndexError, KeyError): - subdirectory = "" - # If there are multiple hash values under the same algorithm, use the - # first one. This matches the behavior of Link.hash_value. - hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment} - return cls( - parsed=parsed._replace(netloc=netloc, query="", fragment=""), - query=urllib.parse.parse_qs(parsed.query), - subdirectory=subdirectory, - hashes=hashes, - ) + +def _clean_link(link: Link) -> _CleanResult: + parsed = link._parsed_url + netloc = parsed.netloc.rsplit("@", 1)[-1] + # According to RFC 8089, an empty host in file: means localhost. + if parsed.scheme == "file" and not netloc: + netloc = "localhost" + fragment = urllib.parse.parse_qs(parsed.fragment) + if "egg" in fragment: + logger.debug("Ignoring egg= fragment in %s", link) + try: + # If there are multiple subdirectory values, use the first one. + # This matches the behavior of Link.subdirectory_fragment. + subdirectory = fragment["subdirectory"][0] + except (IndexError, KeyError): + subdirectory = "" + # If there are multiple hash values under the same algorithm, use the + # first one. This matches the behavior of Link.hash_value. + hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment} + return _CleanResult( + parsed=parsed._replace(netloc=netloc, query="", fragment=""), + query=urllib.parse.parse_qs(parsed.query), + subdirectory=subdirectory, + hashes=hashes, + ) @functools.lru_cache(maxsize=None) def links_equivalent(link1: Link, link2: Link) -> bool: - return _CleanResult.from_link(link1) == _CleanResult.from_link(link2) + return _clean_link(link1) == _clean_link(link2)