8000 add new pathlib base classes for 3.13 by tungol · Pull Request #12937 · python/typeshed · GitHub
[go: up one dir, main page]

Skip to content

add new pathlib base classes for 3.13 #12937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add new pathlib base classes for 3.13
  • Loading branch information
tungol committed Oct 31, 2024
commit 694954a393d067dc3449b58d1c97c19453d9e273
1 change: 1 addition & 0 deletions stdlib/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ os: 3.0-
ossaudiodev: 3.0-3.12
parser: 3.0-3.9
pathlib: 3.4-
pathlib._abc: 3.13-
pdb: 3.0-
pickle: 3.0-
pickletools: 3.0-
Expand Down
10 changes: 8 additions & 2 deletions stdlib/pathlib.pyi → stdlib/pathlib/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
from typing import IO, Any, BinaryIO, ClassVar, Literal, overload
from typing_extensions import Self, deprecated

if sys.version_info >= (3, 13):
from pathlib._abc import PathBase, PurePathBase
else:
PathBase = object
PurePathBase = object

if sys.version_info >= (3, 9):
from types import GenericAlias

Expand All @@ -26,7 +32,7 @@
if sys.version_info >= (3, 13):
__all__ += ["UnsupportedOperation"]

class PurePath(PathLike[str]):
class PurePath(PathLike[str], PurePathBase):
if sys.version_info >= (3, 13):
parser: ClassVar[types.ModuleType]
def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ...
Expand Down Expand Up @@ -100,9 +106,9 @@
class PurePosixPath(PurePath): ...
class PureWindowsPath(PurePath): ...

class Path(PurePath):
class Path(PathBase, PurePath):

Check failure on line 109 in stdlib/pathlib/__init__.pyi

View workflow job for this annotation

GitHub Actions / Test typeshed with pyright (Linux, 3.12)

Cannot create consistent method ordering (reportGeneralTypeIssues)
if sys.version_info >= (3, 12):
def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... # pyright: ignore[reportInconsistentConstructor]

Check failure on line 111 in stdlib/pathlib/__init__.pyi

View workflow job for this annotation

GitHub Actions / Test typeshed with pyright (Linux, 3.12)

Unnecessary "# pyright: ignore" rule: "reportInconsistentConstructor" (reportUnnecessaryTypeIgnoreComment)
else:
def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ...

Expand Down Expand Up @@ -291,8 +297,8 @@
if sys.version_info >= (3, 14):
def rmtree(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ...

class PosixPath(Path, PurePosixPath): ...

Check failure on line 300 in stdlib/pathlib/__init__.pyi

View workflow job for this annotation

GitHub Actions / Test typeshed with pyright (Linux, 3.12)

Cannot create consistent method ordering (reportGeneralTypeIssues)
class WindowsPath(Path, PureWindowsPath): ...

Check failure on line 301 in stdlib/pathlib/__init__.pyi

View workflow job for this annotation

GitHub Actions / Test typeshed with pyright (Linux, 3.12)

Cannot create consistent method ordering (reportGeneralTypeIssues)

if sys.version_info >= (3, 13):
class UnsupportedOperation(NotImplementedError): ...
192 changes: 192 additions & 0 deletions stdlib/pathlib/_abc.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import types
from _typeshed import (
AnyOrLiteralStr,
BytesPath,
OpenBinaryMode,
OpenBinaryModeReading,
OpenBinaryModeUpdating,
OpenBinaryModeWriting,
OpenTextMode,
ReadableBuffer,
StrOrBytesPath,
StrPath,
)
from collections.abc import Callable, Generator, Iterator, Sequence
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
from os import PathLike, stat_result
from typing import IO, Any, AnyStr, BinaryIO, ClassVar, Literal, overload
from typing_extensions import LiteralString, Self

__all__ = ["UnsupportedOperation"]

class UnsupportedOperation(NotImplementedError): ...

class ParserBase:
@property
def sep(self) -> str: ...
@overload
def join(self, path: LiteralString, *paths: LiteralString) -> LiteralString: ...
@overload
def join(self, path: StrPath, *paths: StrPath) -> str: ...
@overload
def join(self, path: BytesPath, *paths: BytesPath) -> bytes: ...
@overload
def split(self, path: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ...
@overload
def split(self, path: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ...
@overload
def splitdrive(self, path: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ...
@overload
def splitdrive(self, path: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ...
@overload
def normcase(self, path: PathLike[AnyStr]) -> AnyStr: ...
@overload
def normcase(self, path: AnyOrLiteralStr) -> AnyOrLiteralStr: ...
def isabs(self, path: StrOrBytesPath) -> bool: ...

class PurePathBase:
parser: ClassVar[types.ModuleType | ParserBase]
def __init__(self, path: StrPath, *paths: StrPath) -> None: ...
def with_segments(self, *pathsegments: StrPath) -> Self: ...
def as_posix(self) -> str: ...
@property
def drive(self) -> str: ...
@property
def root(self) -> str: ...
@property
def anchor(self) -> str: ...
@property
def name(self) -> str: ...
@property
def suffix(self) -> str: ...
@property
def suffixes(self) -> list[str]: ...
@property
def stem(self) -> str: ...
def with_name(self, name: str) -> Self: ...
def with_stem(self, stem: str) -> Self: ...
def with_suffix(self, suffix: str) -> Self: ...
def relative_to(self, other: StrPath, *, walk_up: bool = False) -> Self: ...
def is_relative_to(self, other: StrPath) -> bool: ...
@property
def parts(self) -> tuple[str, ...]: ...
def joinpath(self, *pathsegments: StrPath) -> Self: ...
def __truediv__(self, key: StrPath) -> Self: ...
def __rtruediv__(self, key: StrPath) -> Self: ...
@property
def parent(self) -> Self: ...
@property
def parents(self) -> Sequence[Self]: ...
def is_absolute(self) -> bool: ...
def match(self, path_pattern: str, *, case_sensitive: bool | None = None) -> bool: ...
def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ...

class PathBase(PurePathBase):
def stat(self, *, follow_symlinks: bool = True) -> stat_result: ...
def lstat(self) -> stat_result: ...
def exists(self, *, follow_symlinks: bool = True) -> bool: ...
def is_dir(self, *, follow_symlinks: bool = True) -> bool: ...
def is_file(self, *, follow_symlinks: bool = True) -> bool: ...
def is_mount(self) -> bool: ...
def is_symlink(self) -> bool: ...
def is_junction(self) -> bool: ...
def is_block_device(self) -> bool: ...
def is_char_device(self) -> bool: ...
def is_fifo(self) -> bool: ...
def is_socket(self) -> bool: ...
def samefile(self, other_path: StrPath) -> bool: ...

# Adapted from builtins.open
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we should require all implementations of this ABC to duplicate this stack of overloads. Not sure what the alternative would be, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't considered that aspect when copying it over. I think the alternative is to define only the most generic form of open on the base class, and implementations can have more specific overloads as needed. I'll update the MR for that.

6D40 Copy link
Contributor Author
@tungol tungol Nov 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stubtest didn't like that... I thought it would work based on a simplified test I did locally, but possibly I misunderstood something. I'll need to look at it later, so I'll put this in draft for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the same concern possibly applies to the overloads on ParserBase. I copied the definitions in the MR right now for that class from the versions that exist in posixpath.pyi.

# Text mode: always returns a TextIOWrapper
# The Traversable .open in stdlib/importlib/abc.pyi should be kept in sync with this.
@overload
def open(
self,
mode: OpenTextMode = "r",
buffering: int = -1,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> TextIOWrapper: ...
# Unbuffered binary mode: returns a FileIO
@overload
def open(
self, mode: OpenBinaryMode, buffering: Literal[0], encoding: None = None, errors: None = None, newline: None = None
) -> FileIO: ...
# Buffering is on: return BufferedRandom, BufferedReader, or BufferedWriter
@overload
def open(
self,
mode: OpenBinaryModeUpdating,
buffering: Literal[-1, 1] = -1,
encoding: None = None,
errors: None = None,
newline: None = None,
) -> BufferedRandom: ...
@overload
def open(
self,
mode: OpenBinaryModeWriting,
buffering: Literal[-1, 1] = -1,
encoding: None = None,
errors: None = None,
newline: None = None,
) -> BufferedWriter: ...
@overload
def open(
self,
mode: OpenBinaryModeReading,
buffering: Literal[-1, 1] = -1,
encoding: None = None,
errors: None = None,
newline: None = None,
) -> BufferedReader: ...
# Buffering cannot be determined: fall back to BinaryIO
@overload
def open(
self, mode: OpenBinaryMode, buffering: int = -1, encoding: None = None, errors: None = None, newline: None = None
) -> BinaryIO: ...
# Fallback if mode is not specified
@overload
def open(
self, mode: str, buffering: int = -1, encoding: str | None = None, errors: str | None = None, newline: str | None = None
) -> IO[Any]: ...
def read_bytes(self) -> bytes: ...
def read_text(self, encoding: str | None = None, errors: str | None = None, newline: str | None = None) -> str: ...
def write_bytes(self, data: ReadableBuffer) -> int: ...
def write_text(
self, data: str, encoding: str | None = None, errors: str | None = None, newline: str | None = None
) -> int: ...
def iterdir(self) -> Generator[Self, None, None]: ...
def glob(
self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = True
) -> Generator[Self, None, None]: ...
def rglob(
self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = True
) -> Generator[Self, None, None]: ...
def walk(
self, top_down: bool = True, on_error: Callable[[OSError], object] | None = None, follow_symlinks: bool = False
) -> Iterator[tuple[Self, list[str], list[str]]]: ...
def absolute(self) -> Self: ...
@classmethod
def cwd(cls) -> Self: ...
def expanduser(self) -> Self: ...
@classmethod
def home(cls) -> Self: ...
def readlink(self) -> Self: ...
def resolve(self, strict: bool = False) -> Self: ...
def symlink_to(self, target: StrOrBytesPath, target_is_directory: bool = False) -> None: ...
def hardlink_to(self, target: StrOrBytesPath) -> None: ...
def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: ...
def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ...
def rename(self, target: StrPath) -> Self: ...
def replace(self, target: StrPath) -> Self: ...
def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: ...
def lchmod(self, mode: int) -> None: ...
def unlink(self, missing_ok: bool = False) -> None: ...
def rmdir(self) -> None: ...
def owner(self, *, follow_symlinks: bool = True) -> str: ...
def group(self, *, follow_symlinks: bool = True) -> str: ...
@classmethod
def from_uri(cls, uri: str) -> Self: ...
def as_uri(self) -> str: ...
Loading
0