Document LargeFiles

This commit is contained in:
Andras Schmelczer 2022-07-16 16:22:22 +02:00
parent f7c82ed0fe
commit d0c5f5b2a4
No known key found for this signature in database
GPG key ID: 39260B5B0614A13E
5 changed files with 97 additions and 56 deletions

View file

@ -4,6 +4,8 @@
from great_ai.large_file import * from great_ai.large_file import *
``` ```
For more details about using LargeFiles, check out the [how to guide](/how-to-guides/large-file/).
::: great_ai.large_file.LargeFileLocal ::: great_ai.large_file.LargeFileLocal
options: options:
show_root_heading: true show_root_heading: true

View file

@ -21,31 +21,26 @@ ARCHIVE_EXTENSION = ".tar.gz"
class LargeFileBase(ABC): class LargeFileBase(ABC):
""" """Base for LargeFile implementations with different backends.
Store large files remotely. Use local cache for speed up.
Store large files remotely using the familiar API of `open()`. With built-in
versioning, pruning and local cache.
By default, files are stored in the ".cache" folder and the least recently used is
deleted after the overall size reaches 30 GBs.
Examples: Examples:
>>> LargeFileBase.cache_path = Path(".cache")
``` >>> LargeFileBase.max_cache_size = "30GB"
with LargeFile("test.txt", "w", keep_last_n=3) as f:
for i in range(1000000):
f.write('test\n')
with LargeFile("test.txt", "r") as f: Attributes:
print(f.readlines()[0]) initialized: Tell whether `configure_credentials` or
`configure_credentials_from_file` has been already called.
path_to_cached_text_file = LargeFile("test.txt", version=0).get() cache_path: Storage location for cached files.
``` max_cache_size: Delete files until the folder at `cache_path` is smaller than
this value. Examples: "5 GB", "10MB", "0.3 TB". Set to `None` for no
By default, files are stored in the ".cache" folder and the automatic cache-pruning.
least recently use is deleted after the overall size reaches 30 GBs.
Change it with the following properties.
```
LargeFile.cache_path = Path(".cache")
LargeFile.max_cache_size = "30GB"
```
""" """
initialized = False initialized = False
@ -86,12 +81,19 @@ class LargeFileBase(ABC):
cls, cls,
secrets: Union[Path, str, ConfigFile], secrets: Union[Path, str, ConfigFile],
) -> None: ) -> None:
"""Load file and feed its content to `configure_credentials`.
Extra keys are ignored.
"""
if not isinstance(secrets, ConfigFile): if not isinstance(secrets, ConfigFile):
secrets = ConfigFile(secrets) secrets = ConfigFile(secrets)
cls.configure_credentials(**{k.lower(): v for k, v in secrets.items()}) cls.configure_credentials(**{k.lower(): v for k, v in secrets.items()})
@classmethod @classmethod
def configure_credentials(cls, **kwargs: str) -> None: def configure_credentials(cls, **kwargs: str) -> None:
"""Configure required credentials for the LargeFile backend."""
cls.initialized = True cls.initialized = True
def __enter__(self) -> IO: def __enter__(self) -> IO:
@ -135,16 +137,22 @@ class LargeFileBase(ABC):
return False return False
@property
def _local_name(self) -> str:
return f"{self._name}{CACHE_NAME_VERSION_SEPARATOR}{self.version}"
@property @property
def version(self) -> int: def version(self) -> int:
"""Numeric version of the file proxied by this LargeFile instance."""
return cast(int, self._version) return cast(int, self._version)
@lru_cache(1) @lru_cache(1)
def get(self, hide_progress: bool = False) -> Path: def get(self, hide_progress: bool = False) -> Path:
"""Return path to the proxy of a file (or directory).
If not available in the local cache, an attempt is made to download it.
Args:
hide_progress: Do not show a progress update after each 10% of progress.
"""
remote_path = next( remote_path = next(
i.remote_path for i in self._instances if i.version == self._version i.remote_path for i in self._instances if i.version == self._version
) )
@ -171,6 +179,14 @@ class LargeFileBase(ABC):
return destination return destination
def push(self, path: Union[Path, str], hide_progress: bool = False) -> None: def push(self, path: Union[Path, str], hide_progress: bool = False) -> None:
"""Upload a file (or directory) as a new version of `key`.
The file/directory is compressed before upload.
Args:
hide_progress: Do not show a progress update after each 10% of progress.
"""
if isinstance(path, str): if isinstance(path, str):
path = Path(path) path = Path(path)
@ -208,9 +224,26 @@ class LargeFileBase(ABC):
self.clean_up() self.clean_up()
def delete(self) -> None: def delete(self) -> None:
"""Delete all versions of the files under this `key`."""
self._keep_last_n = 0 self._keep_last_n = 0
self._delete_old_remote_versions() self._delete_old_remote_versions()
@property
def versions_pretty(self) -> str:
"""Formatted string of all available versions."""
return ", ".join((str(i.version) for i in self._instances))
def clean_up(self) -> None:
"""Delete local and remote versions according to currently set cache and retention policy."""
self._delete_old_remote_versions()
self._prune_cache()
@property
def _local_name(self) -> str:
return f"{self._name}{CACHE_NAME_VERSION_SEPARATOR}{self.version}"
def _find_instances(self) -> None: def _find_instances(self) -> None:
if self._cache_only_mode: if self._cache_only_mode:
self._instances = self._find_instances_from_cache() self._instances = self._find_instances_from_cache()
@ -269,14 +302,6 @@ class LargeFileBase(ABC):
else: else:
raise ValueError("Unsupported file mode.") raise ValueError("Unsupported file mode.")
@property
def versions_pretty(self) -> str:
return ", ".join((str(i.version) for i in self._instances))
def clean_up(self) -> None:
self._delete_old_remote_versions()
self._prune_cache()
def _prune_cache(self) -> None: def _prune_cache(self) -> None:
self.cache_path.mkdir(parents=True, exist_ok=True) self.cache_path.mkdir(parents=True, exist_ok=True)

View file

@ -9,6 +9,30 @@ logger = get_logger("large_file")
class LargeFileLocal(LargeFileBase): class LargeFileLocal(LargeFileBase):
"""LargeFile implementation using local filesystem as a backend.
Store large files remotely using the familiar API of `open()`. With built-in
versioning, pruning and local cache.
IMPORTANT: If LargeFileLocal.max_cache_size is too small, it won't be enough to
store all your files and they can end up deleted.
See parent for more details.
Examples:
>>> LargeFileLocal.cache_path = Path(".cache")
>>> LargeFileLocal.max_cache_size = "30GB"
>>> with LargeFileLocal("my_test.txt", "w", keep_last_n=2) as f:
... f.write('test')
4
>>> with LargeFileLocal("my_test.txt") as f:
... print(f.read())
test
"""
def __init__( def __init__(
self, self,
name: str, name: str,
@ -40,6 +64,7 @@ class LargeFileLocal(LargeFileBase):
def _download( def _download(
self, remote_path: Any, local_path: Path, hide_progress: bool self, remote_path: Any, local_path: Path, hide_progress: bool
) -> None: ) -> None:
# This will never be called because the file must be in the cache
raise NotImplementedError() raise NotImplementedError()
def _upload(self, local_path: Path, hide_progress: bool) -> None: def _upload(self, local_path: Path, hide_progress: bool) -> None:

View file

@ -18,6 +18,14 @@ MONGO_NAME_VERSION_SEPARATOR = "_"
class LargeFileMongo(LargeFileBase): class LargeFileMongo(LargeFileBase):
"""LargeFile implementation using GridFS (MongoDB) as a backend.
Store large files remotely using the familiar API of `open()`. With built-in
versioning, pruning and local cache.
See parent for more details.
"""
mongo_connection_string = None mongo_connection_string = None
mongo_database = None mongo_database = None

View file

@ -16,31 +16,12 @@ S3_NAME_VERSION_SEPARATOR = "/"
class LargeFileS3(LargeFileBase): class LargeFileS3(LargeFileBase):
""" """LargeFile implementation using S3-compatible storage as a backend.
Store large files in S3. Use local cache for speed up.
Examples: Store large files remotely using the familiar API of `open()`. With built-in
versioning, pruning and local cache.
``` See parent for more details.
with LargeFile("test.txt", "w", keep_last_n=3) as f:
for i in range(1000000):
f.write('test\n')
with LargeFile("test.txt", "r") as f:
print(f.readlines()[0])
path_to_cached_text_file = LargeFile("test.txt", version=0).get()
```
By default, files are stored in the ".cache" folder and the
least recently use is deleted after the overall size reaches 30 GBs.
Change it with the following properties.
```
LargeFile.cache_path = Path(".cache")
LargeFile.max_cache_size = "30GB"
```
""" """
region_name = None region_name = None