diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index 1a4de68769a..63085a760c6 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -1,4 +1,5 @@ import cgi +import hashlib import re import urllib.parse import warnings @@ -27,6 +28,8 @@ from poetry.core.semver.version_range import VersionRange from poetry.locations import REPOSITORY_CACHE_DIR from poetry.utils.helpers import canonicalize_name +from poetry.utils.helpers import download_file +from poetry.utils.helpers import temporary_directory from poetry.utils.patterns import wheel_file_re from ..config.config import Config @@ -374,10 +377,37 @@ def _get_release_info(self, name: str, version: str) -> dict: ): urls["sdist"].append(link.url) - h = link.hash - if h: - h = link.hash_name + ":" + link.hash - files.append({"file": link.filename, "hash": h}) + file_hash = "{}:{}".format(link.hash_name, link.hash) if link.hash else None + + if not link.hash or ( + link.hash_name not in ("sha256", "sha384", "sha512") + and hasattr(hashlib, link.hash_name) + ): + with temporary_directory() as temp_dir: + filepath = Path(temp_dir) / link.filename + self._download(link.url, str(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 = "{}:{}".format( + required_hash.name, required_hash.hexdigest() + ) + + files.append({"file": link.filename, "hash": file_hash}) data.files = files @@ -415,3 +445,6 @@ def _get(self, endpoint: str) -> Optional[Page]: ) return Page(response.url, response.content, response.headers) + + def _download(self, url, dest): # type: (str, str) -> None + return download_file(url, dest, session=self.session) diff --git a/tests/repositories/fixtures/legacy/ipython.html b/tests/repositories/fixtures/legacy/ipython.html index cbdc19e5850..5b61c92d784 100644 --- a/tests/repositories/fixtures/legacy/ipython.html +++ b/tests/repositories/fixtures/legacy/ipython.html @@ -5,10 +5,10 @@

Links for ipython

- ipython-5.7.0-py2-none-any.whl
+ ipython-5.7.0-py2-none-any.whl
ipython-5.7.0-py3-none-any.whl
ipython-5.7.0.tar.gz
- ipython-7.5.0-py3-none-any.whl
+ ipython-7.5.0-py3-none-any.whl
ipython-7.5.0.tar.gz
diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 8ced98e4e44..5f49110d975 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -273,7 +273,7 @@ def test_get_package_retrieves_non_sha256_hashes(): expected = [ { "file": "ipython-7.5.0-py3-none-any.whl", - "hash": "md5:dbdc53e3918f28fa335a173432402a00", + "hash": "sha256:78aea20b7991823f6a32d55f4e963a61590820e43f666ad95ad07c7f0c704efa", }, { "file": "ipython-7.5.0.tar.gz", @@ -284,12 +284,40 @@ def test_get_package_retrieves_non_sha256_hashes(): assert expected == package.files +def test_get_package_retrieves_non_sha256_hashes_mismatching_known_hash(): + repo = MockRepository() + + package = repo.package("ipython", "5.7.0") + + expected = [ + { + "file": "ipython-5.7.0-py2-none-any.whl", + "hash": "md5:a10a802ef98da741cd6f4f6289d47ba7", + }, + { + "file": "ipython-5.7.0-py3-none-any.whl", + "hash": "sha256:fc0464e68f9c65cd8c453474b4175432cc29ecb6c83775baedf6dbfcee9275ab", + }, + { + "file": "ipython-5.7.0.tar.gz", + "hash": "sha256:8db43a7fb7619037c98626613ff08d03dda9d5d12c84814a4504c78c0da8323c", + }, + ] + + assert expected == package.files + + def test_get_package_retrieves_packages_with_no_hashes(): repo = MockRepository() package = repo.package("jupyter", "1.0.0") - assert [] == package.files + assert [ + { + "file": "jupyter-1.0.0.tar.gz", + "hash": "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f", + } + ] == package.files class MockHttpRepository(LegacyRepository):