diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 910f72b6ae56..17bcce0e1069 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -216,22 +216,17 @@ typing.Union typing._SpecialForm.__mro_entries__ typing_extensions.__all__ typing_extensions.Protocol -urllib.parse._DefragResultBase.geturl -urllib.parse._ParseResultBase.geturl -urllib.parse._SplitResultBase.geturl -urllib.parse.urldefrag -urllib.parse.urlparse -urllib.parse.urlsplit -urllib.parse.urlunparse -urllib.parse.urlunsplit xml.etree.ElementTree.__all__ zipimport.zipimporter.load_module - # ============================================================= -# Allowlist entries that cannot or should not be fixed; >= 3.15 +# Allowlist entries that cannot or should not be fixed; >=3.15 # ============================================================= +# runtime default is a list object used as a sentinel +urllib.parse.urlunparse +urllib.parse.urlunsplit + # Internal implementation details of the sampling profiler. profiling.sampling.binary_collector profiling.sampling.binary_reader diff --git a/stdlib/urllib/parse.pyi b/stdlib/urllib/parse.pyi index fe1cd6a71567..d9ba692bd6e9 100644 --- a/stdlib/urllib/parse.pyi +++ b/stdlib/urllib/parse.pyi @@ -2,6 +2,7 @@ import sys from collections.abc import Iterable, Mapping, Sequence from types import GenericAlias from typing import Any, AnyStr, Final, Generic, Literal, NamedTuple, Protocol, TypeAlias, overload, type_check_only +from typing_extensions import TypeVar __all__ = [ "urlparse", @@ -37,6 +38,11 @@ scheme_chars: Final[str] if sys.version_info < (3, 11): MAX_CACHE_SIZE: Final[int] +_ResultStrT = TypeVar("_ResultStrT", str, bytes) +_ResultComponentT = TypeVar("_ResultComponentT", str, bytes, str | None, bytes | None) +_StrComponentT = TypeVar("_StrComponentT", str, str | None, default=str) +_BytesComponentT = TypeVar("_BytesComponentT", bytes, bytes | None, default=bytes) + class _ResultMixinStr: __slots__ = () def encode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinBytes: ... @@ -63,44 +69,88 @@ class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): __slots__ = () -class _DefragResultBase(NamedTuple, Generic[AnyStr]): - url: AnyStr - fragment: AnyStr +# Need to duplicate the whole class because mypy rejects version-specific +# branches in namedtuple bodies. +if sys.version_info >= (3, 15): + class _DefragResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]): + url: _ResultStrT + fragment: _ResultComponentT + # Ignore needed due to mypy#21453. + def geturl(self) -> _ResultStrT: ... # type: ignore[misc] + +else: + class _DefragResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]): + url: _ResultStrT + fragment: _ResultComponentT + +if sys.version_info >= (3, 15): + class _SplitResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]): + scheme: _ResultComponentT + netloc: _ResultComponentT + path: _ResultStrT + query: _ResultComponentT + fragment: _ResultComponentT + # Ignore needed due to mypy#21453. + def geturl(self) -> _ResultStrT: ... # type: ignore[misc] + +else: + class _SplitResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]): + scheme: _ResultComponentT + netloc: _ResultComponentT + path: _ResultStrT + query: _ResultComponentT + fragment: _ResultComponentT + +if sys.version_info >= (3, 15): + class _ParseResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]): + scheme: _ResultComponentT + netloc: _ResultComponentT + path: _ResultStrT + params: _ResultComponentT + query: _ResultComponentT + fragment: _ResultComponentT + # Ignore needed due to mypy#21453. + def geturl(self) -> _ResultStrT: ... # type: ignore[misc] -class _SplitResultBase(NamedTuple, Generic[AnyStr]): - scheme: AnyStr - netloc: AnyStr - path: AnyStr - query: AnyStr - fragment: AnyStr +else: + class _ParseResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]): + scheme: _ResultComponentT + netloc: _ResultComponentT + path: _ResultStrT + params: _ResultComponentT + query: _ResultComponentT + fragment: _ResultComponentT -class _ParseResultBase(NamedTuple, Generic[AnyStr]): - scheme: AnyStr - netloc: AnyStr - path: AnyStr - params: AnyStr - query: AnyStr - fragment: AnyStr +if sys.version_info >= (3, 15): + # Structured result objects for string data + class DefragResult(_DefragResultBase[str, _StrComponentT], _ResultMixinStr, Generic[_StrComponentT]): ... + class SplitResult(_SplitResultBase[str, _StrComponentT], _NetlocResultMixinStr, Generic[_StrComponentT]): ... + class ParseResult(_ParseResultBase[str, _StrComponentT], _NetlocResultMixinStr, Generic[_StrComponentT]): ... + # Structured result objects for bytes data + class DefragResultBytes(_DefragResultBase[bytes, _BytesComponentT], _ResultMixinBytes, Generic[_BytesComponentT]): ... + class SplitResultBytes(_SplitResultBase[bytes, _BytesComponentT], _NetlocResultMixinBytes, Generic[_BytesComponentT]): ... + class ParseResultBytes(_ParseResultBase[bytes, _BytesComponentT], _NetlocResultMixinBytes, Generic[_BytesComponentT]): ... -# Structured result objects for string data -class DefragResult(_DefragResultBase[str], _ResultMixinStr): - def geturl(self) -> str: ... +else: + # Structured result objects for string data + class DefragResult(_DefragResultBase[str, str], _ResultMixinStr): + def geturl(self) -> str: ... -class SplitResult(_SplitResultBase[str], _NetlocResultMixinStr): - def geturl(self) -> str: ... + class SplitResult(_SplitResultBase[str, str], _NetlocResultMixinStr): + def geturl(self) -> str: ... -class ParseResult(_ParseResultBase[str], _NetlocResultMixinStr): - def geturl(self) -> str: ... + class ParseResult(_ParseResultBase[str, str], _NetlocResultMixinStr): + def geturl(self) -> str: ... -# Structured result objects for bytes data -class DefragResultBytes(_DefragResultBase[bytes], _ResultMixinBytes): - def geturl(self) -> bytes: ... + # Structured result objects for bytes data + class DefragResultBytes(_DefragResultBase[bytes, bytes], _ResultMixinBytes): + def geturl(self) -> bytes: ... -class SplitResultBytes(_SplitResultBase[bytes], _NetlocResultMixinBytes): - def geturl(self) -> bytes: ... + class SplitResultBytes(_SplitResultBase[bytes, bytes], _NetlocResultMixinBytes): + def geturl(self) -> bytes: ... -class ParseResultBytes(_ParseResultBase[bytes], _NetlocResultMixinBytes): - def geturl(self) -> bytes: ... + class ParseResultBytes(_ParseResultBase[bytes, bytes], _NetlocResultMixinBytes): + def geturl(self) -> bytes: ... def parse_qs( qs: AnyStr | None, @@ -137,6 +187,20 @@ def urldefrag(url: str) -> DefragResult: ... @overload def urldefrag(url: bytes | bytearray | None) -> DefragResultBytes: ... +if sys.version_info >= (3, 15): + @overload + def urldefrag(url: str, *, missing_as_none: Literal[True]) -> DefragResult[str | None]: ... + @overload + def urldefrag(url: str, *, missing_as_none: Literal[False] = False) -> DefragResult[str]: ... + @overload + def urldefrag(url: bytes | bytearray | None, *, missing_as_none: Literal[True]) -> DefragResultBytes[bytes | None]: ... + @overload + def urldefrag(url: bytes | bytearray | None, *, missing_as_none: Literal[False] = False) -> DefragResultBytes[bytes]: ... + @overload + def urldefrag(url: str, *, missing_as_none: bool) -> DefragResult[str | None]: ... + @overload + def urldefrag(url: bytes | bytearray | None, *, missing_as_none: bool) -> DefragResultBytes[bytes | None]: ... + # The values are passed through `str()` (unless they are bytes), so anything is valid. _QueryType: TypeAlias = ( Mapping[str, object] @@ -171,6 +235,45 @@ def urlparse(url: str, scheme: str = "", allow_fragments: bool = True) -> ParseR def urlparse( url: bytes | bytearray | None, scheme: bytes | bytearray | None | Literal[""] = "", allow_fragments: bool = True ) -> ParseResultBytes: ... + +if sys.version_info >= (3, 15): + @overload + def urlparse( + url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[True] + ) -> ParseResult[str | None]: ... + @overload + def urlparse( + url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[False] = False + ) -> ParseResult[str]: ... + @overload + def urlparse( + url: bytes | bytearray | None, + scheme: bytes | bytearray | None | Literal[""] = "", + allow_fragments: bool = True, + *, + missing_as_none: Literal[True], + ) -> ParseResultBytes[bytes | None]: ... + @overload + def urlparse( + url: bytes | bytearray | None, + scheme: bytes | bytearray | None | Literal[""] = "", + allow_fragments: bool = True, + *, + missing_as_none: Literal[False] = False, + ) -> ParseResultBytes[bytes]: ... + @overload + def urlparse( + url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: bool + ) -> ParseResult[str | None]: ... + @overload + def urlparse( + url: bytes | bytearray | None, + scheme: bytes | bytearray | None | Literal[""] = "", + allow_fragments: bool = True, + *, + missing_as_none: bool, + ) -> ParseResultBytes[bytes | None]: ... + @overload def urlsplit(url: str, scheme: str = "", allow_fragments: bool = True) -> SplitResult: ... @@ -186,15 +289,66 @@ else: url: bytes | bytearray | None, scheme: bytes | bytearray | None | Literal[""] = "", allow_fragments: bool = True ) -> SplitResultBytes: ... -# Requires an iterable of length 6 -@overload -def urlunparse(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] -@overload -def urlunparse(components: Iterable[AnyStr | None]) -> AnyStr: ... +if sys.version_info >= (3, 15): + @overload + def urlsplit( + url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[True] + ) -> SplitResult[str | None]: ... + @overload + def urlsplit( + url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[False] = False + ) -> SplitResult[str]: ... + @overload + def urlsplit( + url: bytes | None, + scheme: bytes | None | Literal[""] = "", + allow_fragments: bool = True, + *, + missing_as_none: Literal[True], + ) -> SplitResultBytes[bytes | None]: ... + @overload + def urlsplit( + url: bytes | None, + scheme: bytes | None | Literal[""] = "", + allow_fragments: bool = True, + *, + missing_as_none: Literal[False] = False, + ) -> SplitResultBytes[bytes]: ... + @overload + def urlsplit( + url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: bool + ) -> SplitResult[str | None]: ... + @overload + def urlsplit( + url: bytes | None, scheme: bytes | None | Literal[""] = "", allow_fragments: bool = True, *, missing_as_none: bool + ) -> SplitResultBytes[bytes | None]: ... + +if sys.version_info >= (3, 15): + # Requires an iterable of length 6 + @overload + def urlunparse(components: Iterable[None], *, keep_empty: bool = ...) -> Literal[b""]: ... # type: ignore[overload-overlap] + @overload + def urlunparse(components: Iterable[AnyStr | None], *, keep_empty: bool = ...) -> AnyStr: ... + +else: + # Requires an iterable of length 6 + @overload + def urlunparse(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] + @overload + def urlunparse(components: Iterable[AnyStr | None]) -> AnyStr: ... + +if sys.version_info >= (3, 15): + # Requires an iterable of length 5 + @overload + def urlunsplit(components: Iterable[None], *, keep_empty: bool = ...) -> Literal[b""]: ... # type: ignore[overload-overlap] + @overload + def urlunsplit(components: Iterable[AnyStr | None], *, keep_empty: bool = ...) -> AnyStr: ... + +else: + # Requires an iterable of length 5 + @overload + def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] + @overload + def urlunsplit(components: Iterable[AnyStr | None]) -> AnyStr: ... -# Requires an iterable of length 5 -@overload -def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] -@overload -def urlunsplit(components: Iterable[AnyStr | None]) -> AnyStr: ... def unwrap(url: str) -> str: ...