Files
wyndham-ARR/arr_storage/remote.py
2026-07-29 16:38:05 +08:00

111 lines
3.0 KiB
Python

"""Narrow adapter seam for an OSS or S3-compatible provider SDK."""
from __future__ import annotations
from typing import BinaryIO, Mapping, Protocol
from arr_storage.contracts import BackendError, BackendObject, ObjectBackend
class CloudClientError(RuntimeError):
"""Provider adapter error classified without retaining a raw provider message."""
def __init__(self, kind: str):
super().__init__(kind)
self.kind = kind
class CloudClientPort(Protocol):
"""Implement this port with the selected OSS SDK or S3-compatible SDK."""
def upload_file_if_absent(
self,
object_key: str,
source: str,
mime_type: str,
metadata: Mapping[str, str],
) -> BackendObject:
...
def stat_object(self, object_key: str) -> BackendObject:
...
def stream_object(self, object_key: str) -> BinaryIO:
...
def copy_object_if_absent(
self,
source_key: str,
destination_key: str,
metadata: Mapping[str, str],
) -> BackendObject:
...
def delete_exact_object(self, object_key: str) -> None:
...
class CloudObjectBackend(ObjectBackend):
"""Maps a provider-specific client port onto the guarded backend protocol."""
def __init__(self, client: CloudClientPort) -> None:
self._client = client
def put_file(
self,
object_key: str,
source: str,
mime_type: str,
metadata: Mapping[str, str],
*,
if_absent: bool,
) -> BackendObject:
if not if_absent:
raise BackendError("invalid_request")
try:
return self._client.upload_file_if_absent(
object_key,
source,
mime_type,
metadata,
)
except CloudClientError as error:
raise BackendError(error.kind) from None
def head(self, object_key: str) -> BackendObject:
try:
return self._client.stat_object(object_key)
except CloudClientError as error:
raise BackendError(error.kind) from None
def open_reader(self, object_key: str) -> BinaryIO:
try:
return self._client.stream_object(object_key)
except CloudClientError as error:
raise BackendError(error.kind) from None
def copy_object(
self,
source_key: str,
destination_key: str,
metadata: Mapping[str, str],
*,
if_absent: bool,
) -> BackendObject:
if not if_absent:
raise BackendError("invalid_request")
try:
return self._client.copy_object_if_absent(
source_key,
destination_key,
metadata,
)
except CloudClientError as error:
raise BackendError(error.kind) from None
def delete_object(self, object_key: str) -> None:
try:
self._client.delete_exact_object(object_key)
except CloudClientError as error:
raise BackendError(error.kind) from None