Skip to content

Commit

Permalink
Add tests for new methods
Browse files Browse the repository at this point in the history
  • Loading branch information
pjbull committed Oct 16, 2024
1 parent 8ac3004 commit b910486
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 7 deletions.
28 changes: 21 additions & 7 deletions cloudpathlib/cloudpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import shutil
import sys
from types import MethodType
from typing import (
BinaryIO,
Literal,
Expand Down Expand Up @@ -79,7 +80,7 @@ def _make_selector(pattern_parts, _flavour, case_sensitive=True): # noqa: F811
from cloudpathlib.enums import FileCacheMode

from . import anypath

from .decorators import class_or_instancemethod
from .exceptions import (
ClientMismatchError,
CloudPathFileExistsError,
Expand Down Expand Up @@ -203,7 +204,12 @@ def __init__(cls, name: str, bases: Tuple[type, ...], dic: Dict[str, Any]) -> No
and getattr(getattr(Path, attr), "__doc__", None)
):
docstring = getattr(Path, attr).__doc__ + " _(Docstring copied from pathlib.Path)_"
getattr(cls, attr).__doc__ = docstring

if isinstance(getattr(cls, attr), (MethodType)):
getattr(cls, attr).__func__.__doc__ = docstring
else:
getattr(cls, attr).__doc__ = docstring

if isinstance(getattr(cls, attr), property):
# Properties have __doc__ duplicated under fget, and at least some parsers
# read it from there.
Expand Down Expand Up @@ -436,8 +442,13 @@ def is_file(self, follow_symlinks=True) -> bool:
def fspath(self) -> str:
return self.__fspath__()

def from_uri(self, uri: str) -> Self:
return self._new_cloudpath(uri)
@class_or_instancemethod
def from_uri(cls, uri: str) -> Self:
if isinstance(cls, type):
return cls(uri)
else:
# 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:
Expand Down Expand Up @@ -919,10 +930,13 @@ def name(self) -> str:

def full_match(self, pattern: str, case_sensitive: Optional[bool] = None) -> bool:
# strip scheme from start of pattern before testing
if pattern.startswith(self.anchor + self.drive + "/"):
pattern = pattern[len(self.anchor + self.drive + "/") :]
if pattern.startswith(self.anchor + self.drive):
pattern = pattern[len(self.anchor + self.drive) :]

return self._dispatch_to_path("full_match", pattern, case_sensitive=case_sensitive)
# remove drive, which is kept on normal dispatch to pathlib
return PurePosixPath(self._no_prefix_no_drive).full_match(
pattern, case_sensitive=case_sensitive
)

def match(self, path_pattern: str, case_sensitive: Optional[bool] = None) -> bool:
# strip scheme from start of pattern before testing
Expand Down
6 changes: 6 additions & 0 deletions cloudpathlib/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class class_or_instancemethod(classmethod):
"""Allow different behavior depending on if called on instance or class."""

def __get__(self, instance, type_):
descr_get = super().__get__ if instance is None else self.__func__.__get__
return descr_get(instance, type_)
16 changes: 16 additions & 0 deletions tests/test_cloudpath_instantiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def test_dispatch(path_class, cloud_path, monkeypatch):
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "fake-project")

assert isinstance(CloudPath(cloud_path), path_class)
assert isinstance(CloudPath.from_uri(cloud_path), path_class)


def test_dispatch_error():
Expand Down Expand Up @@ -74,6 +75,21 @@ def test_instantiation_errors(rig):
rig.path_class("NOT_S3_PATH")


def test_from_uri(rig):
p = rig.create_cloud_path("dir_0/file0_0.txt")

new_client = rig.client_class(**rig.required_client_kwargs)
p2 = new_client.CloudPath(f"{rig.cloud_prefix}{rig.drive}/{rig.test_dir}/dir_0/file0_0.txt")

# classmethod uses default client
assert rig.path_class.from_uri(str(p)) == p
assert rig.path_class.from_uri(str(p)).client == p.client

# instance method uses same client
assert p2.from_uri(str(p2)) == p2
assert p2.from_uri(str(p2)).client == new_client


def test_idempotency(rig):
rig.client_class._default_client = None

Expand Down
13 changes: 13 additions & 0 deletions tests/test_cloudpath_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,16 @@ def test_sorting(rig):
assert cp1 > str(cp1)
with pytest.raises(TypeError):
assert cp1 >= str(cp1)


def test_full_match(rig):
if sys.version_info > (3, 13):
assert rig.create_cloud_path("a/b/c").full_match("**/a/b/c")
assert not rig.create_cloud_path("a/b/c").full_match("**/a/b/c/d")
assert rig.create_cloud_path("a/b.py").full_match("**/a/*.py")
assert not rig.create_cloud_path("a/b.py").full_match("*.py")
assert rig.create_cloud_path("/a/b/c.py").full_match("**/a/**")

cp: CloudPath = rig.create_cloud_path("file.txt")
assert cp.full_match(cp._no_prefix_no_drive)
assert cp.full_match(str(cp))

0 comments on commit b910486

Please sign in to comment.