diff --git a/pyartifactory/__init__.py b/pyartifactory/__init__.py index 621e7f7..f01e9b7 100644 --- a/pyartifactory/__init__.py +++ b/pyartifactory/__init__.py @@ -1,6 +1,7 @@ """ Import all object definitions here. """ + from __future__ import annotations import contextlib diff --git a/pyartifactory/exception.py b/pyartifactory/exception.py index 086ed1e..263d679 100644 --- a/pyartifactory/exception.py +++ b/pyartifactory/exception.py @@ -1,6 +1,7 @@ """ Definition of all exceptions. """ + from __future__ import annotations diff --git a/pyartifactory/models/__init__.py b/pyartifactory/models/__init__.py index 9d2bca8..58a7033 100644 --- a/pyartifactory/models/__init__.py +++ b/pyartifactory/models/__init__.py @@ -1,6 +1,7 @@ """ Import all models here. """ + from __future__ import annotations from typing import Union diff --git a/pyartifactory/models/artifact.py b/pyartifactory/models/artifact.py index effe21b..b725357 100644 --- a/pyartifactory/models/artifact.py +++ b/pyartifactory/models/artifact.py @@ -1,6 +1,7 @@ """ Definition of all artifact models. """ + from __future__ import annotations import hashlib diff --git a/pyartifactory/models/auth.py b/pyartifactory/models/auth.py index aafa93a..71b1111 100644 --- a/pyartifactory/models/auth.py +++ b/pyartifactory/models/auth.py @@ -1,6 +1,7 @@ """ Definition of all auth models. """ + from __future__ import annotations from typing import Optional, Tuple, Union diff --git a/pyartifactory/models/build.py b/pyartifactory/models/build.py index 67bd773..35dfe94 100644 --- a/pyartifactory/models/build.py +++ b/pyartifactory/models/build.py @@ -1,6 +1,7 @@ """ Definition of all build models. """ + from __future__ import annotations from typing import Dict, List, Optional diff --git a/pyartifactory/models/group.py b/pyartifactory/models/group.py index d9d2864..abb881e 100644 --- a/pyartifactory/models/group.py +++ b/pyartifactory/models/group.py @@ -1,6 +1,7 @@ """ Definition of all group models. """ + from __future__ import annotations from typing import List, Optional diff --git a/pyartifactory/models/permission.py b/pyartifactory/models/permission.py index d675a93..80e61df 100644 --- a/pyartifactory/models/permission.py +++ b/pyartifactory/models/permission.py @@ -1,6 +1,7 @@ """ Definition of all permission models. """ + from __future__ import annotations from enum import Enum diff --git a/pyartifactory/models/repository.py b/pyartifactory/models/repository.py index ca5c097..c7c1284 100644 --- a/pyartifactory/models/repository.py +++ b/pyartifactory/models/repository.py @@ -1,6 +1,7 @@ """ Definition of all repository models. """ + from __future__ import annotations from enum import Enum diff --git a/pyartifactory/models/user.py b/pyartifactory/models/user.py index 1fb7abb..d7bee35 100644 --- a/pyartifactory/models/user.py +++ b/pyartifactory/models/user.py @@ -1,6 +1,7 @@ """ Definition of all user related models. """ + from __future__ import annotations from datetime import datetime diff --git a/pyartifactory/objects/artifact.py b/pyartifactory/objects/artifact.py index 0dac9ab..de92800 100644 --- a/pyartifactory/objects/artifact.py +++ b/pyartifactory/objects/artifact.py @@ -94,7 +94,7 @@ def deploy( if local_file.is_dir(): for root, _, files in os.walk(local_file.as_posix()): - new_root = f"{artifact_folder}/{root[len(local_file.as_posix()):]}" + new_root = f"{artifact_folder}/{root[len(local_file.as_posix()) :]}" for file in files: self.deploy(Path(f"{root}/{file}"), Path(f"{new_root}/{file}"), properties, checksum_enabled) else: @@ -151,7 +151,6 @@ def _download(self, artifact_path: str, local_directory_path: Optional[Path] = N :param local_directory_path: Local path to where the artifact will be downloaded :return: File name """ - artifact_path = artifact_path.lstrip("/") local_filename = artifact_path.split("/")[-1] if local_directory_path: @@ -160,14 +159,28 @@ def _download(self, artifact_path: str, local_directory_path: Optional[Path] = N else: local_file_full_path = Path(local_filename) - artifact_path_url = urllib.parse.quote(artifact_path) - with self._get(f"{artifact_path_url}", stream=True) as response, local_file_full_path.open("wb") as file: - for chunk in response.iter_content(chunk_size=8192): - if chunk: # filter out keep-alive new chunks - file.write(chunk) + with local_file_full_path.open("wb") as file: + for chunk in self.stream(artifact_path): + file.write(chunk) logger.debug("Artifact %s successfully downloaded", local_filename) return local_file_full_path + def stream(self, artifact_path: str, chunk_size: int = 8192) -> Iterator[bytes]: + """ + Open an Iterator stream of an artifact (file). + :param artifact_path: Path to file in Artifactory + :param chunk_size: Size of bytes in a chunk + :return: Iterator of byte chunks + """ + artifact_path = artifact_path.lstrip("/") + + artifact_path_url = urllib.parse.quote(artifact_path) + # Filter out keep-alive new chunks with a filter function + return filter( + lambda chunk: chunk, + self._get(f"{artifact_path_url}", stream=True).iter_content(chunk_size=chunk_size), + ) + def download(self, artifact_path: str, local_directory_path: str = ".") -> Path: """ Download artifact (file or directory) into local directory. diff --git a/pyartifactory/objects/object.py b/pyartifactory/objects/object.py index cb5c9e0..13cc8d1 100644 --- a/pyartifactory/objects/object.py +++ b/pyartifactory/objects/object.py @@ -1,6 +1,7 @@ """ Definition of artifactory base object. """ + from __future__ import annotations from typing import Optional, Tuple diff --git a/pyartifactory/utils.py b/pyartifactory/utils.py index 875fc0b..ae9ca29 100644 --- a/pyartifactory/utils.py +++ b/pyartifactory/utils.py @@ -1,6 +1,7 @@ """ Definition of all utils. """ + from __future__ import annotations from typing import Any diff --git a/pyproject.toml b/pyproject.toml index f12c59a..41c67b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,8 +52,8 @@ pytest-md = "^0.2.0" pytest-emoji = "^0.2.0" [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.isort] multi_line_output = 3 diff --git a/tests/test_artifacts.py b/tests/test_artifacts.py index 8143012..f9e5bda 100644 --- a/tests/test_artifacts.py +++ b/tests/test_artifacts.py @@ -197,6 +197,27 @@ def test_download_artifact_success(tmp_path): assert artifact.is_file() +@responses.activate +def test_stream_artifact_success(): + artifact_name = ARTIFACT_PATH.split("/")[1] + responses.add( + responses.GET, + f"{URL}/api/storage/{ARTIFACT_PATH}", + status=200, + json=FILE_INFO_RESPONSE, + ) + responses.add(responses.GET, f"{URL}/{ARTIFACT_PATH}", json=artifact_name, status=200) + + artifactory = ArtifactoryArtifact(AuthModel(url=URL, auth=AUTH)) + artifact_stream = artifactory.stream(ARTIFACT_PATH) + + text = bytearray() + for chunk in artifact_stream: + text.extend(chunk) + + assert len(text) == len(f'"{artifact_name}"'.encode("utf-8")) + + @pytest.mark.parametrize( "requested_path", [ARTIFACT_REPO, f"{ARTIFACT_REPO}/", f"/{ARTIFACT_REPO}", f"/{ARTIFACT_REPO}/"],