From 78ddf35970986231df1e8b53c554c30b3ad6cade Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Sat, 24 Feb 2024 02:51:54 +0100 Subject: [PATCH] normalize source vcs urls This change ensures when referencing source url for package specifications of source type "git" or for VCS dependencies, a normalized version returned at all times. Relates-to: python-poetry/poetry#8999 --- src/poetry/core/packages/specification.py | 13 +++++++- src/poetry/core/packages/vcs_dependency.py | 5 +-- src/poetry/core/vcs/git.py | 2 +- tests/packages/test_specification.py | 38 ++++++++++++++++++++++ tests/packages/test_vcs_dependency.py | 18 ++++++++++ tests/vcs/test_vcs.py | 2 +- 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/poetry/core/packages/specification.py b/src/poetry/core/packages/specification.py index 85b9574e7..995ecf153 100644 --- a/src/poetry/core/packages/specification.py +++ b/src/poetry/core/packages/specification.py @@ -35,7 +35,7 @@ def __init__( self._pretty_name = name self._name = canonicalize_name(name) self._source_type = source_type - self._source_url = source_url + self._source_url = self._normalize_source_url(source_type, source_url) self._source_reference = source_reference self._source_resolved_reference = source_resolved_reference self._source_subdirectory = source_subdirectory @@ -45,6 +45,17 @@ def __init__( self._features = frozenset(canonicalize_name(feature) for feature in features) + @staticmethod + def _normalize_source_url( + source_type: str | None, source_url: str | None + ) -> str | None: + if source_type and source_url and source_type == "git": + from poetry.core.vcs.git import ParsedUrl + + return ParsedUrl.parse(source_url).url + + return source_url + @property def name(self) -> NormalizedName: return self._name diff --git a/src/poetry/core/packages/vcs_dependency.py b/src/poetry/core/packages/vcs_dependency.py index 8297f90e6..37e3a1b90 100644 --- a/src/poetry/core/packages/vcs_dependency.py +++ b/src/poetry/core/packages/vcs_dependency.py @@ -32,7 +32,6 @@ def __init__( # Attributes must be immutable for clone() to be safe! # (For performance reasons, clone only creates a copy instead of a deep copy). self._vcs = vcs - self._source = source self._branch = branch self._tag = tag @@ -47,13 +46,15 @@ def __init__( optional=optional, allows_prereleases=True, source_type=self._vcs.lower(), - source_url=self._source, + source_url=source, source_reference=branch or tag or rev or "HEAD", source_resolved_reference=resolved_rev, source_subdirectory=directory, extras=extras, ) + self._source = self.source_url + @property def vcs(self) -> str: return self._vcs diff --git a/src/poetry/core/vcs/git.py b/src/poetry/core/vcs/git.py index 1d44d5f49..5feed30f8 100644 --- a/src/poetry/core/vcs/git.py +++ b/src/poetry/core/vcs/git.py @@ -119,7 +119,7 @@ def parse(cls, url: str) -> ParsedUrl: if m: groups = m.groupdict() return ParsedUrl( - groups.get("protocol"), + groups.get("protocol", "ssh"), groups.get("resource"), groups.get("pathname"), groups.get("user"), diff --git a/tests/packages/test_specification.py b/tests/packages/test_specification.py index 79ec3052d..63df92de0 100644 --- a/tests/packages/test_specification.py +++ b/tests/packages/test_specification.py @@ -115,3 +115,41 @@ def test_equal_specifications_have_same_hash( assert spec1 == spec2 assert spec2 == spec1 assert hash(spec1) == hash(spec2) + + +@pytest.mark.parametrize( + "source_url,normalized_url", + [ + ("https://github.com/demo/demo.git", "https://github.com/demo/demo.git"), + ("git@github.com:demo/demo.git", "ssh://git@github.com/demo/demo.git"), + ], +) +def test_specification_normalize_source_url_method( + source_url: str, normalized_url: str +) -> None: + assert ( + PackageSpecification._normalize_source_url("git", source_url) == normalized_url + ) + assert ( + PackageSpecification._normalize_source_url("notgit", source_url) == source_url + ) + + +@pytest.mark.parametrize( + "source_url,normalized_url", + [ + ("https://github.com/demo/demo.git", "https://github.com/demo/demo.git"), + ("git@github.com:demo/demo.git", "ssh://git@github.com/demo/demo.git"), + ], +) +def test_specification_uses_normalize_source_url_for_git( + source_url: str, normalized_url: str +) -> None: + assert ( + PackageSpecification( + name="demo", + source_type="git", + source_url=source_url, + ).source_url + == normalized_url + ) diff --git a/tests/packages/test_vcs_dependency.py b/tests/packages/test_vcs_dependency.py index 062cd040e..6b78b874b 100644 --- a/tests/packages/test_vcs_dependency.py +++ b/tests/packages/test_vcs_dependency.py @@ -229,3 +229,21 @@ def test_vcs_dependencies_are_equal_if_resolved_references_match() -> None: ) assert dependency1 == dependency2 + + +@pytest.mark.parametrize( + "source_url,normalized_url", + [ + ("https://github.com/demo/demo.git", "https://github.com/demo/demo.git"), + ("git@github.com:demo/demo.git", "ssh://git@github.com/demo/demo.git"), + ], +) +def test_vcs_source_is_normalized(source_url: str, normalized_url: str) -> None: + dependency = VCSDependency( + name="demo", + vcs="git", + source=source_url, + branch="main", + ) + assert dependency.source == normalized_url + assert dependency.source_url == normalized_url diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py index fa0c88203..0f6dd96f3 100644 --- a/tests/vcs/test_vcs.py +++ b/tests/vcs/test_vcs.py @@ -258,7 +258,7 @@ def test_normalize_url(url: str, normalized: GitUrl) -> None: ), ( "git@github.com:org/repo", - ParsedUrl(None, "github.com", ":org/repo", "git", None, "repo", None), + ParsedUrl("ssh", "github.com", ":org/repo", "git", None, "repo", None), ), ( "git+https://github.com/sdispater/pendulum",