Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyartifactory/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Import all object definitions here.
"""

from __future__ import annotations

import contextlib
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/exception.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all exceptions.
"""

from __future__ import annotations


Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Import all models here.
"""

from __future__ import annotations

from typing import Union
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/artifact.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all artifact models.
"""

from __future__ import annotations

import hashlib
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all auth models.
"""

from __future__ import annotations

from typing import Optional, Tuple, Union
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/build.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all build models.
"""

from __future__ import annotations

from typing import Dict, List, Optional
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/group.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all group models.
"""

from __future__ import annotations

from typing import List, Optional
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/permission.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all permission models.
"""

from __future__ import annotations

from enum import Enum
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all repository models.
"""

from __future__ import annotations

from enum import Enum
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/models/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all user related models.
"""

from __future__ import annotations

from datetime import datetime
Expand Down
27 changes: 20 additions & 7 deletions pyartifactory/objects/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/objects/object.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of artifactory base object.
"""

from __future__ import annotations

from typing import Optional, Tuple
Expand Down
1 change: 1 addition & 0 deletions pyartifactory/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Definition of all utils.
"""

from __future__ import annotations

from typing import Any
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions tests/test_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}/"],
Expand Down
Loading