Skip to content

Commit

Permalink
Allow pathlike for glob and rglob
Browse files Browse the repository at this point in the history
  • Loading branch information
pjbull committed Oct 17, 2024
1 parent cf74e17 commit da97181
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 8 deletions.
22 changes: 15 additions & 7 deletions cloudpathlib/cloudpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,20 +450,28 @@ def from_uri(cls, uri: str) -> Self:
# called on instance, use new_cloudpath to keep same client
return cls._new_cloudpath(uri)

def _glob_checks(self, pattern: str) -> None:
if ".." in pattern:
def _glob_checks(self, pattern: Union[str, os.PathLike]) -> str:
if isinstance(pattern, os.PathLike):
if isinstance(pattern, CloudPath):
str_pattern = str(pattern.relative_to(self))
else:
str_pattern = os.fspath(pattern)

if ".." in str_pattern:
raise CloudPathNotImplementedError(
"Relative paths with '..' not supported in glob patterns."
)

if pattern.startswith(self.cloud_prefix) or pattern.startswith("/"):
if str_pattern.startswith(self.cloud_prefix) or str_pattern.startswith("/"):
raise CloudPathNotImplementedError("Non-relative patterns are unsupported")

if self.drive == "":
raise CloudPathNotImplementedError(
".glob is only supported within a bucket or container; you can use `.iterdir` to list buckets; for example, CloudPath('s3://').iterdir()"
)

return str_pattern

def _build_subtree(self, recursive):
# build a tree structure for all files out of default dicts
Tree: Callable = lambda: defaultdict(Tree)
Expand Down Expand Up @@ -507,9 +515,9 @@ def _glob(self, selector, recursive: bool) -> Generator[Self, None, None]:
yield (self / str(p)[len(self.name) + 1 :])

def glob(
self, pattern: str, case_sensitive: Optional[bool] = None
self, pattern: Union[str, os.PathLike], case_sensitive: Optional[bool] = None
) -> Generator[Self, None, None]:
self._glob_checks(pattern)
pattern = self._glob_checks(pattern)

pattern_parts = PurePosixPath(pattern).parts
selector = _make_selector(
Expand All @@ -524,9 +532,9 @@ def glob(
)

def rglob(
self, pattern: str, case_sensitive: Optional[bool] = None
self, pattern: Union[str, os.PathLike], case_sensitive: Optional[bool] = None
) -> Generator[Self, None, None]:
self._glob_checks(pattern)
pattern = self._glob_checks(pattern)

pattern_parts = PurePosixPath(pattern).parts
selector = _make_selector(
Expand Down
10 changes: 9 additions & 1 deletion tests/test_cloudpath_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,13 @@ def test_glob(glob_test_dirs):
# https://github.com/python/cpython/blob/7ffe7ba30fc051014977c6f393c51e57e71a6648/Lib/test/test_pathlib.py#L1634-L1720

def _check_glob(pattern, glob_method, **kwargs):
local_pattern = kwargs.pop("local_pattern", None)

_assert_glob_results_match(
getattr(cloud_root, glob_method)(pattern, **kwargs),
getattr(local_root, glob_method)(pattern, **kwargs),
getattr(local_root, glob_method)(
pattern if local_pattern is None else local_pattern, **kwargs
),
cloud_root,
local_root,
)
Expand All @@ -214,6 +218,8 @@ def _check_glob(pattern, glob_method, **kwargs):
_check_glob("*A", "glob")
_check_glob("*B/*", "glob")
_check_glob("*/fileB", "glob")
_check_glob(PurePosixPath("**/*"), "glob")
_check_glob(cloud_root / "**/*", "glob", local_pattern="**/*")

# rglob_common
_check_glob("*", "rglob")
Expand All @@ -222,6 +228,8 @@ def _check_glob(pattern, glob_method, **kwargs):
_check_glob("*/fileA", "rglob")
_check_glob("*/fileB", "rglob")
_check_glob("file*", "rglob")
_check_glob(PurePosixPath("*"), "rglob")
_check_glob(cloud_root / "*", "rglob", local_pattern="*")

dir_c_cloud = cloud_root / "dirC"
dir_c_local = local_root / "dirC"
Expand Down

0 comments on commit da97181

Please sign in to comment.