import logging from typing import TYPE_CHECKING import boto3 import httpx from dmp.meta.constants import CONTENT_TYPE_MAPPING if TYPE_CHECKING: from mypy_boto3_s3 import S3Client as _S3Client logger = logging.getLogger(__name__) class S3Client: def __init__(self, *, region_name: str, session: boto3.Session | None = None): self.region_name = region_name self._session = session self._client: _S3Client | None = None @property def session(self) -> boto3.Session: if self._session is None: self._session = boto3.Session() return self._session @property def client(self) -> "_S3Client": if self._client is None: self._client = self.session.client("s3", region_name=self.region_name) return self._client def generate_presigned_url( self, bucket: str, key: str, expires_in: int = 3600 ) -> str: return self.client.generate_presigned_url( ClientMethod="get_object", Params={ "Bucket": bucket, "Key": key, }, ExpiresIn=expires_in, ) def put_object_from_url( self, bucket: str, path_prefix: str, url: str, filename: str, ) -> str | None: try: with httpx.Client() as client: response = client.get(url) response.raise_for_status() file_extension = CONTENT_TYPE_MAPPING.get( response.headers["Content-Type"], "png" ) path = f"{path_prefix}/{filename}.{file_extension}" self.client.put_object( Bucket=bucket, Key=path, Body=response.content, ContentType="None", ) except Exception: logger.exception( "S3 put object from url error", extra={"file": filename}, ) return None return path