generated from NERC-CEH/python-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added retry logic for invalid parquet files * Restructured Tested S3Reader Added S3ReaderWriter * Updated readme * Mocks * Removed docker build * Comment corrections
- Loading branch information
1 parent
d99f456
commit 720d2a0
Showing
11 changed files
with
320 additions
and
186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import logging | ||
|
||
from botocore.client import BaseClient | ||
from botocore.exceptions import ClientError | ||
from mypy_boto3_s3.client import S3Client | ||
|
||
from driutils.io.interfaces import ReaderInterface, WriterInterface | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class S3Base: | ||
"""Base class to reuse initializer""" | ||
|
||
_connection: S3Client | ||
"""The S3 client used to perform the work""" | ||
|
||
def __init__(self, s3_client: S3Client) -> None: | ||
"""Initializes | ||
Args: | ||
s3_client: The S3 client used to do work | ||
""" | ||
|
||
if not isinstance(s3_client, BaseClient): | ||
raise TypeError(f"'s3_client must be a BaseClient, not '{type(s3_client)}'") | ||
|
||
self._connection = s3_client | ||
|
||
|
||
class S3Reader(S3Base, ReaderInterface): | ||
"""Class for handling file reads using the AWS S3 client""" | ||
|
||
def read(self, bucket_name: str, s3_key: str) -> bytes: | ||
""" | ||
Retrieves an object from an S3 bucket. | ||
If any step fails, it logs an error and re-raises the exception. | ||
Args: | ||
bucket_name: The name of the S3 bucket. | ||
s3_key: The key (path) of the object within the bucket. | ||
Returns: | ||
bytes: raw bytes of the S3 object | ||
Raises: | ||
Exception: If there's any error in retrieving or parsing the object. | ||
""" | ||
try: | ||
data = self._connection.get_object(Bucket=bucket_name, Key=s3_key) | ||
return data["Body"].read() | ||
except (RuntimeError, ClientError) as e: | ||
logger.error(f"Failed to get {s3_key} from {bucket_name}") | ||
logger.exception(e) | ||
raise e | ||
|
||
|
||
class S3Writer(S3Base, WriterInterface): | ||
"""Writes to an S3 bucket""" | ||
|
||
def write(self, bucket_name: str, key: str, body: bytes) -> None: | ||
"""Uploads an object to an S3 bucket. | ||
This function attempts to upload a byte object to a specified S3 bucket | ||
using the provided S3 client. If the upload fails, it logs an error | ||
message and re-raises the exception. | ||
Args: | ||
bucket_name: The name of the S3 bucket. | ||
key: The key (path) of the object within the bucket. | ||
body: data to write to s3 object | ||
Raises: | ||
TypeError: If body is not bytes | ||
""" | ||
if not isinstance(body, bytes): | ||
raise TypeError(f"'body' must be 'bytes', not '{type(body)}") | ||
|
||
self._connection.put_object(Bucket=bucket_name, Key=key, Body=body) | ||
|
||
|
||
class S3ReaderWriter(S3Reader, S3Writer): | ||
"""Class to handle reading and writing in S3""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
"""Core classes used by writers""" | ||
|
||
from abc import ABC, abstractmethod | ||
from typing import Any, Self | ||
|
||
|
||
class ContextClass: | ||
_connection: Any | ||
"""Reference to the connection object""" | ||
|
||
def __enter__(self) -> Self: | ||
"""Creates a connection when used in a context block""" | ||
return self | ||
|
||
def __exit__(self, *args) -> None: | ||
"""Closes the connection when exiting the context""" | ||
self.close() | ||
|
||
def __del__(self): | ||
"""Closes the connection when deleted""" | ||
self.close() | ||
|
||
def close(self) -> None: | ||
"""Closes the connection""" | ||
self._connection.close() | ||
|
||
|
||
class ReaderInterface(ABC): | ||
"""Abstract implementation for a IO reader""" | ||
|
||
@abstractmethod | ||
def read(self, *args, **kwargs) -> Any: | ||
"""Reads data from a source""" | ||
|
||
|
||
class WriterInterface(ABC): | ||
"""Interface for defining parquet writing objects""" | ||
|
||
@abstractmethod | ||
def write(self, *args, **kwargs) -> None: | ||
"""Abstract method for read operations""" |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.