Skip to content

cloudpathlib.AzureBlobPath

Bases: CloudPath

Class for representing and operating on Azure Blob Storage URIs, in the style of the Python standard library's pathlib module. Instances represent a path in Blob Storage with filesystem path semantics, and convenient methods allow for basic operations like joining, reading, writing, iterating over contents, etc. This class almost entirely mimics the pathlib.Path interface, so most familiar properties and methods should be available and behave in the expected way.

The AzureBlobClient class handles authentication with Azure. If a client instance is not explicitly specified on AzureBlobPath instantiation, a default client is used. See AzureBlobClient's documentation for more details.

Source code in cloudpathlib/azure/azblobpath.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
@register_path_class("azure")
class AzureBlobPath(CloudPath):
    """Class for representing and operating on Azure Blob Storage URIs, in the style of the Python
    standard library's [`pathlib` module](https://docs.python.org/3/library/pathlib.html).
    Instances represent a path in Blob Storage with filesystem path semantics, and convenient
    methods allow for basic operations like joining, reading, writing, iterating over contents,
    etc. This class almost entirely mimics the [`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)
    interface, so most familiar properties and methods should be available and behave in the
    expected way.

    The [`AzureBlobClient`](../azblobclient/) class handles authentication with Azure. If a
    client instance is not explicitly specified on `AzureBlobPath` instantiation, a default client
    is used. See `AzureBlobClient`'s documentation for more details.
    """

    cloud_prefix: str = "az://"
    client: "AzureBlobClient"

    @property
    def drive(self) -> str:
        return self.container

    def mkdir(
        self, parents=False, exist_ok=False, mode: Optional[Any] = None, *, parent_mode=None
    ):
        self.client._mkdir(self, parents=parents, exist_ok=exist_ok)

    def touch(self, exist_ok: bool = True, mode: Optional[Any] = None):
        if self.exists():
            if not exist_ok:
                raise FileExistsError(f"File exists: {self}")
            self.client._move_file(self, self)
        else:
            tf = TemporaryDirectory()
            p = Path(tf.name) / "empty"
            p.touch()

            self.client._upload_file(p, self)

            tf.cleanup()

    def stat(self, follow_symlinks=True):
        try:
            meta = self.client._get_metadata(self)
        except ResourceNotFoundError:
            raise NoStatError(
                f"No stats available for {self}; it may be a directory or not exist."
            )

        return os.stat_result(
            (
                None,  # mode
                None,  # ino
                self.cloud_prefix,  # dev,
                None,  # nlink,
                None,  # uid,
                None,  # gid,
                meta.get("size", 0),  # size,
                None,  # atime,
                meta.get("last_modified", 0).timestamp(),  # mtime,
                None,  # ctime,
            )
        )

    def replace(self, target: "AzureBlobPath") -> "AzureBlobPath":
        try:
            return super().replace(target)

        # we can rename directories on ADLS Gen2
        except CloudPathIsADirectoryError:
            if self.client._check_hns(self):
                return self.client._move_file(self, target)
            else:
                raise

    @property
    def container(self) -> str:
        return self._no_prefix.split("/", 1)[0]

    @property
    def blob(self) -> str:
        key = self._no_prefix_no_drive

        # key should never have starting slash for
        if key.startswith("/"):
            key = key[1:]

        return key

    @property
    def etag(self):
        return self.client._get_metadata(self).get("etag", None)

    @property
    def md5(self) -> str:
        return self.client._get_metadata(self).get("content_settings", {}).get("content_md5", None)

Attributes

anchor: str property

blob: str property

client: AzureBlobClient instance-attribute

cloud_prefix: str = 'az://' class-attribute instance-attribute

container: str property

drive: str property

etag property

fspath: str property

md5: str property

name: str property

parent: Self property

parents: Tuple[Self, ...] property

parser: Self property

parts: Tuple[str, ...] property

stem: str property

suffix: str property

suffixes: List[str] property

Methods:

__init__(cloud_path: Union[str, Self, CloudPath], *parts: str, client: Optional[Client] = None) -> None

Source code in cloudpathlib/cloudpath.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
def __init__(
    self,
    cloud_path: Union[str, Self, "CloudPath"],
    *parts: str,
    client: Optional["Client"] = None,
) -> None:
    # handle if local file gets opened. must be set at the top of the method in case any code
    # below raises an exception, this prevents __del__ from raising an AttributeError
    self._handle: Optional[IO] = None
    self._client: Optional["Client"] = None

    if parts:
        # ensure first part ends in "/"; (sometimes it is just prefix, sometimes a longer path)
        if not str(cloud_path).endswith("/"):
            cloud_path = str(cloud_path) + "/"

        cloud_path = str(cloud_path) + "/".join(p.strip("/") for p in parts)

    self.is_valid_cloudpath(cloud_path, raise_on_error=True)
    self._cloud_meta.validate_completeness()

    # versions of the raw string that provide useful methods
    self._str = str(cloud_path)
    self._url = urlparse(self._str)
    self._path = PurePosixPath(f"/{self._no_prefix}")

    # setup client
    if client is None:
        if isinstance(cloud_path, CloudPath):
            self._client = cloud_path.client
    else:
        self._client = client

    if client is not None and not isinstance(client, self._cloud_meta.client_class):
        raise ClientMismatchError(
            f"Client of type [{client.__class__}] is not valid for cloud path of type "
            f"[{self.__class__}]; must be instance of [{self._cloud_meta.client_class}], or "
            f"None to use default client for this cloud path class."
        )

    # track if local has been written to, if so it may need to be uploaded
    self._dirty = False

absolute() -> Self

Source code in cloudpathlib/cloudpath.py
1141
1142
def absolute(self) -> Self:
    return self

as_uri() -> str

Source code in cloudpathlib/cloudpath.py
498
499
def as_uri(self) -> str:
    return str(self)

as_url(presign: bool = False, expire_seconds: int = 60 * 60) -> str

Source code in cloudpathlib/cloudpath.py
480
481
482
483
484
485
def as_url(self, presign: bool = False, expire_seconds: int = 60 * 60) -> str:
    if presign:
        url = self.client._generate_presigned_url(self, expire_seconds=expire_seconds)
    else:
        url = self.client._get_public_url(self)
    return url

clear_cache()

Removes cache if it exists

Source code in cloudpathlib/cloudpath.py
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
def clear_cache(self):
    """Removes cache if it exists"""
    try:
        local = self._local
    except CloudPathLocalPathTraversalError:
        # a key that cannot be safely mapped to a local path can never have been cached,
        # so there is nothing to clear (and __del__ must not raise on such paths)
        return
    if local.exists():
        if local.is_file():
            local.unlink()
        else:
            shutil.rmtree(local)

copy(target: Union[str, os.PathLike, Self], follow_symlinks: bool = True, preserve_metadata: bool = False, force_overwrite_to_cloud: Optional[bool] = None) -> Union[Path, Self]

copy(
    target: Self,
    follow_symlinks=True,
    preserve_metadata=False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Self
copy(
    target: Path,
    follow_symlinks=True,
    preserve_metadata=False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Path
copy(
    target: str,
    follow_symlinks=True,
    preserve_metadata=False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, CloudPath]

Copy self to target folder or file, if self is a file.

Source code in cloudpathlib/cloudpath.py
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
def copy(
    self,
    target: Union[str, os.PathLike, Self],
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, Self]:
    """Copy self to target folder or file, if self is a file."""
    return self._copy(
        target,
        follow_symlinks=follow_symlinks,
        preserve_metadata=preserve_metadata,
        force_overwrite_to_cloud=force_overwrite_to_cloud,
        remove_src=False,
    )

copy_into(target_dir: Union[str, os.PathLike, Self], follow_symlinks: bool = True, preserve_metadata: bool = False, force_overwrite_to_cloud: Optional[bool] = None) -> Union[Path, Self]

copy_into(
    target_dir: Self,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Self
copy_into(
    target_dir: Path,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Path
copy_into(
    target_dir: str,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, CloudPath]

Copy self into target directory, preserving the filename.

Source code in cloudpathlib/cloudpath.py
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
def copy_into(
    self,
    target_dir: Union[str, os.PathLike, Self],
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, Self]:
    """Copy self into target directory, preserving the filename."""
    target_path = anypath.to_anypath(target_dir) / self.name

    result = self._copy(
        target_path,
        follow_symlinks=follow_symlinks,
        preserve_metadata=preserve_metadata,
        force_overwrite_to_cloud=force_overwrite_to_cloud,
        remove_src=False,
    )
    return cast(Union[Path, Self], result)

copytree(destination, force_overwrite_to_cloud=None, ignore=None)

copytree(
    destination: Self,
    force_overwrite_to_cloud: Optional[bool] = None,
    ignore: Optional[
        Callable[[str, Iterable[str]], Container[str]]
    ] = None,
) -> Self
copytree(
    destination: Path,
    force_overwrite_to_cloud: Optional[bool] = None,
    ignore: Optional[
        Callable[[str, Iterable[str]], Container[str]]
    ] = None,
) -> Path
copytree(
    destination: Union[str, os.PathLike, Self],
    force_overwrite_to_cloud: Optional[bool] = None,
    ignore: Optional[
        Callable[[str, Iterable[str]], Container[str]]
    ] = None,
) -> Union[Path, CloudPath]

Copy self to a directory, if self is a directory.

Source code in cloudpathlib/cloudpath.py
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
def copytree(self, destination, force_overwrite_to_cloud=None, ignore=None):
    """Copy self to a directory, if self is a directory."""
    if not self.is_dir():
        raise CloudPathNotADirectoryError(
            f"Origin path {self} must be a directory. To copy a single file use the method copy."
        )

    destination = anypath.to_anypath(destination)

    if destination.exists() and destination.is_file():
        raise CloudPathFileExistsError(
            f"Destination path {destination} of copytree must be a directory."
        )

    contents = list(self.iterdir())

    if ignore is not None:
        ignored_names = ignore(self._no_prefix_no_drive, [x.name for x in contents])
    else:
        ignored_names = set()

    destination.mkdir(parents=True, exist_ok=True)

    # when copying to a local destination, a listing may surface a key's ".." segment as a
    # directory entry named "..", so each child destination must be confirmed within the
    # destination before recursing (otherwise the recursion walks outside it); cloud-to-cloud
    # copytree is not a local-filesystem traversal concern
    destination_is_local = isinstance(destination, Path)

    for subpath in contents:
        if subpath.name in ignored_names:
            continue
        if subpath.is_file():
            file_destination = destination / subpath.name
            if destination_is_local:
                _ensure_local_path_within_base(file_destination, destination, subpath)
            subpath.copy(file_destination, force_overwrite_to_cloud=force_overwrite_to_cloud)
        elif subpath.is_dir():
            dir_destination = destination / (
                subpath.name + ("" if subpath.name.endswith("/") else "/")
            )
            if destination_is_local:
                _ensure_local_path_within_base(dir_destination, destination, subpath)
            subpath.copytree(
                dir_destination,
                force_overwrite_to_cloud=force_overwrite_to_cloud,
                ignore=ignore,
            )

    return destination

download_to(destination: Union[str, os.PathLike]) -> Path

Source code in cloudpathlib/cloudpath.py
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
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 = _ensure_local_path_within_base(
                destination / self.name, destination, self
            )
        return self.client._download_file(self, destination)
    else:
        destination.mkdir(exist_ok=True)
        for f in self.iterdir():
            rel = str(self)
            if not rel.endswith("/"):
                rel = rel + "/"

            rel_dest = str(f)[len(rel) :]
            target = _ensure_local_path_within_base(destination / rel_dest, destination, f)
            f.download_to(target)

        return destination

exists(follow_symlinks=True) -> bool

Source code in cloudpathlib/cloudpath.py
501
502
def exists(self, follow_symlinks=True) -> bool:
    return self.client._exists(self)

from_uri(uri: str) -> Self classmethod

Source code in cloudpathlib/cloudpath.py
514
515
516
@classmethod
def from_uri(cls, uri: str) -> Self:
    return cls(uri)

full_match(pattern: str, case_sensitive: Optional[bool] = None) -> bool

Source code in cloudpathlib/cloudpath.py
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
def full_match(self, pattern: str, case_sensitive: Optional[bool] = None) -> bool:
    if sys.version_info < (3, 13):
        raise NotImplementedError("full_match requires Python 3.13 or higher")

    # strip scheme from start of pattern before testing
    if pattern.startswith(self.anchor + self.drive):
        pattern = pattern[len(self.anchor + self.drive) :]
    elif pattern.startswith(self.anchor):
        # for http paths, keep leading slash
        pattern = pattern[len(self.anchor) - 1 :]

    # remove drive, which is kept on normal dispatch to pathlib
    return PurePosixPath(self._no_prefix_no_drive).full_match(  # type: ignore[attr-defined]
        pattern, case_sensitive=case_sensitive
    )

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

Source code in cloudpathlib/cloudpath.py
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
def glob(
    self,
    pattern: Union[str, os.PathLike],
    case_sensitive: Optional[bool] = None,
    recurse_symlinks: bool = True,
) -> Generator[Self, None, None]:
    pattern = self._glob_checks(pattern)

    pattern_parts = PurePosixPath(pattern).parts
    selector = _make_selector(
        tuple(pattern_parts), _posix_flavour, case_sensitive=case_sensitive
    )

    yield from self._glob(
        selector,
        "/" in pattern
        or "**"
        in pattern,  # recursive listing needed if explicit ** or any sub folder in pattern
    )

info() -> CloudPathInfo

Return a CloudPathInfo object for this path.

Source code in cloudpathlib/cloudpath.py
1288
1289
1290
def info(self) -> "CloudPathInfo":
    """Return a CloudPathInfo object for this path."""
    return CloudPathInfo(self)

is_absolute() -> bool

Source code in cloudpathlib/cloudpath.py
1144
1145
def is_absolute(self) -> bool:
    return True

is_dir(follow_symlinks=True) -> bool

Source code in cloudpathlib/cloudpath.py
504
505
def is_dir(self, follow_symlinks=True) -> bool:
    return self.client._is_file_or_dir(self) == "dir"

is_file(follow_symlinks=True) -> bool

Source code in cloudpathlib/cloudpath.py
507
508
def is_file(self, follow_symlinks=True) -> bool:
    return self.client._is_file_or_dir(self) == "file"

is_junction()

Source code in cloudpathlib/cloudpath.py
1088
1089
def is_junction(self):
    return False  # only windows paths can be junctions, not cloudpaths

is_relative_to(other: Self) -> bool

Source code in cloudpathlib/cloudpath.py
1167
1168
1169
1170
1171
1172
def is_relative_to(self, other: Self) -> bool:
    try:
        self.relative_to(other)
        return True
    except ValueError:
        return False

is_valid_cloudpath(path: Union[str, CloudPath], raise_on_error: bool = False) -> Union[bool, TypeGuard[Self]] classmethod

is_valid_cloudpath(
    path: CloudPath, raise_on_error: bool = ...
) -> TypeGuard[Self]
is_valid_cloudpath(
    path: str, raise_on_error: bool = ...
) -> bool
Source code in cloudpathlib/cloudpath.py
373
374
375
376
377
378
379
380
381
382
383
384
@classmethod
def is_valid_cloudpath(
    cls, path: Union[str, "CloudPath"], raise_on_error: bool = False
) -> Union[bool, TypeGuard[Self]]:
    valid = str(path).lower().startswith(cls.cloud_prefix.lower())

    if raise_on_error and not valid:
        raise InvalidPrefixError(
            f"'{path}' is not a valid path since it does not start with '{cls.cloud_prefix}'"
        )

    return valid

iterdir() -> Generator[Self, None, None]

Source code in cloudpathlib/cloudpath.py
619
620
621
622
def iterdir(self) -> Generator[Self, None, None]:
    for f, _ in self.client._list_dir(self, recursive=False):
        if f != self:  # iterdir does not include itself in pathlib
            yield f

joinpath(*pathsegments: Union[str, os.PathLike]) -> Self

Source code in cloudpathlib/cloudpath.py
1138
1139
def joinpath(self, *pathsegments: Union[str, os.PathLike]) -> Self:
    return self._dispatch_to_path("joinpath", *pathsegments)

match(path_pattern: str, case_sensitive: Optional[bool] = None) -> bool

Source code in cloudpathlib/cloudpath.py
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
def match(self, path_pattern: str, case_sensitive: Optional[bool] = None) -> bool:
    # strip scheme from start of pattern before testing
    if path_pattern.startswith(self.anchor + self.drive + "/"):
        path_pattern = path_pattern[len(self.anchor + self.drive + "/") :]

    kwargs = dict(case_sensitive=case_sensitive)

    if sys.version_info < (3, 12):
        kwargs.pop("case_sensitive")

    return self._dispatch_to_path("match", path_pattern, **kwargs)

mkdir(parents=False, exist_ok=False, mode: Optional[Any] = None, *, parent_mode=None)

Source code in cloudpathlib/azure/azblobpath.py
41
42
43
44
def mkdir(
    self, parents=False, exist_ok=False, mode: Optional[Any] = None, *, parent_mode=None
):
    self.client._mkdir(self, parents=parents, exist_ok=exist_ok)

move(target: Union[str, os.PathLike, Self], follow_symlinks: bool = True, preserve_metadata: bool = False, force_overwrite_to_cloud: Optional[bool] = None) -> Union[Path, Self]

move(
    target: Self,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Self
move(
    target: Path,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Path
move(
    target: str,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, CloudPath]

Move self to target location, removing the source.

Source code in cloudpathlib/cloudpath.py
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
def move(
    self,
    target: Union[str, os.PathLike, Self],
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, Self]:
    """Move self to target location, removing the source."""
    return self._copy(
        target,
        follow_symlinks=follow_symlinks,
        preserve_metadata=preserve_metadata,
        force_overwrite_to_cloud=force_overwrite_to_cloud,
        remove_src=True,
    )

move_into(target_dir: Union[str, os.PathLike, Self], follow_symlinks: bool = True, preserve_metadata: bool = False, force_overwrite_to_cloud: Optional[bool] = None) -> Union[Path, Self]

move_into(
    target_dir: Self,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Self
move_into(
    target_dir: Path,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Path
move_into(
    target_dir: str,
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, CloudPath]

Move self into target directory, preserving the filename and removing the source.

Source code in cloudpathlib/cloudpath.py
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
def move_into(
    self,
    target_dir: Union[str, os.PathLike, Self],
    follow_symlinks: bool = True,
    preserve_metadata: bool = False,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Union[Path, Self]:
    """Move self into target directory, preserving the filename and removing the source."""
    target_path = anypath.to_anypath(target_dir) / self.name

    result = self._copy(
        target_path,
        follow_symlinks=follow_symlinks,
        preserve_metadata=preserve_metadata,
        force_overwrite_to_cloud=force_overwrite_to_cloud,
        remove_src=True,
    )
    return cast(Union[Path, Self], result)

open(mode: str = 'r', buffering: int = -1, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None, force_overwrite_from_cloud: Optional[bool] = None, force_overwrite_to_cloud: Optional[bool] = None) -> IO[Any]

open(
    mode: OpenTextMode = "r",
    buffering: int = -1,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[str] = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> TextIOWrapper
open(
    mode: OpenBinaryMode,
    buffering: Literal[0],
    encoding: None = None,
    errors: None = None,
    newline: None = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> FileIO
open(
    mode: OpenBinaryModeUpdating,
    buffering: Literal[-1, 1] = -1,
    encoding: None = None,
    errors: None = None,
    newline: None = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> BufferedRandom
open(
    mode: OpenBinaryModeWriting,
    buffering: Literal[-1, 1] = -1,
    encoding: None = None,
    errors: None = None,
    newline: None = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> BufferedWriter
open(
    mode: OpenBinaryModeReading,
    buffering: Literal[-1, 1] = -1,
    encoding: None = None,
    errors: None = None,
    newline: None = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> BufferedReader
open(
    mode: OpenBinaryMode,
    buffering: int = -1,
    encoding: None = None,
    errors: None = None,
    newline: None = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> BinaryIO
open(
    mode: str,
    buffering: int = -1,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[str] = None,
    force_overwrite_from_cloud: Optional[bool] = None,
    force_overwrite_to_cloud: Optional[bool] = None,
) -> IO[Any]
Source code in cloudpathlib/cloudpath.py
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
def open(
    self,
    mode: str = "r",
    buffering: int = -1,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[str] = None,
    force_overwrite_from_cloud: Optional[bool] = None,  # extra kwarg not in pathlib
    force_overwrite_to_cloud: Optional[bool] = None,  # extra kwarg not in pathlib
) -> "IO[Any]":
    from .cloud_io import _validate_file_mode

    _validate_file_mode(mode)
    binary_mode = "b" in mode
    if binary_mode and encoding is not None:
        raise ValueError("binary mode doesn't take an encoding argument")
    if binary_mode and errors is not None:
        raise ValueError("binary mode doesn't take an errors argument")
    if binary_mode and newline is not None:
        raise ValueError("binary mode doesn't take a newline argument")
    if not binary_mode and buffering == 0:
        raise ValueError("can't have unbuffered text I/O")

    # if trying to call open on a directory that exists
    exists_on_cloud = self.exists()

    if exists_on_cloud and not self.is_file():
        raise CloudPathIsADirectoryError(
            f"Cannot open directory, only files. Tried to open ({self})"
        )

    if not exists_on_cloud and any(m in mode for m in ("r", "a")):
        raise CloudPathFileNotFoundError(
            f"File opened for read or append, but it does not exist on cloud: {self}"
        )

    if "x" in mode and exists_on_cloud:
        raise CloudPathFileExistsError(f"Cannot open existing file ({self}) for creation.")

    if self.client.file_cache_mode == FileCacheMode.streaming:
        # Append (a) and update (+) modes cannot be done as pure streaming over object
        # storage; they fall through to the cached path for correct semantics.
        if any(m in mode for m in ("a", "+")):
            warn(
                f"Opening {self} with mode {mode!r} in streaming mode downloads the whole "
                f"object to the local cache, modifies it there, and re-uploads it on close "
                f"(object stores cannot append to or update an object in place).",
                stacklevel=2,
            )
        else:
            return self._open_streaming(
                mode,
                buffering,
                encoding,
                errors,
                newline,
                exists_on_cloud=exists_on_cloud,
                force_overwrite_to_cloud=force_overwrite_to_cloud,
            )

    # Standard cached mode
    self._refresh_cache(force_overwrite_from_cloud=force_overwrite_from_cloud)

    # create any directories that may be needed if the file is new
    if not self._local.exists():
        self._local.parent.mkdir(parents=True, exist_ok=True)
        original_mtime = 0.0
    else:
        original_mtime = self._local.stat().st_mtime

    buffer = self._local.open(
        mode=mode,
        buffering=buffering,
        encoding=encoding,
        errors=errors,
        newline=newline,
    )

    # write modes need special on closing the buffer
    if any(m in mode for m in ("w", "+", "x", "a")):
        # dirty, handle, patch close
        wrapped_close = buffer.close

        # since we are pretending this is a cloud file, upload it to the cloud
        # when the buffer is closed
        def _patched_close_upload(*args, **kwargs) -> None:
            wrapped_close(*args, **kwargs)

            # we should be idempotent and not upload again if
            # we already ran our close method patch
            if not self._dirty:
                return

            # original mtime should match what was in the cloud; because of system clocks or rounding
            # by the cloud provider, the new version in our cache is not newer than the original
            # version; explicitly set the new modified time to be after the original modified
            # time. Equal mtimes need the same treatment as older ones: we know this file was
            # written, and the upload requires the local file to be strictly newer.
            if self._local.stat().st_mtime <= original_mtime:
                new_mtime = original_mtime + 1
                os.utime(self._local, times=(new_mtime, new_mtime))

            self._upload_local_to_cloud(force_overwrite_to_cloud=force_overwrite_to_cloud)
            self._dirty = False

        buffer.close = _patched_close_upload  # type: ignore

        # keep reference in case we need to close when __del__ is called on this object
        self._handle = buffer

        # opened for write, so mark dirty
        self._dirty = True

    # if we don't want any cache around, remove the cache as soon as the file is
    # closed; streaming mode only reaches the cache through the append/update
    # fallback and should not leave anything behind either
    if self.client.file_cache_mode in (FileCacheMode.close_file, FileCacheMode.streaming):
        # this may be _patched_close_upload, in which case we need to
        # make sure to call that first so the file gets uploaded
        wrapped_close_for_cache = buffer.close

        def _patched_close_empty_cache(*args, **kwargs):
            wrapped_close_for_cache(*args, **kwargs)

            # remove local file as last step on closing
            self.clear_cache()

        buffer.close = _patched_close_empty_cache  # type: ignore

    return buffer

read_bytes() -> bytes

Source code in cloudpathlib/cloudpath.py
1075
1076
1077
def read_bytes(self) -> bytes:
    with self.open(mode="rb") as f:
        return f.read()

read_text(encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None) -> str

Source code in cloudpathlib/cloudpath.py
1079
1080
1081
1082
1083
1084
1085
1086
def read_text(
    self,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[str] = None,
) -> str:
    with self.open(mode="r", encoding=encoding, errors=errors, newline=newline) as f:
        return f.read()

relative_to(other: Self, walk_up: bool = False) -> PurePosixPath

Source code in cloudpathlib/cloudpath.py
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
def relative_to(self, other: Self, walk_up: bool = False) -> PurePosixPath:
    # We don't dispatch regularly since this never returns a cloud path (since it is relative, and cloud paths are
    # absolute)
    if not isinstance(other, CloudPath):
        raise ValueError(f"{self} is a cloud path, but {other} is not")
    if self.anchor != other.anchor:
        raise ValueError(
            f"{self} is a {self.anchor} path, but {other} is a {other.anchor} path"
        )

    kwargs = dict(walk_up=walk_up)

    if sys.version_info < (3, 12):
        kwargs.pop("walk_up")

    return self._path.relative_to(other._path, **kwargs)  # type: ignore[call-arg]

rename(target: Self) -> Self

Source code in cloudpathlib/cloudpath.py
1015
1016
1017
1018
def rename(self, target: Self) -> Self:
    # for cloud services replace == rename since we don't just rename,
    # we actually move files
    return self.replace(target)

replace(target: AzureBlobPath) -> AzureBlobPath

Source code in cloudpathlib/azure/azblobpath.py
83
84
85
86
87
88
89
90
91
92
def replace(self, target: "AzureBlobPath") -> "AzureBlobPath":
    try:
        return super().replace(target)

    # we can rename directories on ADLS Gen2
    except CloudPathIsADirectoryError:
        if self.client._check_hns(self):
            return self.client._move_file(self, target)
        else:
            raise

resolve(strict: bool = False) -> Self

Source code in cloudpathlib/cloudpath.py
1147
1148
def resolve(self, strict: bool = False) -> Self:
    return self

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

Source code in cloudpathlib/cloudpath.py
604
605
606
607
608
609
610
611
612
613
614
615
616
617
def rglob(
    self,
    pattern: Union[str, os.PathLike],
    case_sensitive: Optional[bool] = None,
    recurse_symlinks: bool = True,
) -> Generator[Self, None, None]:
    pattern = self._glob_checks(pattern)

    pattern_parts = PurePosixPath(pattern).parts
    selector = _make_selector(
        ("**",) + tuple(pattern_parts), _posix_flavour, case_sensitive=case_sensitive
    )

    yield from self._glob(selector, True)

rmdir() -> None

Source code in cloudpathlib/cloudpath.py
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
def rmdir(self) -> None:
    if self.is_file():
        raise CloudPathNotADirectoryError(
            f"Path {self} is a file; call unlink instead of rmdir."
        )
    try:
        next(self.iterdir())
        raise DirectoryNotEmptyError(
            f"Directory not empty: '{self}'. Use rmtree to delete recursively."
        )
    except StopIteration:
        pass
    self.client._remove(self)

rmtree() -> None

Delete an entire directory tree.

Source code in cloudpathlib/cloudpath.py
1318
1319
1320
1321
1322
1323
1324
def rmtree(self) -> None:
    """Delete an entire directory tree."""
    if self.is_file():
        raise CloudPathNotADirectoryError(
            f"Path {self} is a file; call unlink instead of rmtree."
        )
    self.client._remove(self)

samefile(other_path: Union[str, os.PathLike]) -> bool

Source code in cloudpathlib/cloudpath.py
1034
1035
1036
def samefile(self, other_path: Union[str, os.PathLike]) -> bool:
    # all cloud paths are absolute and the paths are used for hash
    return self == other_path

stat(follow_symlinks=True)

Source code in cloudpathlib/azure/azblobpath.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def stat(self, follow_symlinks=True):
    try:
        meta = self.client._get_metadata(self)
    except ResourceNotFoundError:
        raise NoStatError(
            f"No stats available for {self}; it may be a directory or not exist."
        )

    return os.stat_result(
        (
            None,  # mode
            None,  # ino
            self.cloud_prefix,  # dev,
            None,  # nlink,
            None,  # uid,
            None,  # gid,
            meta.get("size", 0),  # size,
            None,  # atime,
            meta.get("last_modified", 0).timestamp(),  # mtime,
            None,  # ctime,
        )
    )

touch(exist_ok: bool = True, mode: Optional[Any] = None)

Source code in cloudpathlib/azure/azblobpath.py
46
47
48
49
50
51
52
53
54
55
56
57
58
def touch(self, exist_ok: bool = True, mode: Optional[Any] = None):
    if self.exists():
        if not exist_ok:
            raise FileExistsError(f"File exists: {self}")
        self.client._move_file(self, self)
    else:
        tf = TemporaryDirectory()
        p = Path(tf.name) / "empty"
        p.touch()

        self.client._upload_file(p, self)

        tf.cleanup()
Source code in cloudpathlib/cloudpath.py
1038
1039
1040
1041
1042
1043
1044
def unlink(self, missing_ok: bool = True) -> None:
    # Note: missing_ok defaults to False in pathlib, but changing the default now would be a breaking change.
    if self.is_dir():
        raise CloudPathIsADirectoryError(
            f"Path {self} is a directory; call rmdir instead of unlink."
        )
    self.client._remove(self, missing_ok)

upload_from(source: Union[str, os.PathLike], force_overwrite_to_cloud: Optional[bool] = None) -> Self

Upload a file or directory to the cloud path.

Source code in cloudpathlib/cloudpath.py
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
def upload_from(
    self,
    source: Union[str, os.PathLike],
    force_overwrite_to_cloud: Optional[bool] = None,
) -> Self:
    """Upload a file or directory to the cloud path."""
    source = Path(source)

    if source.is_dir():
        for p in source.iterdir():
            (self / p.name).upload_from(p, force_overwrite_to_cloud=force_overwrite_to_cloud)

        return self

    else:
        if self.exists() and self.is_dir():
            dst = self / source.name
        else:
            dst = self

        dst._upload_file_to_cloud(source, force_overwrite_to_cloud=force_overwrite_to_cloud)

        return dst

validate(v: str) -> Self classmethod

Used as a Pydantic validator. See https://docs.pydantic.dev/2.0/usage/types/custom/

Source code in cloudpathlib/cloudpath.py
1858
1859
1860
1861
1862
@classmethod
def validate(cls, v: str) -> Self:
    """Used as a Pydantic validator. See
    https://docs.pydantic.dev/2.0/usage/types/custom/"""
    return cls(v)

walk(top_down: bool = True, on_error: Optional[Callable] = None, follow_symlinks: bool = False, lazy: bool = False) -> Generator[Tuple[Self, List[str], List[str]], None, None]

Walk the directory tree rooted at this path, similar to :meth:pathlib.Path.walk and :func:os.walk.

By default (lazy=False) the entire subtree is listed up front with a single recursive listing, which is the fastest option for a full walk on cloud backends.

Pass lazy=True to defer listing until each directory is visited (one non-recursive listing per directory). This is slower for a full walk, but lets callers prune dirnames in place (when top_down=True) to avoid fetching the contents of skipped subtrees entirely -- which can be dramatically faster and cheaper for sparse traversals.

follow_symlinks is accepted for signature parity with pathlib but has no effect on cloud backends, which do not have symlinks.

Source code in cloudpathlib/cloudpath.py
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
def walk(
    self,
    top_down: bool = True,
    on_error: Optional[Callable] = None,
    follow_symlinks: bool = False,
    lazy: bool = False,
) -> Generator[Tuple[Self, List[str], List[str]], None, None]:
    """Walk the directory tree rooted at this path, similar to
    :meth:`pathlib.Path.walk` and :func:`os.walk`.

    By default (``lazy=False``) the entire subtree is listed up front with a
    single recursive listing, which is the fastest option for a full walk on
    cloud backends.

    Pass ``lazy=True`` to defer listing until each directory is visited (one
    non-recursive listing per directory). This is slower for a full walk,
    but lets callers prune ``dirnames`` in place (when ``top_down=True``) to
    avoid fetching the contents of skipped subtrees entirely -- which can be
    dramatically faster and cheaper for sparse traversals.

    ``follow_symlinks`` is accepted for signature parity with ``pathlib``
    but has no effect on cloud backends, which do not have symlinks.
    """
    if lazy:
        yield from self._walk_lazy(top_down=top_down, on_error=on_error)
        return

    try:
        file_tree = self._build_subtree(recursive=True)  # walking is always recursive
        yield from self._walk_results_from_tree(self, file_tree, top_down=top_down)

    except Exception as e:
        if on_error is not None:
            on_error(e)
        else:
            raise

with_name(name: str) -> Self

Source code in cloudpathlib/cloudpath.py
1244
1245
def with_name(self, name: str) -> Self:
    return self._dispatch_to_path("with_name", name)

with_segments(*pathsegments) -> Self

Create a new CloudPath with the same client out of the given segments. The first segment will be interpreted as the bucket/container name.

Source code in cloudpathlib/cloudpath.py
1247
1248
1249
1250
1251
def with_segments(self, *pathsegments) -> Self:
    """Create a new CloudPath with the same client out of the given segments.
    The first segment will be interpreted as the bucket/container name.
    """
    return self._new_cloudpath("/".join(pathsegments))

with_stem(stem: str) -> Self

Source code in cloudpathlib/cloudpath.py
1241
1242
def with_stem(self, stem: str) -> Self:
    return self._dispatch_to_path("with_stem", stem)

with_suffix(suffix: str) -> Self

Source code in cloudpathlib/cloudpath.py
1253
1254
def with_suffix(self, suffix: str) -> Self:
    return self._dispatch_to_path("with_suffix", suffix)

write_bytes(data: bytes) -> int

Open the file in bytes mode, write to it, and close the file.

NOTE: vendored from pathlib since we override open https://github.com/python/cpython/blob/3.8/Lib/pathlib.py#L1235-L1242

Source code in cloudpathlib/cloudpath.py
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
def write_bytes(self, data: bytes) -> int:
    """Open the file in bytes mode, write to it, and close the file.

    NOTE: vendored from pathlib since we override open
    https://github.com/python/cpython/blob/3.8/Lib/pathlib.py#L1235-L1242
    """
    # type-check for the buffer interface before truncating the file
    view = memoryview(data)
    with self.open(mode="wb") as f:
        return f.write(view)

write_text(data: str, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None) -> int

Open the file in text mode, write to it, and close the file.

NOTE: vendored from pathlib since we override open https://github.com/python/cpython/blob/3.10/Lib/pathlib.py#L1146-L1155

Source code in cloudpathlib/cloudpath.py
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
def write_text(
    self,
    data: str,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[str] = None,
) -> int:
    """Open the file in text mode, write to it, and close the file.

    NOTE: vendored from pathlib since we override open
    https://github.com/python/cpython/blob/3.10/Lib/pathlib.py#L1146-L1155
    """
    if not isinstance(data, str):
        raise TypeError("data must be str, not %s" % data.__class__.__name__)

    with self.open(mode="w", encoding=encoding, errors=errors, newline=newline) as f:
        return f.write(data)