diff --git a/HISTORY.md b/HISTORY.md index a21d8215..046ab12d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -7,6 +7,7 @@ - Drop support for Python 3.7; pin minimal `boto3` version to Python 3.8+ versions. (PR [#407](https://github.com/drivendataorg/cloudpathlib/pull/407)) - fix: use native `exists()` method in `GSClient`. (PR [#420](https://github.com/drivendataorg/cloudpathlib/pull/420)) - Enhancement: lazy instantiation of default client (PR [#432](https://github.com/drivendataorg/cloudpathlib/issues/432), Issue [#428](https://github.com/drivendataorg/cloudpathlib/issues/428)) +- Adds existence check before downloading in `download_to` (Issue [#430](https://github.com/drivendataorg/cloudpathlib/issues/430), PR [#432](https://github.com/drivendataorg/cloudpathlib/pull/432)) ## v0.18.1 (2024-02-26) diff --git a/cloudpathlib/cloudpath.py b/cloudpathlib/cloudpath.py index afca5cb1..8bad810d 100644 --- a/cloudpathlib/cloudpath.py +++ b/cloudpathlib/cloudpath.py @@ -63,6 +63,7 @@ def _make_selector(pattern_parts, _flavour, case_sensitive=True): CloudPathFileExistsError, CloudPathIsADirectoryError, CloudPathNotADirectoryError, + CloudPathNotExistsError, CloudPathNotImplementedError, DirectoryNotEmptyError, IncompleteImplementationError, @@ -899,6 +900,10 @@ def stat(self, follow_symlinks: bool = True) -> os.stat_result: # =========== public cloud methods, not in pathlib =============== def download_to(self, destination: Union[str, os.PathLike]) -> Path: destination = Path(destination) + + if not self.exists(): + raise CloudPathNotExistsError(f"Cannot download because path does not exist: {self}") + if self.is_file(): if destination.is_dir(): destination = destination / self.name diff --git a/cloudpathlib/exceptions.py b/cloudpathlib/exceptions.py index 802f71ee..1b4499fb 100644 --- a/cloudpathlib/exceptions.py +++ b/cloudpathlib/exceptions.py @@ -20,6 +20,10 @@ class CloudPathFileExistsError(CloudPathException, FileExistsError): pass +class CloudPathNotExistsError(CloudPathException): + pass + + class CloudPathIsADirectoryError(CloudPathException, IsADirectoryError): pass diff --git a/tests/test_cloudpath_file_io.py b/tests/test_cloudpath_file_io.py index af8f9e63..e5546f0b 100644 --- a/tests/test_cloudpath_file_io.py +++ b/tests/test_cloudpath_file_io.py @@ -10,6 +10,7 @@ from cloudpathlib import CloudPath from cloudpathlib.exceptions import ( + CloudPathNotExistsError, CloudPathIsADirectoryError, CloudPathNotImplementedError, DirectoryNotEmptyError, @@ -396,6 +397,9 @@ def test_file_read_writes(rig, tmp_path): ) assert cloud_rel_paths == dled_rel_paths + with pytest.raises(CloudPathNotExistsError): + (p / "not_exists_file").download_to(dl_file) + def test_dispatch_to_local_cache(rig): p = rig.create_cloud_path("dir_0/file0_1.txt")