diff --git a/HISTORY.md b/HISTORY.md index 563f64bb..830326c2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -15,6 +15,7 @@ - Changed `LocalClient` so that client instances using the default storage access the default local storage directory through the `get_default_storage_dir` rather than having an explicit reference to the path set at instantiation. This means that calling `get_default_storage_dir` will reset the local storage for all clients using the default local storage, whether the client has already been instantiated or is instantiated after resetting. This fixes unintuitive behavior where `reset_local_storage` did not reset local storage when using the default client. (Issue [#414](https://github.com/drivendataorg/cloudpathlib/issues/414)) - Added a new `local_storage_dir` property to `LocalClient`. This will return the current local storage directory used by that client instance. by reference through the `get_default_ rather than with an explicit. +- Refined the return type annotations for `CloudPath.open()` to match the behavior of `pathlib.Path.open()`. The method now returns specific types (`TextIOWrapper`, `FileIO`, `BufferedRandom`, `BufferedWriter`, `BufferedReader`, `BinaryIO`, `IO[Any]`) based on the provided `mode`, `buffering`, and `encoding` arguments. ([Issue #465](https://github.com/drivendataorg/cloudpathlib/issues/465), [PR #464](https://github.com/drivendataorg/cloudpathlib/pull/464)) - Added Azure Data Lake Storage Gen2 support (Issue [#161](https://github.com/drivendataorg/cloudpathlib/issues/161), PR [#450](https://github.com/drivendataorg/cloudpathlib/pull/450)), thanks to [@M0dEx](https://github.com/M0dEx) for PR [#447](https://github.com/drivendataorg/cloudpathlib/pull/447) and PR [#449](https://github.com/drivendataorg/cloudpathlib/pull/449) ## v0.18.1 (2024-02-26) diff --git a/cloudpathlib/cloudpath.py b/cloudpathlib/cloudpath.py index d7bf391b..4aa895a6 100644 --- a/cloudpathlib/cloudpath.py +++ b/cloudpathlib/cloudpath.py @@ -2,6 +2,7 @@ from collections import defaultdict import collections.abc from contextlib import contextmanager +from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper import os from pathlib import ( # type: ignore Path, @@ -14,6 +15,8 @@ import shutil import sys from typing import ( + BinaryIO, + Literal, overload, Any, Callable, @@ -34,10 +37,20 @@ from urllib.parse import urlparse from warnings import warn +if TYPE_CHECKING: + from _typeshed import ( + OpenBinaryMode, + OpenBinaryModeReading, + OpenBinaryModeUpdating, + OpenBinaryModeWriting, + OpenTextMode, + ) + if sys.version_info >= (3, 10): from typing import TypeGuard else: from typing_extensions import TypeGuard + if sys.version_info >= (3, 11): from typing import Self else: @@ -543,6 +556,90 @@ def walk( else: raise + @overload + def open( + self, + 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": ... + + @overload + def open( + self, + 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": ... + + @overload + def open( + self, + 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": ... + + @overload + def open( + self, + 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": ... + + @overload + def open( + self, + 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": ... + + @overload + def open( + self, + 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": ... + + @overload + def open( + self, + 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]": ... + def open( self, mode: str = "r", @@ -552,7 +649,7 @@ def open( 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]: + ) -> "IO[Any]": # if trying to call open on a directory that exists if self.exists() and not self.is_file(): raise CloudPathIsADirectoryError(