style: Additional style fixes
This commit is contained in:
@@ -10,9 +10,7 @@ import logging
|
||||
import os
|
||||
from typing import Any, BinaryIO, Dict, Iterator, List, Optional, Union
|
||||
|
||||
from clevercloud_storage_framework.exceptions import (
|
||||
StorageError
|
||||
)
|
||||
from clevercloud_storage_framework.exceptions import StorageError
|
||||
from clevercloud_storage_framework.interface import StorageProviderInterface
|
||||
|
||||
|
||||
@@ -127,7 +125,9 @@ class StorageClient:
|
||||
|
||||
return b"".join(chunks)
|
||||
|
||||
def write_file(self, path: str, content: Union[bytes, str, BinaryIO, Iterator[bytes]]) -> None:
|
||||
def write_file(
|
||||
self, path: str, content: Union[bytes, str, BinaryIO, Iterator[bytes]]
|
||||
) -> None:
|
||||
"""
|
||||
Write content to a file.
|
||||
|
||||
@@ -180,7 +180,9 @@ class StorageClient:
|
||||
|
||||
# Get provider for the path, passing the SAS token if available
|
||||
if sas_token:
|
||||
source_provider = StorageClientFactory.get_provider_for_path(path, sas_token)
|
||||
source_provider = StorageClientFactory.get_provider_for_path(
|
||||
path, sas_token
|
||||
)
|
||||
else:
|
||||
source_provider = StorageClientFactory.get_provider_for_path(path)
|
||||
source_provider.delete_file(path)
|
||||
@@ -219,7 +221,9 @@ class StorageClient:
|
||||
return
|
||||
|
||||
# If the provider supports the source and can accept files from the source protocol
|
||||
if source_supported and self.provider.supports_destination_protocol(source_protocol):
|
||||
if source_supported and self.provider.supports_destination_protocol(
|
||||
source_protocol
|
||||
):
|
||||
self.provider.copy_file(source_path, destination_path)
|
||||
return
|
||||
|
||||
@@ -236,7 +240,9 @@ class StorageClient:
|
||||
source_provider = self.provider
|
||||
if not source_supported:
|
||||
try:
|
||||
source_provider = StorageClientFactory.get_provider_for_path(source_path)
|
||||
source_provider = StorageClientFactory.get_provider_for_path(
|
||||
source_path
|
||||
)
|
||||
except StorageError:
|
||||
# Fall back to the current provider if none found
|
||||
source_provider = self.provider
|
||||
@@ -272,9 +278,9 @@ class StorageClient:
|
||||
destination_path (str): The destination file path
|
||||
"""
|
||||
# Check if the current provider supports both paths
|
||||
both_supported = self.provider.supports_path(source_path) and self.provider.supports_path(
|
||||
destination_path
|
||||
)
|
||||
both_supported = self.provider.supports_path(
|
||||
source_path
|
||||
) and self.provider.supports_path(destination_path)
|
||||
|
||||
# If the current provider supports both paths, use its move_file method
|
||||
if both_supported:
|
||||
@@ -291,7 +297,9 @@ class StorageClient:
|
||||
|
||||
if not self.provider.supports_path(source_path):
|
||||
try:
|
||||
source_provider = StorageClientFactory.get_provider_for_path(source_path)
|
||||
source_provider = StorageClientFactory.get_provider_for_path(
|
||||
source_path
|
||||
)
|
||||
except StorageError:
|
||||
source_provider = self.provider
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ class StorageClientFactory:
|
||||
ProviderConfiguration: provider configuration instance for the requested provider and path, or None,
|
||||
if no configuration could be found.
|
||||
"""
|
||||
candidate_configs: List[ProviderConfiguration] = cls._registered_configurations.get(
|
||||
provider_type, []
|
||||
candidate_configs: List[ProviderConfiguration] = (
|
||||
cls._registered_configurations.get(provider_type, [])
|
||||
)
|
||||
for candidate_config in candidate_configs:
|
||||
if candidate_config.matches_path_prefix(path):
|
||||
@@ -115,8 +115,8 @@ class StorageClientFactory:
|
||||
provider_type: str = provider_config.get_provider_type()
|
||||
if provider_type not in StorageClientFactory.get_registered_providers_ids():
|
||||
raise StorageError(f"Unsupported provider type: {provider_type}")
|
||||
configs_for_provider: List[ProviderConfiguration] = cls._registered_configurations.get(
|
||||
provider_type, []
|
||||
configs_for_provider: List[ProviderConfiguration] = (
|
||||
cls._registered_configurations.get(provider_type, [])
|
||||
)
|
||||
if configs_for_provider:
|
||||
configs_for_provider.append(provider_config)
|
||||
@@ -161,8 +161,8 @@ class StorageClientFactory:
|
||||
from clevercloud_storage_framework.builder import ProviderBuilder
|
||||
|
||||
if apply_provider_config:
|
||||
config: ProviderConfiguration = cls.get_registered_provider_config_by_provider_type(
|
||||
provider_type, path
|
||||
config: ProviderConfiguration = (
|
||||
cls.get_registered_provider_config_by_provider_type(provider_type, path)
|
||||
)
|
||||
if config:
|
||||
params: Dict[str, Any] = config.get_config_params()
|
||||
@@ -234,7 +234,9 @@ class StorageClientFactory:
|
||||
if provider_class.supports_path_format(path):
|
||||
if apply_provider_config:
|
||||
config: ProviderConfiguration = (
|
||||
cls.get_registered_provider_config_by_provider_type(provider_type, path)
|
||||
cls.get_registered_provider_config_by_provider_type(
|
||||
provider_type, path
|
||||
)
|
||||
)
|
||||
if config:
|
||||
params: Dict[str, Any] = config.get_config_params()
|
||||
|
||||
@@ -33,7 +33,9 @@ class StorageProviderInterface(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_files(self, path: str, recursive: bool = False) -> Iterator[Dict[str, Any]]:
|
||||
def list_files(
|
||||
self, path: str, recursive: bool = False
|
||||
) -> Iterator[Dict[str, Any]]:
|
||||
"""
|
||||
List files in the specified path.
|
||||
|
||||
@@ -132,7 +134,9 @@ class StorageProviderInterface(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_file(self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]) -> None:
|
||||
def write_file(
|
||||
self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]
|
||||
) -> None:
|
||||
"""
|
||||
Write content to a file.
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ class ProviderConfiguration(object):
|
||||
self._configuration: Dict[str, Any] = {}
|
||||
|
||||
if target_uri_prefix:
|
||||
uri_provider_type = StorageClientFactory.get_provider_type_for_path(target_uri_prefix)
|
||||
uri_provider_type = StorageClientFactory.get_provider_type_for_path(
|
||||
target_uri_prefix
|
||||
)
|
||||
if uri_provider_type != provider_type:
|
||||
raise InvalidPathError(
|
||||
target_uri_prefix,
|
||||
|
||||
@@ -81,7 +81,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
self.logger.debug("EFSStorageProvider initialized successfully")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to initialize EFSStorageProvider: {str(e)}")
|
||||
raise StorageError(f"Failed to initialize EFS client: {str(e)}", provider="efs")
|
||||
raise StorageError(
|
||||
f"Failed to initialize EFS client: {str(e)}", provider="efs"
|
||||
)
|
||||
|
||||
def _get_local_path(self, filesystem_id: str, path: str) -> str:
|
||||
"""
|
||||
@@ -148,7 +150,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"EFS describe_file_systems error: {str(e)}")
|
||||
raise StorageError(f"Failed to describe filesystem: {str(e)}", provider="efs")
|
||||
raise StorageError(
|
||||
f"Failed to describe filesystem: {str(e)}", provider="efs"
|
||||
)
|
||||
|
||||
# Mount the filesystem
|
||||
try:
|
||||
@@ -170,7 +174,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise StorageError(f"Failed to mount filesystem: {result.stderr}", provider="efs")
|
||||
raise StorageError(
|
||||
f"Failed to mount filesystem: {result.stderr}", provider="efs"
|
||||
)
|
||||
|
||||
except ClientError as e:
|
||||
error_code = e.response.get("Error", {}).get("Code", "")
|
||||
@@ -181,7 +187,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"EFS mount error: {str(e)}")
|
||||
raise StorageError(f"Failed to mount filesystem: {str(e)}", provider="efs")
|
||||
raise StorageError(
|
||||
f"Failed to mount filesystem: {str(e)}", provider="efs"
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"EFS mount error: {str(e)}")
|
||||
raise StorageError(f"Failed to mount filesystem: {str(e)}", provider="efs")
|
||||
@@ -189,7 +197,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
def get_chunk_size(self) -> int:
|
||||
return self.chunk_size
|
||||
|
||||
def list_files(self, path: str, recursive: bool = False) -> Iterator[Dict[str, Any]]:
|
||||
def list_files(
|
||||
self, path: str, recursive: bool = False
|
||||
) -> Iterator[Dict[str, Any]]:
|
||||
"""
|
||||
List files in an EFS filesystem with the given path.
|
||||
|
||||
@@ -230,13 +240,16 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
# Yield directories
|
||||
for dir_name in dirs:
|
||||
dir_path = os.path.join(root, dir_name)
|
||||
rel_dir_path = os.path.join(rel_path, dir_name) if rel_path else dir_name
|
||||
rel_dir_path = (
|
||||
os.path.join(rel_path, dir_name) if rel_path else dir_name
|
||||
)
|
||||
|
||||
stat = os.stat(dir_path)
|
||||
|
||||
yield {
|
||||
"name": dir_name,
|
||||
"path": f"efs://{filesystem_id}/{os.path.join(fs_path, rel_dir_path) if fs_path else rel_dir_path}",
|
||||
"path": f"efs://{filesystem_id}/"
|
||||
f"{os.path.join(fs_path, rel_dir_path) if fs_path else rel_dir_path}",
|
||||
"size": 0,
|
||||
"type": "directory",
|
||||
"last_modified": stat.st_mtime,
|
||||
@@ -246,13 +259,16 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
# Yield files
|
||||
for file_name in files:
|
||||
file_path = os.path.join(root, file_name)
|
||||
rel_file_path = os.path.join(rel_path, file_name) if rel_path else file_name
|
||||
rel_file_path = (
|
||||
os.path.join(rel_path, file_name) if rel_path else file_name
|
||||
)
|
||||
|
||||
stat = os.stat(file_path)
|
||||
|
||||
yield {
|
||||
"name": file_name,
|
||||
"path": f"efs://{filesystem_id}/{os.path.join(fs_path, rel_file_path) if fs_path else rel_file_path}",
|
||||
"path": f"efs://{filesystem_id}/"
|
||||
f"{os.path.join(fs_path, rel_file_path) if fs_path else rel_file_path}",
|
||||
"size": stat.st_size,
|
||||
"type": "file",
|
||||
"last_modified": stat.st_mtime,
|
||||
@@ -314,7 +330,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
return False
|
||||
except Exception as e:
|
||||
self.logger.error(f"EFS file_exists error: {str(e)}")
|
||||
raise StorageError(f"Failed to check if file exists: {str(e)}", provider="efs")
|
||||
raise StorageError(
|
||||
f"Failed to check if file exists: {str(e)}", provider="efs"
|
||||
)
|
||||
|
||||
def get_file_size(self, path: str) -> int:
|
||||
"""
|
||||
@@ -473,7 +491,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
self.logger.error(f"EFS read_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to read file: {str(e)}", provider="efs")
|
||||
|
||||
def write_file(self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]) -> None:
|
||||
def write_file(
|
||||
self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]
|
||||
) -> None:
|
||||
"""
|
||||
Write content to a file in EFS.
|
||||
|
||||
@@ -610,7 +630,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
self._ensure_filesystem_mounted(dest_filesystem_id)
|
||||
|
||||
# Get the local paths
|
||||
source_local_path = self._get_local_path(source_filesystem_id, source_fs_path)
|
||||
source_local_path = self._get_local_path(
|
||||
source_filesystem_id, source_fs_path
|
||||
)
|
||||
dest_local_path = self._get_local_path(dest_filesystem_id, dest_fs_path)
|
||||
|
||||
# Check if the source file exists
|
||||
@@ -621,13 +643,20 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
|
||||
# Check if it's a file
|
||||
if not os.path.isfile(source_local_path):
|
||||
raise ResourceNotFoundError(f"Not a file: {source_path}", provider="efs")
|
||||
raise ResourceNotFoundError(
|
||||
f"Not a file: {source_path}", provider="efs"
|
||||
)
|
||||
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(os.path.dirname(os.path.abspath(dest_local_path)), exist_ok=True)
|
||||
os.makedirs(
|
||||
os.path.dirname(os.path.abspath(dest_local_path)), exist_ok=True
|
||||
)
|
||||
|
||||
# Copy the file
|
||||
with open(source_local_path, "rb") as src, open(dest_local_path, "wb") as dst:
|
||||
with (
|
||||
open(source_local_path, "rb") as src,
|
||||
open(dest_local_path, "wb") as dst,
|
||||
):
|
||||
while True:
|
||||
chunk = src.read(self.chunk_size)
|
||||
if not chunk:
|
||||
@@ -655,7 +684,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
self._ensure_filesystem_mounted(source_filesystem_id)
|
||||
|
||||
# Get the local path
|
||||
source_local_path = self._get_local_path(source_filesystem_id, source_fs_path)
|
||||
source_local_path = self._get_local_path(
|
||||
source_filesystem_id, source_fs_path
|
||||
)
|
||||
|
||||
# Check if the source file exists
|
||||
if not os.path.exists(source_local_path):
|
||||
@@ -665,7 +696,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
|
||||
# Check if it's a file
|
||||
if not os.path.isfile(source_local_path):
|
||||
raise ResourceNotFoundError(f"Not a file: {source_path}", provider="efs")
|
||||
raise ResourceNotFoundError(
|
||||
f"Not a file: {source_path}", provider="efs"
|
||||
)
|
||||
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(
|
||||
@@ -674,7 +707,10 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
)
|
||||
|
||||
# Copy the file
|
||||
with open(source_local_path, "rb") as src, open(destination_path, "wb") as dst:
|
||||
with (
|
||||
open(source_local_path, "rb") as src,
|
||||
open(destination_path, "wb") as dst,
|
||||
):
|
||||
while True:
|
||||
chunk = src.read(self.chunk_size)
|
||||
if not chunk:
|
||||
@@ -702,7 +738,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
self._ensure_filesystem_mounted(dest_filesystem_id)
|
||||
|
||||
# Get the local path
|
||||
dest_local_path = self._get_local_path(dest_filesystem_id, dest_fs_path)
|
||||
dest_local_path = self._get_local_path(
|
||||
dest_filesystem_id, dest_fs_path
|
||||
)
|
||||
|
||||
# Check if the source file exists
|
||||
if not os.path.exists(source_path):
|
||||
@@ -711,10 +749,15 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
)
|
||||
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(os.path.dirname(os.path.abspath(dest_local_path)), exist_ok=True)
|
||||
os.makedirs(
|
||||
os.path.dirname(os.path.abspath(dest_local_path)), exist_ok=True
|
||||
)
|
||||
|
||||
# Copy the file
|
||||
with open(source_path, "rb") as src, open(dest_local_path, "wb") as dst:
|
||||
with (
|
||||
open(source_path, "rb") as src,
|
||||
open(dest_local_path, "wb") as dst,
|
||||
):
|
||||
while True:
|
||||
chunk = src.read(self.chunk_size)
|
||||
if not chunk:
|
||||
@@ -835,7 +878,9 @@ class EFSStorageProvider(StorageProviderInterface):
|
||||
|
||||
# Check if the directory exists
|
||||
if not os.path.exists(local_path):
|
||||
raise ResourceNotFoundError(f"Directory not found: {path}", provider="efs")
|
||||
raise ResourceNotFoundError(
|
||||
f"Directory not found: {path}", provider="efs"
|
||||
)
|
||||
|
||||
# Check if it's a directory
|
||||
if not os.path.isdir(local_path):
|
||||
|
||||
@@ -14,7 +14,7 @@ from ..exceptions import (
|
||||
InvalidPathError,
|
||||
OperationNotSupportedError,
|
||||
ResourceNotFoundError,
|
||||
StorageError
|
||||
StorageError,
|
||||
)
|
||||
from ..interface import StorageProviderInterface
|
||||
from ..utils.stream import ChunkedReader
|
||||
@@ -82,7 +82,9 @@ class GlacierStorageProvider(StorageProviderInterface):
|
||||
InvalidPathError: If the path is not a valid Glacier path
|
||||
"""
|
||||
if not path or not self.supports_path(path):
|
||||
raise InvalidPathError(path, "Path must be in the format 'glacier://vault/archive-id'")
|
||||
raise InvalidPathError(
|
||||
path, "Path must be in the format 'glacier://vault/archive-id'"
|
||||
)
|
||||
|
||||
# Remove the 'glacier://' prefix
|
||||
path = path[10:]
|
||||
@@ -128,7 +130,9 @@ class GlacierStorageProvider(StorageProviderInterface):
|
||||
"""
|
||||
return path.startswith("glacier://")
|
||||
|
||||
def list_files(self, path: str, recursive: bool = False) -> Iterator[Dict[str, Any]]:
|
||||
def list_files(
|
||||
self, path: str, recursive: bool = False
|
||||
) -> Iterator[Dict[str, Any]]:
|
||||
"""
|
||||
List files in a vault.
|
||||
|
||||
@@ -227,7 +231,9 @@ class GlacierStorageProvider(StorageProviderInterface):
|
||||
except self.client.exceptions.ResourceNotFoundException:
|
||||
raise ResourceNotFoundError(path, "glacier")
|
||||
|
||||
def write_file(self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]) -> None:
|
||||
def write_file(
|
||||
self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]
|
||||
) -> None:
|
||||
"""
|
||||
Write a file.
|
||||
|
||||
@@ -255,7 +261,9 @@ class GlacierStorageProvider(StorageProviderInterface):
|
||||
chunks = list(content)
|
||||
file_obj = io.BytesIO(b"".join(chunks))
|
||||
else:
|
||||
raise ValueError("Content must be bytes, a file-like object, or an iterator of bytes")
|
||||
raise ValueError(
|
||||
"Content must be bytes, a file-like object, or an iterator of bytes"
|
||||
)
|
||||
|
||||
# Upload the archive
|
||||
self.client.upload_archive(vaultName=vault_name, body=file_obj)
|
||||
|
||||
@@ -14,7 +14,7 @@ from typing import Any, BinaryIO, Dict, Iterator, Optional, Tuple, Union
|
||||
from clevercloud_storage_framework.exceptions import (
|
||||
InvalidPathError,
|
||||
ResourceNotFoundError,
|
||||
StorageError
|
||||
StorageError,
|
||||
)
|
||||
from clevercloud_storage_framework.interface import StorageProviderInterface
|
||||
|
||||
@@ -50,7 +50,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
def get_chunk_size(self) -> int:
|
||||
return self.chunk_size
|
||||
|
||||
def list_files(self, path: str, recursive: bool = False) -> Iterator[Dict[str, Any]]:
|
||||
def list_files(
|
||||
self, path: str, recursive: bool = False
|
||||
) -> Iterator[Dict[str, Any]]:
|
||||
"""
|
||||
List files in the specified local directory.
|
||||
|
||||
@@ -73,7 +75,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
# Check if it's a directory
|
||||
if not os.path.isdir(path):
|
||||
raise ResourceNotFoundError(f"Not a directory: {path}", provider="local")
|
||||
raise ResourceNotFoundError(
|
||||
f"Not a directory: {path}", provider="local"
|
||||
)
|
||||
|
||||
# List files
|
||||
if recursive:
|
||||
@@ -86,7 +90,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
# Yield directories
|
||||
for dir_name in dirs:
|
||||
dir_path = os.path.join(root, dir_name)
|
||||
rel_dir_path = os.path.join(rel_path, dir_name) if rel_path else dir_name
|
||||
rel_dir_path = (
|
||||
os.path.join(rel_path, dir_name) if rel_path else dir_name
|
||||
)
|
||||
|
||||
stat = os.stat(dir_path)
|
||||
|
||||
@@ -102,7 +108,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
# Yield files
|
||||
for file_name in files:
|
||||
file_path = os.path.join(root, file_name)
|
||||
rel_file_path = os.path.join(rel_path, file_name) if rel_path else file_name
|
||||
rel_file_path = (
|
||||
os.path.join(rel_path, file_name) if rel_path else file_name
|
||||
)
|
||||
|
||||
stat = os.stat(file_path)
|
||||
|
||||
@@ -134,7 +142,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
except PermissionError:
|
||||
# Re-raise permission errors
|
||||
raise PermissionError(f"Permission denied for listing: {path}", provider="local")
|
||||
raise PermissionError(
|
||||
f"Permission denied for listing: {path}", provider="local"
|
||||
)
|
||||
except ResourceNotFoundError:
|
||||
# Re-raise resource not found errors
|
||||
raise
|
||||
@@ -183,7 +193,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
except PermissionError:
|
||||
# Re-raise permission errors
|
||||
raise PermissionError(f"Permission denied for accessing: {path}", provider="local")
|
||||
raise PermissionError(
|
||||
f"Permission denied for accessing: {path}", provider="local"
|
||||
)
|
||||
except ResourceNotFoundError:
|
||||
# Re-raise resource not found errors
|
||||
raise
|
||||
@@ -231,13 +243,17 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
except PermissionError:
|
||||
# Re-raise permission errors
|
||||
raise PermissionError(f"Permission denied for accessing: {path}", provider="local")
|
||||
raise PermissionError(
|
||||
f"Permission denied for accessing: {path}", provider="local"
|
||||
)
|
||||
except ResourceNotFoundError:
|
||||
# Re-raise resource not found errors
|
||||
raise
|
||||
except Exception as e:
|
||||
self.logger.error(f"Local get_file_metadata error: {str(e)}")
|
||||
raise StorageError(f"Failed to get file metadata: {str(e)}", provider="local")
|
||||
raise StorageError(
|
||||
f"Failed to get file metadata: {str(e)}", provider="local"
|
||||
)
|
||||
|
||||
def read_file(
|
||||
self, path: str, offset: int = 0, length: Optional[int] = None
|
||||
@@ -290,7 +306,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
except PermissionError:
|
||||
# Re-raise permission errors
|
||||
raise PermissionError(f"Permission denied for reading: {path}", provider="local")
|
||||
raise PermissionError(
|
||||
f"Permission denied for reading: {path}", provider="local"
|
||||
)
|
||||
except ResourceNotFoundError:
|
||||
# Re-raise resource not found errors
|
||||
raise
|
||||
@@ -298,7 +316,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
self.logger.error(f"Local read_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to read file: {str(e)}", provider="local")
|
||||
|
||||
def write_file(self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]) -> None:
|
||||
def write_file(
|
||||
self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]
|
||||
) -> None:
|
||||
"""
|
||||
Write content to a file in the local filesystem.
|
||||
|
||||
@@ -342,7 +362,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
except PermissionError:
|
||||
# Re-raise permission errors
|
||||
raise PermissionError(f"Permission denied for writing to: {path}", provider="local")
|
||||
raise PermissionError(
|
||||
f"Permission denied for writing to: {path}", provider="local"
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Local write_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to write file: {str(e)}", provider="local")
|
||||
@@ -373,7 +395,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
except PermissionError:
|
||||
# Re-raise permission errors
|
||||
raise PermissionError(f"Permission denied for deleting: {path}", provider="local")
|
||||
raise PermissionError(
|
||||
f"Permission denied for deleting: {path}", provider="local"
|
||||
)
|
||||
except ResourceNotFoundError:
|
||||
# Re-raise resource not found errors
|
||||
raise
|
||||
@@ -412,10 +436,14 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
# Check if it's a file
|
||||
if not os.path.isfile(source_path):
|
||||
raise ResourceNotFoundError(f"Not a file: {source_path}", provider="local")
|
||||
raise ResourceNotFoundError(
|
||||
f"Not a file: {source_path}", provider="local"
|
||||
)
|
||||
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(os.path.dirname(os.path.abspath(destination_path)), exist_ok=True)
|
||||
os.makedirs(
|
||||
os.path.dirname(os.path.abspath(destination_path)), exist_ok=True
|
||||
)
|
||||
|
||||
# Copy the file
|
||||
shutil.copy2(source_path, destination_path)
|
||||
@@ -428,7 +456,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
StorageClientFactory,
|
||||
)
|
||||
|
||||
source_provider = StorageClientFactory.get_provider_for_path(source_path)
|
||||
source_provider = StorageClientFactory.get_provider_for_path(
|
||||
source_path
|
||||
)
|
||||
|
||||
# Read from source provider and write to local
|
||||
content = source_provider.read_file(source_path)
|
||||
@@ -476,10 +506,14 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
|
||||
# Check if it's a file
|
||||
if not os.path.isfile(source_path):
|
||||
raise ResourceNotFoundError(f"Not a file: {source_path}", provider="local")
|
||||
raise ResourceNotFoundError(
|
||||
f"Not a file: {source_path}", provider="local"
|
||||
)
|
||||
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(os.path.dirname(os.path.abspath(destination_path)), exist_ok=True)
|
||||
os.makedirs(
|
||||
os.path.dirname(os.path.abspath(destination_path)), exist_ok=True
|
||||
)
|
||||
|
||||
# Move the file
|
||||
shutil.move(source_path, destination_path)
|
||||
@@ -492,7 +526,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
StorageClientFactory,
|
||||
)
|
||||
|
||||
source_provider = StorageClientFactory.get_provider_for_path(source_path)
|
||||
source_provider = StorageClientFactory.get_provider_for_path(
|
||||
source_path
|
||||
)
|
||||
|
||||
# Copy the file
|
||||
self.copy_file(source_path, destination_path)
|
||||
@@ -533,7 +569,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Local create_directory error: {str(e)}")
|
||||
raise StorageError(f"Failed to create directory: {str(e)}", provider="local")
|
||||
raise StorageError(
|
||||
f"Failed to create directory: {str(e)}", provider="local"
|
||||
)
|
||||
|
||||
def delete_directory(self, path: str, recursive: bool = False) -> None:
|
||||
"""
|
||||
@@ -551,11 +589,15 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
try:
|
||||
# Check if the directory exists
|
||||
if not os.path.exists(path):
|
||||
raise ResourceNotFoundError(f"Directory not found: {path}", provider="local")
|
||||
raise ResourceNotFoundError(
|
||||
f"Directory not found: {path}", provider="local"
|
||||
)
|
||||
|
||||
# Check if it's a directory
|
||||
if not os.path.isdir(path):
|
||||
raise ResourceNotFoundError(f"Not a directory: {path}", provider="local")
|
||||
raise ResourceNotFoundError(
|
||||
f"Not a directory: {path}", provider="local"
|
||||
)
|
||||
|
||||
# Delete the directory
|
||||
if recursive:
|
||||
@@ -581,7 +623,9 @@ class LocalStorageProvider(StorageProviderInterface):
|
||||
raise
|
||||
except Exception as e:
|
||||
self.logger.error(f"Local delete_directory error: {str(e)}")
|
||||
raise StorageError(f"Failed to delete directory: {str(e)}", provider="local")
|
||||
raise StorageError(
|
||||
f"Failed to delete directory: {str(e)}", provider="local"
|
||||
)
|
||||
|
||||
def get_provider_name(self) -> str:
|
||||
"""
|
||||
|
||||
@@ -94,7 +94,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
self.logger.debug("S3StorageProvider initialized successfully")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to initialize S3StorageProvider: {str(e)}")
|
||||
raise StorageError(f"Failed to initialize S3 client: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to initialize S3 client: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
def _append_sas_token(self, url: str) -> str:
|
||||
"""
|
||||
@@ -121,7 +123,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
def get_chunk_size(self) -> int:
|
||||
return self.chunk_size
|
||||
|
||||
def list_files(self, path: str, recursive: bool = False) -> Iterator[Dict[str, Any]]:
|
||||
def list_files(
|
||||
self, path: str, recursive: bool = False
|
||||
) -> Iterator[Dict[str, Any]]:
|
||||
"""
|
||||
List files in an S3 bucket with the given prefix.
|
||||
|
||||
@@ -201,7 +205,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
error_code = e.response.get("Error", {}).get("Code", "")
|
||||
|
||||
if error_code == "NoSuchBucket":
|
||||
raise ResourceNotFoundError(f"Bucket not found: {bucket}", provider="s3")
|
||||
raise ResourceNotFoundError(
|
||||
f"Bucket not found: {bucket}", provider="s3"
|
||||
)
|
||||
elif error_code in ("AccessDenied", "AllAccessDisabled"):
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for bucket: {bucket}", provider="s3"
|
||||
@@ -240,10 +246,14 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
return False
|
||||
elif error_code in ("403", "AccessDenied"):
|
||||
# If we get access denied, we can't determine if the file exists
|
||||
raise StoragePermissionError(f"Permission denied for object: {path}", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for object: {path}", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 file_exists error: {str(e)}")
|
||||
raise StorageError(f"Failed to check if file exists: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to check if file exists: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
def get_file_size(self, path: str) -> int:
|
||||
"""
|
||||
@@ -272,7 +282,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
if error_code in ("404", "NoSuchKey", "NotFound"):
|
||||
raise ResourceNotFoundError(f"File not found: {path}", provider="s3")
|
||||
elif error_code in ("403", "AccessDenied"):
|
||||
raise StoragePermissionError(f"Permission denied for object: {path}", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for object: {path}", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 get_file_size error: {str(e)}")
|
||||
raise StorageError(f"Failed to get file size: {str(e)}", provider="s3")
|
||||
@@ -318,10 +330,14 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
if error_code in ("404", "NoSuchKey", "NotFound"):
|
||||
raise ResourceNotFoundError(f"File not found: {path}", provider="s3")
|
||||
elif error_code in ("403", "AccessDenied"):
|
||||
raise StoragePermissionError(f"Permission denied for object: {path}", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for object: {path}", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 get_file_metadata error: {str(e)}")
|
||||
raise StorageError(f"Failed to get file metadata: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to get file metadata: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
def read_file(
|
||||
self, path: str, offset: int = 0, length: Optional[int] = None
|
||||
@@ -371,7 +387,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
headers["Range"] = range_header
|
||||
|
||||
# Stream the response without loading everything into memory
|
||||
response = self.http_session.get(presigned_url, headers=headers, stream=True)
|
||||
response = self.http_session.get(
|
||||
presigned_url, headers=headers, stream=True
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# Create a custom iterator to yield chunks of the requested size
|
||||
@@ -399,7 +417,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
if error_code in ("404", "NoSuchKey", "NotFound"):
|
||||
raise ResourceNotFoundError(f"File not found: {path}", provider="s3")
|
||||
elif error_code in ("403", "AccessDenied"):
|
||||
raise StoragePermissionError(f"Permission denied for object: {path}", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for object: {path}", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 read_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to read file: {str(e)}", provider="s3")
|
||||
@@ -441,9 +461,13 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
return url
|
||||
except ClientError as e:
|
||||
self.logger.error(f"S3 generate_presigned_url error: {str(e)}")
|
||||
raise StorageError(f"Failed to generate pre-signed URL: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to generate pre-signed URL: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
def write_file(self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]) -> None:
|
||||
def write_file(
|
||||
self, path: str, content: Union[bytes, BinaryIO, Iterator[bytes]]
|
||||
) -> None:
|
||||
"""
|
||||
Write content to a file in S3.
|
||||
|
||||
@@ -470,7 +494,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
and not isinstance(content, (bytes, bytearray))
|
||||
):
|
||||
# Initialize multipart upload
|
||||
response = self.client.create_multipart_upload(Bucket=bucket, Key=key)
|
||||
response = self.client.create_multipart_upload(
|
||||
Bucket=bucket, Key=key
|
||||
)
|
||||
upload_id = response["UploadId"]
|
||||
|
||||
try:
|
||||
@@ -479,7 +505,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
part_number = 1
|
||||
|
||||
# Use ChunkedWriter with preserve_chunks=True to maintain individual chunks
|
||||
writer = ChunkedWriter(content, self.chunk_size, preserve_chunks=True)
|
||||
writer = ChunkedWriter(
|
||||
content, self.chunk_size, preserve_chunks=True
|
||||
)
|
||||
|
||||
for chunk in writer:
|
||||
if not chunk: # Skip empty chunks
|
||||
@@ -530,7 +558,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
|
||||
# Complete the multipart upload
|
||||
complete_body = {"Parts": parts}
|
||||
response = self.http_session.post(complete_url, json=complete_body)
|
||||
response = self.http_session.post(
|
||||
complete_url, json=complete_body
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
@@ -551,7 +581,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
# Abort the multipart upload
|
||||
self.http_session.delete(abort_url)
|
||||
|
||||
raise StorageError(f"Failed to write file: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to write file: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
return
|
||||
else:
|
||||
@@ -600,7 +632,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
part_number = 1
|
||||
|
||||
# Use ChunkedWriter with preserve_chunks=True to maintain individual chunks
|
||||
writer = ChunkedWriter(content, self.chunk_size, preserve_chunks=True)
|
||||
writer = ChunkedWriter(
|
||||
content, self.chunk_size, preserve_chunks=True
|
||||
)
|
||||
|
||||
# Upload each chunk as a separate part
|
||||
for chunk in writer:
|
||||
@@ -617,7 +651,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
)
|
||||
|
||||
# Add the part to the list
|
||||
parts.append({"PartNumber": part_number, "ETag": response["ETag"]})
|
||||
parts.append(
|
||||
{"PartNumber": part_number, "ETag": response["ETag"]}
|
||||
)
|
||||
|
||||
part_number += 1
|
||||
|
||||
@@ -630,7 +666,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
)
|
||||
except Exception as e:
|
||||
# Abort the multipart upload on error
|
||||
self.client.abort_multipart_upload(Bucket=bucket, Key=key, UploadId=upload_id)
|
||||
self.client.abort_multipart_upload(
|
||||
Bucket=bucket, Key=key, UploadId=upload_id
|
||||
)
|
||||
raise e
|
||||
else:
|
||||
raise ValueError(f"Unsupported content type: {type(content)}")
|
||||
@@ -639,7 +677,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
error_code = e.response.get("Error", {}).get("Code", "")
|
||||
|
||||
if error_code in ("403", "AccessDenied"):
|
||||
raise StoragePermissionError(f"Permission denied for object: {path}", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for object: {path}", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 write_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to write file: {str(e)}", provider="s3")
|
||||
@@ -671,7 +711,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
error_code = e.response.get("Error", {}).get("Code", "")
|
||||
|
||||
if error_code in ("403", "AccessDenied"):
|
||||
raise StoragePermissionError(f"Permission denied for object: {path}", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
f"Permission denied for object: {path}", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 delete_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to delete file: {str(e)}", provider="s3")
|
||||
@@ -727,7 +769,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
copy_source = {"Bucket": source_bucket, "Key": source_key}
|
||||
|
||||
# Initiate multipart upload
|
||||
response = self.client.create_multipart_upload(Bucket=dest_bucket, Key=dest_key)
|
||||
response = self.client.create_multipart_upload(
|
||||
Bucket=dest_bucket, Key=dest_key
|
||||
)
|
||||
upload_id = response["UploadId"]
|
||||
|
||||
try:
|
||||
@@ -788,7 +832,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
)
|
||||
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(os.path.dirname(os.path.abspath(destination_path)), exist_ok=True)
|
||||
os.makedirs(
|
||||
os.path.dirname(os.path.abspath(destination_path)), exist_ok=True
|
||||
)
|
||||
|
||||
# Download the file
|
||||
with open(destination_path, "wb") as f:
|
||||
@@ -820,9 +866,13 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
error_code = e.response.get("Error", {}).get("Code", "")
|
||||
|
||||
if error_code in ("404", "NoSuchKey", "NotFound"):
|
||||
raise ResourceNotFoundError(f"Source file not found: {source_path}", provider="s3")
|
||||
raise ResourceNotFoundError(
|
||||
f"Source file not found: {source_path}", provider="s3"
|
||||
)
|
||||
elif error_code in ("403", "AccessDenied"):
|
||||
raise StoragePermissionError(f"Permission denied for copy operation", provider="s3")
|
||||
raise StoragePermissionError(
|
||||
"Permission denied for copy operation", provider="s3"
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 copy_file error: {str(e)}")
|
||||
raise StorageError(f"Failed to copy file: {str(e)}", provider="s3")
|
||||
@@ -886,7 +936,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 create_directory error: {str(e)}")
|
||||
raise StorageError(f"Failed to create directory: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to create directory: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
def delete_directory(self, path: str, recursive: bool = False) -> None:
|
||||
"""
|
||||
@@ -921,17 +973,23 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
break
|
||||
|
||||
if not has_objects:
|
||||
raise ResourceNotFoundError(f"Directory not found: {path}", provider="s3")
|
||||
raise ResourceNotFoundError(
|
||||
f"Directory not found: {path}", provider="s3"
|
||||
)
|
||||
|
||||
# If not recursive, check if there are objects other than the directory marker
|
||||
if not recursive:
|
||||
paginator = self.client.get_paginator("list_objects_v2")
|
||||
page_iterator = paginator.paginate(Bucket=bucket, Prefix=key, Delimiter="/")
|
||||
page_iterator = paginator.paginate(
|
||||
Bucket=bucket, Prefix=key, Delimiter="/"
|
||||
)
|
||||
|
||||
for page in page_iterator:
|
||||
# Check if there are any objects other than the directory marker
|
||||
contents = page.get("Contents", [])
|
||||
if len(contents) > 1 or (len(contents) == 1 and contents[0].get("Key") != key):
|
||||
if len(contents) > 1 or (
|
||||
len(contents) == 1 and contents[0].get("Key") != key
|
||||
):
|
||||
raise StorageError(
|
||||
f"Directory not empty: {path}. Use recursive=True to delete non-empty directories.",
|
||||
provider="s3",
|
||||
@@ -954,7 +1012,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
if "Contents" in page:
|
||||
objects = [{"Key": obj["Key"]} for obj in page["Contents"]]
|
||||
if objects:
|
||||
self.client.delete_objects(Bucket=bucket, Delete={"Objects": objects})
|
||||
self.client.delete_objects(
|
||||
Bucket=bucket, Delete={"Objects": objects}
|
||||
)
|
||||
else:
|
||||
# Delete only the directory marker
|
||||
self.client.delete_object(Bucket=bucket, Key=key)
|
||||
@@ -968,7 +1028,9 @@ class S3StorageProvider(StorageProviderInterface):
|
||||
)
|
||||
else:
|
||||
self.logger.error(f"S3 delete_directory error: {str(e)}")
|
||||
raise StorageError(f"Failed to delete directory: {str(e)}", provider="s3")
|
||||
raise StorageError(
|
||||
f"Failed to delete directory: {str(e)}", provider="s3"
|
||||
)
|
||||
|
||||
def get_provider_name(self) -> str:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user