From a3ead21b31ba703747bb1d16fce14590a3d525c3 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 18:45:29 -0700 Subject: [PATCH 1/4] Add Python 3.15 path and importlib updates --- stdlib/@tests/stubtest_allowlists/py315.txt | 48 ------- stdlib/genericpath.pyi | 66 +++++++--- stdlib/glob.pyi | 19 +-- stdlib/http/client.pyi | 43 +++++-- stdlib/http/server.pyi | 45 +++++-- stdlib/importlib/metadata/__init__.pyi | 10 +- stdlib/importlib/resources/abc.pyi | 8 +- stdlib/inspect.pyi | 14 +- stdlib/ntpath.pyi | 26 +++- stdlib/pathlib/__init__.pyi | 11 +- stdlib/posixpath.pyi | 136 +++++++++++++++----- stdlib/pydoc.pyi | 5 +- stdlib/zipimport.pyi | 18 ++- 13 files changed, 304 insertions(+), 145 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index dc7e5fbe5c58..46cea1231154 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -138,43 +138,16 @@ enum.__all__ enum.auto.__init__ enum.auto.value functools.partialmethod.__new__ -genericpath.ALL_BUT_LAST -genericpath.__all__ -genericpath.commonprefix -genericpath.getatime -genericpath.getctime -genericpath.getmtime -genericpath.getsize -genericpath.samefile -genericpath.samestat -glob.glob0 -glob.glob1 gzip.compress hashlib.__all__ http.HTTPMethod.description -http.client.HTTPConnection.__init__ -http.client.HTTPSConnection.__init__ -http.server.CGIHTTPRequestHandler -http.server.SimpleHTTPRequestHandler.__init__ -http.server.SimpleHTTPRequestHandler.default_content_type -http.server.__all__ importlib._abc.Loader.load_module importlib._bootstrap_external.NamespacePath importlib.abc.InspectLoader.source_to_code importlib.abc.MetaPathFinder.discover importlib.abc.PathEntryFinder.discover -importlib.metadata.DeprecatedNonAbstract -importlib.metadata.Distribution -importlib.metadata.MetadataNotFound -importlib.metadata.PathDistribution -importlib.metadata.__all__ -importlib.resources._common.files -importlib.resources._common.package_to_anchor importlib.resources.abc.Traversable.open -importlib.resources.abc.Traversable.read_text inspect._ParameterKind.description -inspect.getdoc -inspect.getfullargspec io.Reader.__class_getitem__ io.Reader.read io.Writer.__class_getitem__ @@ -213,27 +186,9 @@ multiprocessing.managers._BaseSetProxy.clear multiprocessing.managers._BaseSetProxy.copy multiprocessing.managers._BaseSetProxy.pop multiprocessing.process.BaseProcess.__init__ -ntpath.ALL_BUT_LAST -ntpath.__all__ -ntpath.realpath -os.path.ALL_BUT_LAST -os.path.__all__ -pathlib.PurePath.__vfspath__ -pathlib.PurePath.is_reserved pdb.Pdb.print_stack_entry pkgutil.resolve_name platform.java_ver -posixpath.ALL_BUT_LAST -posixpath.__all__ -posixpath.basename -posixpath.dirname -posixpath.isabs -posixpath.normcase -posixpath.realpath -posixpath.split -posixpath.splitdrive -posixpath.splitext -posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint @@ -256,8 +211,6 @@ profiling.sampling.sample profiling.sampling.stack_collector profiling.sampling.string_table profiling.tracing -pydoc.Doc.STDLIB_DIR -pydoc.Doc.getdocloc re.Pattern.prefixmatch re.__all__ re.prefixmatch @@ -366,4 +319,3 @@ wave.__all__ xml.etree.ElementTree.__all__ xml.is_valid_name xml.utils -zipimport.zipimporter.load_module diff --git a/stdlib/genericpath.pyi b/stdlib/genericpath.pyi index 3caed77a661a..33261a399d99 100644 --- a/stdlib/genericpath.pyi +++ b/stdlib/genericpath.pyi @@ -3,7 +3,7 @@ import sys from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRichComparisonT from collections.abc import Sequence from typing import Literal, NewType, overload -from typing_extensions import LiteralString +from typing_extensions import LiteralString, deprecated __all__ = [ "commonprefix", @@ -23,22 +23,43 @@ if sys.version_info >= (3, 12): __all__ += ["islink"] if sys.version_info >= (3, 13): __all__ += ["isjunction", "isdevdrive", "lexists"] +if sys.version_info >= (3, 15): + __all__ += ["ALL_BUT_LAST"] + ALL_BUT_LAST: object # All overloads can return empty string. Ideally, Literal[""] would be a valid # Iterable[T], so that list[T] | Literal[""] could be used as a return # type. But because this only works when T is str, we need Sequence[T] instead. -@overload -def commonprefix(m: Sequence[LiteralString]) -> LiteralString: ... -@overload -def commonprefix(m: Sequence[StrPath]) -> str: ... -@overload -def commonprefix(m: Sequence[BytesPath]) -> bytes | Literal[""]: ... -@overload -def commonprefix(m: Sequence[list[SupportsRichComparisonT]]) -> Sequence[SupportsRichComparisonT]: ... -@overload -def commonprefix(m: Sequence[tuple[SupportsRichComparisonT, ...]]) -> Sequence[SupportsRichComparisonT]: ... +if sys.version_info >= (3, 15): + @overload + @deprecated("Deprecated since Python 3.15; use os.path.commonpath() for path prefixes.") + def commonprefix(m: Sequence[LiteralString], /) -> LiteralString: ... + @overload + @deprecated("Deprecated since Python 3.15; use os.path.commonpath() for path prefixes.") + def commonprefix(m: Sequence[StrPath], /) -> str: ... + @overload + @deprecated("Deprecated since Python 3.15; use os.path.commonpath() for path prefixes.") + def commonprefix(m: Sequence[BytesPath], /) -> bytes | Literal[""]: ... + @overload + @deprecated("Deprecated since Python 3.15; use os.path.commonpath() for path prefixes.") + def commonprefix(m: Sequence[list[SupportsRichComparisonT]], /) -> Sequence[SupportsRichComparisonT]: ... + @overload + @deprecated("Deprecated since Python 3.15; use os.path.commonpath() for path prefixes.") + def commonprefix(m: Sequence[tuple[SupportsRichComparisonT, ...]], /) -> Sequence[SupportsRichComparisonT]: ... + +else: + @overload + def commonprefix(m: Sequence[LiteralString]) -> LiteralString: ... + @overload + def commonprefix(m: Sequence[StrPath]) -> str: ... + @overload + def commonprefix(m: Sequence[BytesPath]) -> bytes | Literal[""]: ... + @overload + def commonprefix(m: Sequence[list[SupportsRichComparisonT]]) -> Sequence[SupportsRichComparisonT]: ... + @overload + def commonprefix(m: Sequence[tuple[SupportsRichComparisonT, ...]]) -> Sequence[SupportsRichComparisonT]: ... + def exists(path: FileDescriptorOrPath) -> bool: ... -def getsize(filename: FileDescriptorOrPath) -> int: ... def isfile(path: FileDescriptorOrPath) -> bool: ... def isdir(s: FileDescriptorOrPath) -> bool: ... @@ -47,12 +68,23 @@ if sys.version_info >= (3, 12): # These return float if os.stat_float_times() == True, # but int is a subclass of float. -def getatime(filename: FileDescriptorOrPath) -> float: ... -def getmtime(filename: FileDescriptorOrPath) -> float: ... -def getctime(filename: FileDescriptorOrPath) -> float: ... -def samefile(f1: FileDescriptorOrPath, f2: FileDescriptorOrPath) -> bool: ... def sameopenfile(fp1: int, fp2: int) -> bool: ... -def samestat(s1: os.stat_result, s2: os.stat_result) -> bool: ... + +if sys.version_info >= (3, 15): + def getsize(filename: FileDescriptorOrPath, /) -> int: ... + def getatime(filename: FileDescriptorOrPath, /) -> float: ... + def getmtime(filename: FileDescriptorOrPath, /) -> float: ... + def getctime(filename: FileDescriptorOrPath, /) -> float: ... + def samefile(f1: FileDescriptorOrPath, f2: FileDescriptorOrPath, /) -> bool: ... + def samestat(s1: os.stat_result, s2: os.stat_result, /) -> bool: ... + +else: + def getsize(filename: FileDescriptorOrPath) -> int: ... + def getatime(filename: FileDescriptorOrPath) -> float: ... + def getmtime(filename: FileDescriptorOrPath) -> float: ... + def getctime(filename: FileDescriptorOrPath) -> float: ... + def samefile(f1: FileDescriptorOrPath, f2: FileDescriptorOrPath) -> bool: ... + def samestat(s1: os.stat_result, s2: os.stat_result) -> bool: ... if sys.version_info >= (3, 13): def isjunction(path: StrOrBytesPath) -> bool: ... diff --git a/stdlib/glob.pyi b/stdlib/glob.pyi index 07455acaefc6..1e4d7203070d 100644 --- a/stdlib/glob.pyi +++ b/stdlib/glob.pyi @@ -9,14 +9,17 @@ __all__ = ["escape", "glob", "iglob"] if sys.version_info >= (3, 13): __all__ += ["translate"] -@deprecated( - "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." -) -def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... -@deprecated( - "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." -) -def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... +if sys.version_info >= (3, 15): + pass +else: + @deprecated( + "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." + ) + def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... + @deprecated( + "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." + ) + def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... if sys.version_info >= (3, 11): def glob( diff --git a/stdlib/http/client.pyi b/stdlib/http/client.pyi index 2af9769d3c36..fa89c0669680 100644 --- a/stdlib/http/client.pyi +++ b/stdlib/http/client.pyi @@ -178,14 +178,27 @@ class HTTPConnection: host: str port: int sock: socket | MaybeNone # can be `None` if `.connect()` was not called - def __init__( - self, - host: str, - port: int | None = None, - timeout: float | None = ..., - source_address: tuple[str, int] | None = None, - blocksize: int = 8192, - ) -> None: ... + if sys.version_info >= (3, 15): + def __init__( + self, + host: str, + port: int | None = None, + timeout: float | None = ..., + source_address: tuple[str, int] | None = None, + blocksize: int = 8192, + *, + max_response_headers: int | None = None, + ) -> None: ... + else: + def __init__( + self, + host: str, + port: int | None = None, + timeout: float | None = ..., + source_address: tuple[str, int] | None = None, + blocksize: int = 8192, + ) -> None: ... + def request( self, method: str, @@ -211,7 +224,19 @@ class HTTPConnection: class HTTPSConnection(HTTPConnection): # Can be `None` if `.connect()` was not called: sock: ssl.SSLSocket | MaybeNone - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 15): + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: float | None = ..., + source_address: tuple[str, int] | None = None, + context: ssl.SSLContext | None = None, + blocksize: int = 8192, + max_response_headers: int | None = None, + ) -> None: ... + elif sys.version_info >= (3, 12): def __init__( self, host: str, diff --git a/stdlib/http/server.pyi b/stdlib/http/server.pyi index 2c1a374331bc..0f892060b782 100644 --- a/stdlib/http/server.pyi +++ b/stdlib/http/server.pyi @@ -10,7 +10,16 @@ from ssl import Purpose, SSLContext from typing import Any, AnyStr, BinaryIO, ClassVar, Protocol, type_check_only from typing_extensions import Self, deprecated -if sys.version_info >= (3, 14): +if sys.version_info >= (3, 15): + __all__ = [ + "HTTPServer", + "ThreadingHTTPServer", + "HTTPSServer", + "ThreadingHTTPSServer", + "BaseHTTPRequestHandler", + "SimpleHTTPRequestHandler", + ] +elif sys.version_info >= (3, 14): __all__ = [ "HTTPServer", "ThreadingHTTPServer", @@ -77,6 +86,8 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): protocol_version: str MessageClass: type responses: Mapping[int, tuple[str, str]] + if sys.version_info >= (3, 15): + default_content_type: str default_request_version: str # undocumented weekdayname: ClassVar[Sequence[str]] # undocumented monthname: ClassVar[Sequence[str | None]] # undocumented @@ -102,14 +113,26 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): if sys.version_info >= (3, 12): index_pages: ClassVar[tuple[str, ...]] directory: str - def __init__( - self, - request: socketserver._RequestType, - client_address: _socket._RetAddress, - server: socketserver.BaseServer, - *, - directory: StrPath | None = None, - ) -> None: ... + if sys.version_info >= (3, 15): + def __init__( + self, + request: socketserver._RequestType, + client_address: _socket._RetAddress, + server: socketserver.BaseServer, + *, + directory: StrPath | None = None, + extra_response_headers: Mapping[str, str] | None = None, + ) -> None: ... + else: + def __init__( + self, + request: socketserver._RequestType, + client_address: _socket._RetAddress, + server: socketserver.BaseServer, + *, + directory: StrPath | None = None, + ) -> None: ... + def do_GET(self) -> None: ... def do_HEAD(self) -> None: ... def send_head(self) -> io.BytesIO | BinaryIO | None: ... # undocumented @@ -120,7 +143,9 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def executable(path: StrPath) -> bool: ... # undocumented -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 15): + pass +elif sys.version_info >= (3, 13): @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): cgi_directories: list[str] diff --git a/stdlib/importlib/metadata/__init__.pyi b/stdlib/importlib/metadata/__init__.pyi index a75b27e3d483..a1d0cbb912e4 100644 --- a/stdlib/importlib/metadata/__init__.pyi +++ b/stdlib/importlib/metadata/__init__.pyi @@ -31,6 +31,9 @@ __all__ = [ "version", ] +if sys.version_info >= (3, 15): + __all__ += ["PackagePath", "MetadataNotFound", "SimplePath"] + _SimplePath: TypeAlias = SimplePath def packages_distributions() -> Mapping[str, list[str]]: ... @@ -39,6 +42,9 @@ class PackageNotFoundError(ModuleNotFoundError): @property def name(self) -> str: ... # type: ignore[override] +if sys.version_info >= (3, 15): + class MetadataNotFound(FileNotFoundError): ... + if sys.version_info >= (3, 13): _EntryPointBase = object elif sys.version_info >= (3, 11): @@ -203,7 +209,9 @@ class FileHash: value: str def __init__(self, spec: str) -> None: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 15): + _distribution_parent = abc.ABC +elif sys.version_info >= (3, 12): class DeprecatedNonAbstract: ... _distribution_parent = DeprecatedNonAbstract else: diff --git a/stdlib/importlib/resources/abc.pyi b/stdlib/importlib/resources/abc.pyi index 477339ea7429..a3a40127a1f3 100644 --- a/stdlib/importlib/resources/abc.pyi +++ b/stdlib/importlib/resources/abc.pyi @@ -44,8 +44,12 @@ if sys.version_info >= (3, 11): def __truediv__(self, child: StrPath, /) -> Traversable: ... @abstractmethod def read_bytes(self) -> bytes: ... - @abstractmethod - def read_text(self, encoding: str | None = None) -> str: ... + if sys.version_info >= (3, 15): + @abstractmethod + def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ... + else: + @abstractmethod + def read_text(self, encoding: str | None = None) -> str: ... class TraversableResources(ResourceReader): @abstractmethod diff --git a/stdlib/inspect.pyi b/stdlib/inspect.pyi index f74f45dfcbf2..5a7b74b2d1ba 100644 --- a/stdlib/inspect.pyi +++ b/stdlib/inspect.pyi @@ -306,7 +306,13 @@ def getblock(lines: list[str]) -> list[str]: ... def getblock(lines: tuple[str, ...]) -> tuple[str, ...]: ... @overload def getblock(lines: Sequence[str]) -> Sequence[str]: ... -def getdoc(object: object) -> str | None: ... + +if sys.version_info >= (3, 15): + def getdoc(object: object, *, inherit_class_doc: bool = True, fallback_to_class_doc: bool = True) -> str | None: ... + +else: + def getdoc(object: object) -> str | None: ... + def getcomments(object: object) -> str | None: ... def getfile(object: _SourceObjectType) -> str: ... def getmodule(object: object, _filename: str | None = None) -> ModuleType | None: ... @@ -505,7 +511,11 @@ class FullArgSpec(NamedTuple): kwonlydefaults: dict[str, Any] | None annotations: dict[str, Any] -def getfullargspec(func: object) -> FullArgSpec: ... +if sys.version_info >= (3, 15): + def getfullargspec(func: object, *, annotation_format: Format = Format.VALUE) -> FullArgSpec: ... # noqa: Y011 + +else: + def getfullargspec(func: object) -> FullArgSpec: ... class ArgInfo(NamedTuple): args: list[str] diff --git a/stdlib/ntpath.pyi b/stdlib/ntpath.pyi index 074df075b972..371447820a46 100644 --- a/stdlib/ntpath.pyi +++ b/stdlib/ntpath.pyi @@ -51,6 +51,8 @@ if sys.version_info >= (3, 12): from posixpath import isjunction as isjunction, splitroot as splitroot if sys.version_info >= (3, 13): from genericpath import isdevdrive as isdevdrive +if sys.version_info >= (3, 15): + from genericpath import ALL_BUT_LAST as ALL_BUT_LAST __all__ = [ "normcase", @@ -97,6 +99,8 @@ if sys.version_info >= (3, 12): __all__ += ["isjunction", "splitroot"] if sys.version_info >= (3, 13): __all__ += ["isdevdrive", "isreserved"] +if sys.version_info >= (3, 15): + __all__ += ["ALL_BUT_LAST"] altsep: LiteralString @@ -111,13 +115,25 @@ def join(path: StrPath, /, *paths: StrPath) -> str: ... def join(path: BytesPath, /, *paths: BytesPath) -> bytes: ... if sys.platform == "win32": - @overload - def realpath(path: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ... - @overload - def realpath(path: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + if sys.version_info >= (3, 15): + @overload + def realpath(path: PathLike[AnyStr], /, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload + def realpath(path: AnyStr, /, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + else: + @overload + def realpath(path: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload + def realpath(path: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... else: - realpath = abspath + if sys.version_info >= (3, 15): + @overload + def realpath(path: PathLike[AnyStr], /, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload + def realpath(path: AnyStr, /, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + else: + realpath = abspath if sys.version_info >= (3, 13): def isreserved(path: StrOrBytesPath) -> bool: ... diff --git a/stdlib/pathlib/__init__.pyi b/stdlib/pathlib/__init__.pyi index 12cd36425d13..91f3f53b6eb7 100644 --- a/stdlib/pathlib/__init__.pyi +++ b/stdlib/pathlib/__init__.pyi @@ -29,7 +29,9 @@ if sys.version_info >= (3, 13): __all__ += ["UnsupportedOperation"] class PurePath(PathLike[str]): - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 15): + pass + elif sys.version_info >= (3, 13): __slots__ = ( "_raw_paths", "_drv", @@ -82,6 +84,9 @@ class PurePath(PathLike[str]): def __hash__(self) -> int: ... def __fspath__(self) -> str: ... + if sys.version_info >= (3, 15): + def __vfspath__(self) -> str: ... + def __lt__(self, other: PurePath) -> bool: ... def __le__(self, other: PurePath) -> bool: ... def __gt__(self, other: PurePath) -> bool: ... @@ -93,7 +98,9 @@ class PurePath(PathLike[str]): @deprecated("Deprecated since Python 3.14; will be removed in Python 3.19. Use `Path.as_uri()` instead.") def as_uri(self) -> str: ... def is_absolute(self) -> bool: ... - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 15): + pass + elif sys.version_info >= (3, 13): @deprecated( "Deprecated since Python 3.13; will be removed in Python 3.15. " "Use `os.path.isreserved()` to detect reserved paths on Windows." diff --git a/stdlib/posixpath.pyi b/stdlib/posixpath.pyi index 84e1b1e028bd..2de3c2879734 100644 --- a/stdlib/posixpath.pyi +++ b/stdlib/posixpath.pyi @@ -17,6 +17,9 @@ from genericpath import ( samestat as samestat, ) +if sys.version_info >= (3, 15): + from genericpath import ALL_BUT_LAST as ALL_BUT_LAST + if sys.version_info >= (3, 13): from genericpath import isdevdrive as isdevdrive from os import PathLike @@ -64,6 +67,8 @@ __all__ = [ "commonpath", ] __all__ += ["ALLOW_MISSING"] +if sys.version_info >= (3, 15): + __all__ += ["ALL_BUT_LAST"] if sys.version_info >= (3, 12): __all__ += ["isjunction", "splitroot"] if sys.version_info >= (3, 13): @@ -85,14 +90,27 @@ devnull: LiteralString def abspath(path: PathLike[AnyStr]) -> AnyStr: ... @overload def abspath(path: AnyStr) -> AnyStr: ... -@overload -def basename(p: PathLike[AnyStr]) -> AnyStr: ... -@overload -def basename(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... -@overload -def dirname(p: PathLike[AnyStr]) -> AnyStr: ... -@overload -def dirname(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... + +if sys.version_info >= (3, 15): + @overload + def basename(p: PathLike[AnyStr], /) -> AnyStr: ... + @overload + def basename(p: AnyOrLiteralStr, /) -> AnyOrLiteralStr: ... + @overload + def dirname(p: PathLike[AnyStr], /) -> AnyStr: ... + @overload + def dirname(p: AnyOrLiteralStr, /) -> AnyOrLiteralStr: ... + +else: + @overload + def basename(p: PathLike[AnyStr]) -> AnyStr: ... + @overload + def basename(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... + @overload + def dirname(p: PathLike[AnyStr]) -> AnyStr: ... + @overload + def dirname(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... + @overload def expanduser(path: PathLike[AnyStr]) -> AnyStr: ... @overload @@ -101,10 +119,19 @@ def expanduser(path: AnyStr) -> AnyStr: ... def expandvars(path: PathLike[AnyStr]) -> AnyStr: ... @overload def expandvars(path: AnyStr) -> AnyStr: ... -@overload -def normcase(s: PathLike[AnyStr]) -> AnyStr: ... -@overload -def normcase(s: AnyOrLiteralStr) -> AnyOrLiteralStr: ... + +if sys.version_info >= (3, 15): + @overload + def normcase(s: PathLike[AnyStr], /) -> AnyStr: ... + @overload + def normcase(s: AnyOrLiteralStr, /) -> AnyOrLiteralStr: ... + +else: + @overload + def normcase(s: PathLike[AnyStr]) -> AnyStr: ... + @overload + def normcase(s: AnyOrLiteralStr) -> AnyOrLiteralStr: ... + @overload def normpath(path: PathLike[AnyStr]) -> AnyStr: ... @overload @@ -116,7 +143,7 @@ def commonpath(paths: Iterable[StrPath]) -> str: ... @overload def commonpath(paths: Iterable[BytesPath]) -> bytes: ... -# First parameter is not actually pos-only, +# First parameter is not actually pos-only before Python 3.15, # but must be defined as pos-only in the stub or cross-platform code doesn't type-check, # as the parameter name is different in ntpath.join() @overload @@ -125,36 +152,77 @@ def join(a: LiteralString, /, *paths: LiteralString) -> LiteralString: ... def join(a: StrPath, /, *paths: StrPath) -> str: ... @overload def join(a: BytesPath, /, *paths: BytesPath) -> bytes: ... -@overload -def realpath(filename: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ... -@overload -def realpath(filename: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + +if sys.version_info >= (3, 15): + @overload + def realpath(filename: PathLike[AnyStr], /, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload + def realpath(filename: AnyStr, /, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + +else: + @overload + def realpath(filename: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload + def realpath(filename: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload def relpath(path: LiteralString, start: LiteralString | None = None) -> LiteralString: ... @overload def relpath(path: BytesPath, start: BytesPath | None = None) -> bytes: ... @overload def relpath(path: StrPath, start: StrPath | None = None) -> str: ... -@overload -def split(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... -@overload -def split(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... -@overload -def splitdrive(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... -@overload -def splitdrive(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... -@overload -def splitext(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... -@overload -def splitext(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... -def isabs(s: StrOrBytesPath) -> bool: ... + +if sys.version_info >= (3, 15): + @overload + def split(p: PathLike[AnyStr], /) -> tuple[AnyStr, AnyStr]: ... + @overload + def split(p: AnyOrLiteralStr, /) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... + @overload + def splitdrive(p: PathLike[AnyStr], /) -> tuple[AnyStr, AnyStr]: ... + @overload + def splitdrive(p: AnyOrLiteralStr, /) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... + +else: + @overload + def split(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... + @overload + def split(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... + @overload + def splitdrive(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... + @overload + def splitdrive(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... + +if sys.version_info >= (3, 15): + @overload + def splitext(p: PathLike[AnyStr], /) -> tuple[AnyStr, AnyStr]: ... + @overload + def splitext(p: AnyOrLiteralStr, /) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... + +else: + @overload + def splitext(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... + @overload + def splitext(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr]: ... + +if sys.version_info >= (3, 15): + def isabs(s: StrOrBytesPath, /) -> bool: ... + +else: + def isabs(s: StrOrBytesPath) -> bool: ... + def islink(path: FileDescriptorOrPath) -> bool: ... def ismount(path: FileDescriptorOrPath) -> bool: ... def lexists(path: FileDescriptorOrPath) -> bool: ... if sys.version_info >= (3, 12): def isjunction(path: StrOrBytesPath) -> bool: ... - @overload - def splitroot(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr, AnyOrLiteralStr]: ... - @overload - def splitroot(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr, AnyStr]: ... + if sys.version_info >= (3, 15): + @overload + def splitroot(path: AnyOrLiteralStr, /) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr, AnyOrLiteralStr]: ... + @overload + def splitroot(path: PathLike[AnyStr], /) -> tuple[AnyStr, AnyStr, AnyStr]: ... + else: + @overload + def splitroot(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr, AnyOrLiteralStr]: ... + @overload + def splitroot(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr, AnyStr]: ... diff --git a/stdlib/pydoc.pyi b/stdlib/pydoc.pyi index 59be9438015a..bf3e58df0e51 100644 --- a/stdlib/pydoc.pyi +++ b/stdlib/pydoc.pyi @@ -62,6 +62,9 @@ def safeimport(path: str, forceload: bool = ..., cache: MutableMapping[str, Modu class Doc: PYTHONDOCS: str + if sys.version_info >= (3, 15): + STDLIB_DIR: str + def document(self, object: object, name: str | None = None, *args: Any) -> str: ... def fail(self, object: object, name: str | None = None, *args: Any) -> NoReturn: ... @abstractmethod @@ -76,7 +79,7 @@ class Doc: def docproperty(self, object: object, name: str | None = None, *args: Any) -> str: ... @abstractmethod def docdata(self, object: object, name: str | None = None, *args: Any) -> str: ... - def getdocloc(self, object: object, basedir: str = ...) -> str | None: ... + def getdocloc(self, object: object, basedir: str | None = None) -> str | None: ... class HTMLRepr(Repr): def __init__(self) -> None: ... diff --git a/stdlib/zipimport.pyi b/stdlib/zipimport.pyi index f660684d351e..1dae386451f5 100644 --- a/stdlib/zipimport.pyi +++ b/stdlib/zipimport.pyi @@ -34,9 +34,15 @@ class zipimporter(_LoaderBasics): def get_source(self, fullname: str) -> str | None: ... def is_package(self, fullname: str) -> bool: ... - @deprecated("Deprecated since Python 3.10; removed in Python 3.15. Use `exec_module()` instead.") - def load_module(self, fullname: str) -> ModuleType: ... - def exec_module(self, module: ModuleType) -> None: ... - def create_module(self, spec: ModuleSpec) -> None: ... - def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... - def invalidate_caches(self) -> None: ... + if sys.version_info >= (3, 15): + def exec_module(self, module: ModuleType) -> None: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... + def invalidate_caches(self) -> None: ... + else: + @deprecated("Deprecated since Python 3.10; removed in Python 3.15. Use `exec_module()` instead.") + def load_module(self, fullname: str) -> ModuleType: ... + def exec_module(self, module: ModuleType) -> None: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... + def invalidate_caches(self) -> None: ... From e841dade125ddba5262445abed68c7c3340350ac Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 18:53:46 -0700 Subject: [PATCH 2/4] Fix pydoc stubtest compatibility --- stdlib/@tests/stubtest_allowlists/py315.txt | 2 ++ stdlib/pydoc.pyi | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 46cea1231154..67f91444884d 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -146,6 +146,8 @@ importlib._bootstrap_external.NamespacePath importlib.abc.InspectLoader.source_to_code importlib.abc.MetaPathFinder.discover importlib.abc.PathEntryFinder.discover +importlib.resources._common.files +importlib.resources._common.package_to_anchor importlib.resources.abc.Traversable.open inspect._ParameterKind.description io.Reader.__class_getitem__ diff --git a/stdlib/pydoc.pyi b/stdlib/pydoc.pyi index bf3e58df0e51..d340f5c587b3 100644 --- a/stdlib/pydoc.pyi +++ b/stdlib/pydoc.pyi @@ -79,7 +79,10 @@ class Doc: def docproperty(self, object: object, name: str | None = None, *args: Any) -> str: ... @abstractmethod def docdata(self, object: object, name: str | None = None, *args: Any) -> str: ... - def getdocloc(self, object: object, basedir: str | None = None) -> str | None: ... + if sys.version_info >= (3, 15): + def getdocloc(self, object: object, basedir: str | None = None) -> str | None: ... + else: + def getdocloc(self, object: object, basedir: str = ...) -> str | None: ... class HTMLRepr(Repr): def __init__(self) -> None: ... From 2f504a4f301f039a0bea3c5443683787970ecf79 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 19:41:08 -0700 Subject: [PATCH 3/4] Simplify zipimport version guards --- stdlib/zipimport.pyi | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/stdlib/zipimport.pyi b/stdlib/zipimport.pyi index 1dae386451f5..4b34f1f2ad3e 100644 --- a/stdlib/zipimport.pyi +++ b/stdlib/zipimport.pyi @@ -34,15 +34,11 @@ class zipimporter(_LoaderBasics): def get_source(self, fullname: str) -> str | None: ... def is_package(self, fullname: str) -> bool: ... - if sys.version_info >= (3, 15): - def exec_module(self, module: ModuleType) -> None: ... - def create_module(self, spec: ModuleSpec) -> None: ... - def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... - def invalidate_caches(self) -> None: ... - else: + if sys.version_info < (3, 15): @deprecated("Deprecated since Python 3.10; removed in Python 3.15. Use `exec_module()` instead.") def load_module(self, fullname: str) -> ModuleType: ... - def exec_module(self, module: ModuleType) -> None: ... - def create_module(self, spec: ModuleSpec) -> None: ... - def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... - def invalidate_caches(self) -> None: ... + + def exec_module(self, module: ModuleType) -> None: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... + def invalidate_caches(self) -> None: ... From b9c3af12102b64d294d0028d9bc68b037606a428 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 06:49:02 -0700 Subject: [PATCH 4/4] tweaks --- stdlib/glob.pyi | 4 +--- stdlib/http/server.pyi | 14 +------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/stdlib/glob.pyi b/stdlib/glob.pyi index 1e4d7203070d..bdfb2cfbcfed 100644 --- a/stdlib/glob.pyi +++ b/stdlib/glob.pyi @@ -9,9 +9,7 @@ __all__ = ["escape", "glob", "iglob"] if sys.version_info >= (3, 13): __all__ += ["translate"] -if sys.version_info >= (3, 15): - pass -else: +if sys.version_info < (3, 15): @deprecated( "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." ) diff --git a/stdlib/http/server.pyi b/stdlib/http/server.pyi index 0f892060b782..c6192a99c9ba 100644 --- a/stdlib/http/server.pyi +++ b/stdlib/http/server.pyi @@ -143,9 +143,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def executable(path: StrPath) -> bool: ... # undocumented -if sys.version_info >= (3, 15): - pass -elif sys.version_info >= (3, 13): +if sys.version_info < (3, 15): @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): cgi_directories: list[str] @@ -155,13 +153,3 @@ elif sys.version_info >= (3, 13): def is_executable(self, path: StrPath) -> bool: ... # undocumented def is_python(self, path: StrPath) -> bool: ... # undocumented def run_cgi(self) -> None: ... # undocumented - -else: - class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): - cgi_directories: list[str] - have_fork: bool # undocumented - def do_POST(self) -> None: ... - def is_cgi(self) -> bool: ... # undocumented - def is_executable(self, path: StrPath) -> bool: ... # undocumented - def is_python(self, path: StrPath) -> bool: ... # undocumented - def run_cgi(self) -> None: ... # undocumented