From 6ec16524165c8070014ef7945cbbf13b96604441 Mon Sep 17 00:00:00 2001 From: Christian Behrens Date: Fri, 19 Aug 2022 19:47:47 +0200 Subject: [PATCH 1/2] draft abstractmethods for anypath --- cloudpathlib/anypath.py | 190 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 3 deletions(-) diff --git a/cloudpathlib/anypath.py b/cloudpathlib/anypath.py index 197dbe65..85bc6e0d 100644 --- a/cloudpathlib/anypath.py +++ b/cloudpathlib/anypath.py @@ -1,13 +1,31 @@ +from __future__ import absolute_import import os -from abc import ABC +from abc import ABCMeta, abstractmethod from pathlib import Path -from typing import Union +from typing import Generator, Iterable, List, Sequence, Tuple, Union from .cloudpath import InvalidPrefixError, CloudPath from .exceptions import AnyPathTypeError -class AnyPath(ABC): +class AnyPathMeta(ABCMeta): + def __init__(cls, name, bases, dic): + # Copy docstring from pathlib.Path + for attr in dir(cls): + if ( + not attr.startswith("_") + and hasattr(Path, attr) + 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), property): + # Properties have __doc__ duplicated under fget, and at least some parsers + # read it from there. + getattr(cls, attr).fget.__doc__ = docstring + + +class AnyPath(metaclass=AnyPathMeta): """Polymorphic virtual superclass for CloudPath and pathlib.Path. Constructing an instance will automatically dispatch to CloudPath or Path based on the input. It also supports both isinstance and issubclass checks. @@ -43,6 +61,172 @@ def _validate(cls, value) -> Union[CloudPath, Path]: # Note __new__ is static method and not a class method return cls.__new__(cls, value) + @property + @abstractmethod + def anchor(self) -> str: + pass + + @property + @abstractmethod + def name(self) -> str: + pass + + @property + @abstractmethod + def suffix(self) -> str: + pass + + @property + @abstractmethod + def suffixes(self) -> List[str]: + pass + + @property + @abstractmethod + def stem(self) -> str: + pass + + @property + @abstractmethod + def parts(self) -> Tuple[str]: + pass + + @property + @abstractmethod + def parent(self) -> "AnyPath": + pass + + @property + @abstractmethod + def parents(self) -> Sequence["AnyPath"]: + pass + + @property + @abstractmethod + def drive(self) -> str: + pass + + @abstractmethod + def absolute(self) -> "AnyPath": + pass + + @abstractmethod + def as_uri(self) -> str: + pass + + @abstractmethod + def exists(self) -> bool: + pass + + @abstractmethod + def glob(self, pattern: str) -> Generator["AnyPath", None, None]: + pass + + @abstractmethod + def is_absolute(self) -> bool: + pass + + @abstractmethod + def is_dir(self) -> bool: + pass + + @abstractmethod + def is_file(self) -> bool: + pass + + @abstractmethod + def is_relative_to(self, other) -> bool: + pass + + @abstractmethod + def iterdir(self) -> Iterable["AnyPath"]: + pass + + @abstractmethod + def joinpath(self, *args) -> "AnyPath": + pass + + @abstractmethod + def match(self, path_pattern: str) -> bool: + pass + + @abstractmethod + def mkdir(self, parents: bool = False, exist_ok: bool = False) -> None: + """docstring? has mode for pathlib""" + pass + + @abstractmethod + def open(self, mode="r", buffering=-1, encoding=None, errors=None, newline=None, **kwargs): + pass + + @abstractmethod + def read_bytes(self) -> bytes: + pass + + @abstractmethod + def read_text(self, *args, **kwargs) -> str: + pass + + @abstractmethod + def relative_to(self, other) -> Path: + pass + + @abstractmethod + def rename(self, target: "AnyPath") -> "AnyPath": + """Add docstring as behavior for CloudPath differs""" + pass + + @abstractmethod + def replace(self, target) -> "AnyPath": + """Add docstring as behavior for CloudPath differs""" + pass + + @abstractmethod + def resolve(self) -> "AnyPath": + pass + + @abstractmethod + def rglob(self, pattern: str) -> Generator["AnyPath", None, None]: + pass + + @abstractmethod + def rmdir(self) -> None: + pass + + @abstractmethod + def samefile(self, other_path) -> bool: + pass + + @abstractmethod + def stat(self): + """docstring? has mode for pathlib""" + pass + + @abstractmethod + def touch(self, exist_ok: bool = True) -> None: + """docstring? has mode for pathlib""" + pass + + @abstractmethod + def unlink(self, missing_ok=False) -> None: + pass + + @abstractmethod + def with_name(self, name: str) -> "AnyPath": + pass + + @abstractmethod + def with_suffix(self, suffix: str) -> "AnyPath": + pass + + @abstractmethod + def write_bytes(self, data: bytes) -> int: + pass + + @abstractmethod + def write_text(self, data: str, encoding=None, errors=None) -> int: + pass + AnyPath.register(CloudPath) # type: ignore AnyPath.register(Path) From 3d455b3b9be21a4567d92ba7fbce87a79f318170 Mon Sep 17 00:00:00 2001 From: Christian Behrens Date: Fri, 19 Aug 2022 19:49:02 +0200 Subject: [PATCH 2/2] some more type hints for cloudpath --- cloudpathlib/cloudpath.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cloudpathlib/cloudpath.py b/cloudpathlib/cloudpath.py index be93bbc0..ce732035 100644 --- a/cloudpathlib/cloudpath.py +++ b/cloudpathlib/cloudpath.py @@ -323,7 +323,7 @@ def exists(self) -> bool: def fspath(self) -> str: return self.__fspath__() - def _glob_checks(self, pattern): + def _glob_checks(self, pattern: str): if ".." in pattern: raise CloudPathNotImplementedError( "Relative paths with '..' not supported in glob patterns." @@ -346,7 +346,7 @@ def _glob(self, selector): for p in selector.select_from(root): yield self.client.CloudPath(f"{self.cloud_prefix}{self.drive}{p}") - def glob(self, pattern): + def glob(self, pattern: str): self._glob_checks(pattern) pattern_parts = PurePosixPath(pattern).parts @@ -354,7 +354,7 @@ def glob(self, pattern): yield from self._glob(selector) - def rglob(self, pattern): + def rglob(self, pattern: str): self._glob_checks(pattern) pattern_parts = PurePosixPath(pattern).parts @@ -566,7 +566,7 @@ def relative_to(self, other): ) return self._path.relative_to(other._path) - def is_relative_to(self, other): + def is_relative_to(self, other) -> bool: try: self.relative_to(other) return True @@ -612,10 +612,10 @@ def suffix(self): def suffixes(self): return self._dispatch_to_path("suffixes") - def with_name(self, name): + def with_name(self, name: str): return self._dispatch_to_path("with_name", name) - def with_suffix(self, suffix): + def with_suffix(self, suffix: str): return self._dispatch_to_path("with_suffix", suffix) # ====================== DISPATCHED TO LOCAL CACHE FOR CONCRETE PATHS ====================== @@ -1001,7 +1001,7 @@ def _filter_children(self, rel_to): } @staticmethod - def _is_relative_to(maybe_child, maybe_parent): + def _is_relative_to(maybe_child, maybe_parent) -> bool: try: maybe_child.relative_to(maybe_parent) return True