diff --git a/src/poetry/publishing/hash_manager.py b/src/poetry/publishing/hash_manager.py new file mode 100644 index 00000000000..03fe010d4d7 --- /dev/null +++ b/src/poetry/publishing/hash_manager.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import hashlib +import io + +from contextlib import suppress +from typing import TYPE_CHECKING +from typing import NamedTuple + + +if TYPE_CHECKING: + from pathlib import Path + + +class Hexdigest(NamedTuple): + md5: str | None + sha256: str | None + blake2_256: str | None + + +class HashManager: + def __init__(self) -> None: + self._sha2_hasher = hashlib.sha256() + + self._md5_hasher = None + with suppress(ValueError): + # FIPS mode disables MD5 + self._md5_hasher = hashlib.md5() + + self._blake_hasher = None + with suppress(ValueError, TypeError): + # FIPS mode disables blake2 + self._blake_hasher = hashlib.blake2b(digest_size=256 // 8) + + def _md5_update(self, content: bytes) -> None: + if self._md5_hasher is not None: + self._md5_hasher.update(content) + + def _md5_hexdigest(self) -> str | None: + if self._md5_hasher is not None: + return self._md5_hasher.hexdigest() + return None + + def _blake_update(self, content: bytes) -> None: + if self._blake_hasher is not None: + self._blake_hasher.update(content) + + def _blake_hexdigest(self) -> str | None: + if self._blake_hasher is not None: + return self._blake_hasher.hexdigest() + return None + + def hash(self, file: Path) -> None: + with file.open("rb") as fp: + for content in iter(lambda: fp.read(io.DEFAULT_BUFFER_SIZE), b""): + self._md5_update(content) + self._sha2_hasher.update(content) + self._blake_update(content) + + def hexdigest(self) -> Hexdigest: + return Hexdigest( + self._md5_hexdigest(), + self._sha2_hasher.hexdigest(), + self._blake_hexdigest(), + ) diff --git a/src/poetry/publishing/uploader.py b/src/poetry/publishing/uploader.py index 4a1ae3ace65..c76ed052520 100644 --- a/src/poetry/publishing/uploader.py +++ b/src/poetry/publishing/uploader.py @@ -1,13 +1,8 @@ from __future__ import annotations -import hashlib -import io - -from contextlib import suppress from pathlib import Path from typing import TYPE_CHECKING from typing import Any -from typing import NamedTuple import requests @@ -20,6 +15,7 @@ from requests_toolbelt.multipart import MultipartEncoderMonitor from poetry.__version__ import __version__ +from poetry.publishing.hash_manager import HashManager from poetry.utils.constants import REQUESTS_TIMEOUT from poetry.utils.patterns import wheel_file_re @@ -50,59 +46,6 @@ def __init__(self, error: ConnectionError | HTTPError | str) -> None: super().__init__(message) -class Hexdigest(NamedTuple): - md5: str | None - sha256: str | None - blake2_256: str | None - - -class HashManager: - def __init__(self) -> None: - self._sha2_hasher = hashlib.sha256() - - self._md5_hasher = None - with suppress(ValueError): - # FIPS mode disables MD5 - self._md5_hasher = hashlib.md5() - - self._blake_hasher = None - with suppress(ValueError, TypeError): - # FIPS mode disables blake2 - self._blake_hasher = hashlib.blake2b(digest_size=256 // 8) - - def _md5_update(self, content: bytes) -> None: - if self._md5_hasher is not None: - self._md5_hasher.update(content) - - def _md5_hexdigest(self) -> str | None: - if self._md5_hasher is not None: - return self._md5_hasher.hexdigest() - return None - - def _blake_update(self, content: bytes) -> None: - if self._blake_hasher is not None: - self._blake_hasher.update(content) - - def _blake_hexdigest(self) -> str | None: - if self._blake_hasher is not None: - return self._blake_hasher.hexdigest() - return None - - def hash(self, file: Path) -> None: - with file.open("rb") as fp: - for content in iter(lambda: fp.read(io.DEFAULT_BUFFER_SIZE), b""): - self._md5_update(content) - self._sha2_hasher.update(content) - self._blake_update(content) - - def hexdigest(self) -> Hexdigest: - return Hexdigest( - self._md5_hexdigest(), - self._sha2_hasher.hexdigest(), - self._blake_hexdigest(), - ) - - class Uploader: def __init__(self, poetry: Poetry, io: IO, dist_dir: Path | None = None) -> None: self._poetry = poetry diff --git a/tests/publishing/test_file_hashes.py b/tests/publishing/test_hash_manager.py similarity index 97% rename from tests/publishing/test_file_hashes.py rename to tests/publishing/test_hash_manager.py index a8861dbea77..773bb05437c 100644 --- a/tests/publishing/test_file_hashes.py +++ b/tests/publishing/test_hash_manager.py @@ -5,7 +5,7 @@ import pytest -from poetry.publishing.uploader import HashManager +from poetry.publishing.hash_manager import HashManager if TYPE_CHECKING: