diff --git a/src/poetry/installation/chooser.py b/src/poetry/installation/chooser.py index b484504ed9a..4601189d16d 100644 --- a/src/poetry/installation/chooser.py +++ b/src/poetry/installation/chooser.py @@ -8,6 +8,7 @@ from poetry.config.config import Config from poetry.config.config import PackageFilterPolicy +from poetry.repositories.http_repository import HTTPRepository from poetry.utils.wheel import Wheel @@ -103,6 +104,12 @@ def _get_links(self, package: Package) -> list[Link]: assert link.hash_name is not None h = link.hash_name + ":" + link.hash + if ( + h not in hashes + and link.hash_name not in ("sha256", "sha384", "sha512") + and isinstance(repository, HTTPRepository) + ): + h = repository.calculate_sha256(link) or h if h not in hashes: logger.debug( "Skipping %s as %s checksum does not match expected value", diff --git a/src/poetry/repositories/http_repository.py b/src/poetry/repositories/http_repository.py index 9427d72bfe7..ebf8b1a4d77 100644 --- a/src/poetry/repositories/http_repository.py +++ b/src/poetry/repositories/http_repository.py @@ -226,24 +226,7 @@ def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any] and link.hash_name not in ("sha256", "sha384", "sha512") and hasattr(hashlib, link.hash_name) ): - with self._cached_or_downloaded_file(link) as filepath: - known_hash = ( - getattr(hashlib, link.hash_name)() if link.hash_name else None - ) - required_hash = hashlib.sha256() - - chunksize = 4096 - with filepath.open("rb") as f: - while True: - chunk = f.read(chunksize) - if not chunk: - break - if known_hash: - known_hash.update(chunk) - required_hash.update(chunk) - - if not known_hash or known_hash.hexdigest() == link.hash: - file_hash = f"{required_hash.name}:{required_hash.hexdigest()}" + file_hash = self.calculate_sha256(link) or file_hash files.append({"file": link.filename, "hash": file_hash}) @@ -257,6 +240,25 @@ def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any] return data.asdict() + def calculate_sha256(self, link: Link) -> str | None: + with self._cached_or_downloaded_file(link) as filepath: + known_hash = getattr(hashlib, link.hash_name)() if link.hash_name else None + required_hash = hashlib.sha256() + + chunksize = 4096 + with filepath.open("rb") as f: + while True: + chunk = f.read(chunksize) + if not chunk: + break + if known_hash: + known_hash.update(chunk) + required_hash.update(chunk) + + if not known_hash or known_hash.hexdigest() == link.hash: + return f"{required_hash.name}:{required_hash.hexdigest()}" + return None + def _get_response(self, endpoint: str) -> requests.Response | None: url = self._url + endpoint try: diff --git a/tests/helpers.py b/tests/helpers.py index eefe9c74c1a..3dd5b1d8915 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -36,6 +36,7 @@ from poetry.installation.operations.operation import Operation from poetry.poetry import Poetry + from poetry.utils.authenticator import Authenticator FIXTURE_PATH = Path(__file__).parent / "fixtures" @@ -120,7 +121,7 @@ def mock_clone( return MockDulwichRepo(dest) -def mock_download(url: str, dest: Path) -> None: +def mock_download(url: str, dest: Path, session: Authenticator | None = None) -> None: parts = urllib.parse.urlparse(url) fixture = FIXTURE_PATH / parts.path.lstrip("/") diff --git a/tests/installation/test_chooser.py b/tests/installation/test_chooser.py index fbd3861ad05..6c9930f899a 100644 --- a/tests/installation/test_chooser.py +++ b/tests/installation/test_chooser.py @@ -405,3 +405,25 @@ def test_chooser_throws_an_error_if_package_hashes_do_not_match( with pytest.raises(RuntimeError) as e: chooser.choose_for(package) assert files[0]["hash"] in str(e) + + +@pytest.mark.usefixtures("mock_legacy") +def test_chooser_md5_remote_fallback_to_sha256_inline_calculation( + env: MockEnv, pool: RepositoryPool +) -> None: + chooser = Chooser(pool, env) + package = Package( + "demo", + "0.1.0", + source_type="legacy", + source_reference="foo", + source_url="https://foo.bar/simple/", + ) + package.files = [ + { + "hash": "sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad", # noqa: E501 + "filename": "demo-0.1.0.tar.gz", + } + ] + res = chooser.choose_for(package) + assert res.filename == "demo-0.1.0.tar.gz" diff --git a/tests/repositories/fixtures/legacy/demo.html b/tests/repositories/fixtures/legacy/demo.html new file mode 100644 index 00000000000..ae9506cd5a1 --- /dev/null +++ b/tests/repositories/fixtures/legacy/demo.html @@ -0,0 +1,6 @@ + +Simple Index + +demo-0.1.0.tar.gz
+ +