From 29ea9bbc78049467cda41891922f8bc3cf8be2fb Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 27 Oct 2026 16:55:06 +0000 Subject: [PATCH] Preserve cached directory sources during uploads --- .../large_file/large_file/large_file_base.py | 42 ++++++++----------- tests/large_file/test_storage_safety.py | 15 +++++++ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/great_ai/large_file/large_file/large_file_base.py b/great_ai/large_file/large_file/large_file_base.py index d66becc..5d62e15 100644 --- a/great_ai/large_file/large_file/large_file_base.py +++ b/great_ai/large_file/large_file/large_file_base.py @@ -213,37 +213,31 @@ class LargeFileBase(ABC): if isinstance(path, str): path = Path(path) + if "w" not in self._mode: + self._find_instances() + self._version = self._instances[-1].version + 1 if self._instances else 0 with tempfile.TemporaryDirectory() as tmp: - if path.is_file(): - logger.info(f"Copying file for {self._local_name}") - copy: Any = shutil.copy + root = Path(tmp) + staged = root / self._local_name + if path.is_dir(): + shutil.copytree(path, staged) else: - logger.info(f"Copying directory for {self._local_name}") - copy = shutil.copytree + shutil.copy2(path, staged) - try: - # Make local copy in the cache - shutil.rmtree(self.cache_path / self._cache_name, ignore_errors=True) - copy(str(path), str(self.cache_path / self._cache_name)) - except shutil.SameFileError: - pass # No worries - - copy(str(path), str(Path(tmp) / self._local_name)) - - with tempfile.TemporaryDirectory() as tmp2: - # A directory has to be zipped and it cannot contain the output of the zipping - logger.info(f"Compressing {self._local_name}") - shutil.make_archive( - str(Path(tmp2) / self._local_name), + with tempfile.TemporaryDirectory() as archive_directory: + archive = shutil.make_archive( + str(Path(archive_directory) / self._local_name), COMPRESSION_ALGORITHM, - tmp, + root, ) + self._upload(Path(archive), hide_progress=hide_progress) - file_to_be_uploaded = ( - Path(tmp2) / f"{self._local_name}{ARCHIVE_EXTENSION}" - ) - self._upload(file_to_be_uploaded, hide_progress=hide_progress) + # Stage the source before publishing: it may itself be cached. + destination = self.cache_path / self._cache_name + if destination.exists(): + raise FileExistsError(f"Version already exists: {destination}") + shutil.move(str(staged), str(destination)) self.clean_up() diff --git a/tests/large_file/test_storage_safety.py b/tests/large_file/test_storage_safety.py index 10c5abe..cb45222 100644 --- a/tests/large_file/test_storage_safety.py +++ b/tests/large_file/test_storage_safety.py @@ -108,6 +108,21 @@ def test_evicted_remote_model_is_downloaded_again() -> None: assert downloads == ["model/0", "model/0"] +def test_uploading_cached_directory_preserves_source(tmp_path: Path) -> None: + source = tmp_path / "source" + source.mkdir() + (source / "weights").write_text("model weights") + LargeFileLocal("model", "w").push(source) + reader = LargeFileLocal("model") + cached = reader.get() + reader.push(cached) + assert reader.version == 1 + assert (cached / "weights").read_text() == "model weights" + assert ( + LargeFileLocal("model", version=1).get() / "weights" + ).read_text() == "model weights" + + def test_gridfs_matches_only_exact_model_names() -> None: files = [ SimpleNamespace(name=name, _id=name, length=1)