Skip to content

Commit 270ac3e

Browse files
Add Python 3.15 urllib.parse updates (#15726)
1 parent c23cf15 commit 270ac3e

2 files changed

Lines changed: 199 additions & 50 deletions

File tree

stdlib/@tests/stubtest_allowlists/py315.txt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,22 +201,17 @@ typing.Union
201201
typing._SpecialForm.__mro_entries__
202202
typing_extensions.__all__
203203
typing_extensions.Protocol
204-
urllib.parse._DefragResultBase.geturl
205-
urllib.parse._ParseResultBase.geturl
206-
urllib.parse._SplitResultBase.geturl
207-
urllib.parse.urldefrag
208-
urllib.parse.urlparse
209-
urllib.parse.urlsplit
210-
urllib.parse.urlunparse
211-
urllib.parse.urlunsplit
212204
xml.etree.ElementTree.__all__
213205
zipimport.zipimporter.load_module
214206

215-
216207
# =============================================================
217-
# Allowlist entries that cannot or should not be fixed; >= 3.15
208+
# Allowlist entries that cannot or should not be fixed; >=3.15
218209
# =============================================================
219210

211+
# runtime default is a list object used as a sentinel
212+
urllib.parse.urlunparse
213+
urllib.parse.urlunsplit
214+
220215
# Internal implementation details of the sampling profiler.
221216
profiling.sampling.binary_collector
222217
profiling.sampling.binary_reader

stdlib/urllib/parse.pyi

Lines changed: 194 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import sys
22
from collections.abc import Iterable, Mapping, Sequence
33
from types import GenericAlias
44
from typing import Any, AnyStr, Final, Generic, Literal, NamedTuple, Protocol, TypeAlias, overload, type_check_only
5+
from typing_extensions import TypeVar
56

67
__all__ = [
78
"urlparse",
@@ -37,6 +38,11 @@ scheme_chars: Final[str]
3738
if sys.version_info < (3, 11):
3839
MAX_CACHE_SIZE: Final[int]
3940

41+
_ResultStrT = TypeVar("_ResultStrT", str, bytes)
42+
_ResultComponentT = TypeVar("_ResultComponentT", str, bytes, str | None, bytes | None)
43+
_StrComponentT = TypeVar("_StrComponentT", str, str | None, default=str)
44+
_BytesComponentT = TypeVar("_BytesComponentT", bytes, bytes | None, default=bytes)
45+
4046
class _ResultMixinStr:
4147
__slots__ = ()
4248
def encode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinBytes: ...
@@ -63,44 +69,88 @@ class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr):
6369
class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes):
6470
__slots__ = ()
6571

66-
class _DefragResultBase(NamedTuple, Generic[AnyStr]):
67-
url: AnyStr
68-
fragment: AnyStr
72+
# Need to duplicate the whole class because mypy rejects version-specific
73+
# branches in namedtuple bodies.
74+
if sys.version_info >= (3, 15):
75+
class _DefragResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]):
76+
url: _ResultStrT
77+
fragment: _ResultComponentT
78+
# Ignore needed due to mypy#21453.
79+
def geturl(self) -> _ResultStrT: ... # type: ignore[misc]
80+
81+
else:
82+
class _DefragResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]):
83+
url: _ResultStrT
84+
fragment: _ResultComponentT
85+
86+
if sys.version_info >= (3, 15):
87+
class _SplitResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]):
88+
scheme: _ResultComponentT
89+
netloc: _ResultComponentT
90+
path: _ResultStrT
91+
query: _ResultComponentT
92+
fragment: _ResultComponentT
93+
# Ignore needed due to mypy#21453.
94+
def geturl(self) -> _ResultStrT: ... # type: ignore[misc]
95+
96+
else:
97+
class _SplitResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]):
98+
scheme: _ResultComponentT
99+
netloc: _ResultComponentT
100+
path: _ResultStrT
101+
query: _ResultComponentT
102+
fragment: _ResultComponentT
103+
104+
if sys.version_info >= (3, 15):
105+
class _ParseResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]):
106+
scheme: _ResultComponentT
107+
netloc: _ResultComponentT
108+
path: _ResultStrT
109+
params: _ResultComponentT
110+
query: _ResultComponentT
111+
fragment: _ResultComponentT
112+
# Ignore needed due to mypy#21453.
113+
def geturl(self) -> _ResultStrT: ... # type: ignore[misc]
69114

70-
class _SplitResultBase(NamedTuple, Generic[AnyStr]):
71-
scheme: AnyStr
72-
netloc: AnyStr
73-
path: AnyStr
74-
query: AnyStr
75-
fragment: AnyStr
115+
else:
116+
class _ParseResultBase(NamedTuple, Generic[_ResultStrT, _ResultComponentT]):
117+
scheme: _ResultComponentT
118+
netloc: _ResultComponentT
119+
path: _ResultStrT
120+
params: _ResultComponentT
121+
query: _ResultComponentT
122+
fragment: _ResultComponentT
76123

77-
class _ParseResultBase(NamedTuple, Generic[AnyStr]):
78-
scheme: AnyStr
79-
netloc: AnyStr
80-
path: AnyStr
81-
params: AnyStr
82-
query: AnyStr
83-
fragment: AnyStr
124+
if sys.version_info >= (3, 15):
125+
# Structured result objects for string data
126+
class DefragResult(_DefragResultBase[str, _StrComponentT], _ResultMixinStr, Generic[_StrComponentT]): ...
127+
class SplitResult(_SplitResultBase[str, _StrComponentT], _NetlocResultMixinStr, Generic[_StrComponentT]): ...
128+
class ParseResult(_ParseResultBase[str, _StrComponentT], _NetlocResultMixinStr, Generic[_StrComponentT]): ...
129+
# Structured result objects for bytes data
130+
class DefragResultBytes(_DefragResultBase[bytes, _BytesComponentT], _ResultMixinBytes, Generic[_BytesComponentT]): ...
131+
class SplitResultBytes(_SplitResultBase[bytes, _BytesComponentT], _NetlocResultMixinBytes, Generic[_BytesComponentT]): ...
132+
class ParseResultBytes(_ParseResultBase[bytes, _BytesComponentT], _NetlocResultMixinBytes, Generic[_BytesComponentT]): ...
84133

85-
# Structured result objects for string data
86-
class DefragResult(_DefragResultBase[str], _ResultMixinStr):
87-
def geturl(self) -> str: ...
134+
else:
135+
# Structured result objects for string data
136+
class DefragResult(_DefragResultBase[str, str], _ResultMixinStr):
137+
def geturl(self) -> str: ...
88138

89-
class SplitResult(_SplitResultBase[str], _NetlocResultMixinStr):
90-
def geturl(self) -> str: ...
139+
class SplitResult(_SplitResultBase[str, str], _NetlocResultMixinStr):
140+
def geturl(self) -> str: ...
91141

92-
class ParseResult(_ParseResultBase[str], _NetlocResultMixinStr):
93-
def geturl(self) -> str: ...
142+
class ParseResult(_ParseResultBase[str, str], _NetlocResultMixinStr):
143+
def geturl(self) -> str: ...
94144

95-
# Structured result objects for bytes data
96-
class DefragResultBytes(_DefragResultBase[bytes], _ResultMixinBytes):
97-
def geturl(self) -> bytes: ...
145+
# Structured result objects for bytes data
146+
class DefragResultBytes(_DefragResultBase[bytes, bytes], _ResultMixinBytes):
147+
def geturl(self) -> bytes: ...
98148

99-
class SplitResultBytes(_SplitResultBase[bytes], _NetlocResultMixinBytes):
100-
def geturl(self) -> bytes: ...
149+
class SplitResultBytes(_SplitResultBase[bytes, bytes], _NetlocResultMixinBytes):
150+
def geturl(self) -> bytes: ...
101151

102-
class ParseResultBytes(_ParseResultBase[bytes], _NetlocResultMixinBytes):
103-
def geturl(self) -> bytes: ...
152+
class ParseResultBytes(_ParseResultBase[bytes, bytes], _NetlocResultMixinBytes):
153+
def geturl(self) -> bytes: ...
104154

105155
def parse_qs(
106156
qs: AnyStr | None,
@@ -137,6 +187,20 @@ def urldefrag(url: str) -> DefragResult: ...
137187
@overload
138188
def urldefrag(url: bytes | bytearray | None) -> DefragResultBytes: ...
139189

190+
if sys.version_info >= (3, 15):
191+
@overload
192+
def urldefrag(url: str, *, missing_as_none: Literal[True]) -> DefragResult[str | None]: ...
193+
@overload
194+
def urldefrag(url: str, *, missing_as_none: Literal[False] = False) -> DefragResult[str]: ...
195+
@overload
196+
def urldefrag(url: bytes | bytearray | None, *, missing_as_none: Literal[True]) -> DefragResultBytes[bytes | None]: ...
197+
@overload
198+
def urldefrag(url: bytes | bytearray | None, *, missing_as_none: Literal[False] = False) -> DefragResultBytes[bytes]: ...
199+
@overload
200+
def urldefrag(url: str, *, missing_as_none: bool) -> DefragResult[str | None]: ...
201+
@overload
202+
def urldefrag(url: bytes | bytearray | None, *, missing_as_none: bool) -> DefragResultBytes[bytes | None]: ...
203+
140204
# The values are passed through `str()` (unless they are bytes), so anything is valid.
141205
_QueryType: TypeAlias = (
142206
Mapping[str, object]
@@ -171,6 +235,45 @@ def urlparse(url: str, scheme: str = "", allow_fragments: bool = True) -> ParseR
171235
def urlparse(
172236
url: bytes | bytearray | None, scheme: bytes | bytearray | None | Literal[""] = "", allow_fragments: bool = True
173237
) -> ParseResultBytes: ...
238+
239+
if sys.version_info >= (3, 15):
240+
@overload
241+
def urlparse(
242+
url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[True]
243+
) -> ParseResult[str | None]: ...
244+
@overload
245+
def urlparse(
246+
url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[False] = False
247+
) -> ParseResult[str]: ...
248+
@overload
249+
def urlparse(
250+
url: bytes | bytearray | None,
251+
scheme: bytes | bytearray | None | Literal[""] = "",
252+
allow_fragments: bool = True,
253+
*,
254+
missing_as_none: Literal[True],
255+
) -> ParseResultBytes[bytes | None]: ...
256+
@overload
257+
def urlparse(
258+
url: bytes | bytearray | None,
259+
scheme: bytes | bytearray | None | Literal[""] = "",
260+
allow_fragments: bool = True,
261+
*,
262+
missing_as_none: Literal[False] = False,
263+
) -> ParseResultBytes[bytes]: ...
264+
@overload
265+
def urlparse(
266+
url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: bool
267+
) -> ParseResult[str | None]: ...
268+
@overload
269+
def urlparse(
270+
url: bytes | bytearray | None,
271+
scheme: bytes | bytearray | None | Literal[""] = "",
272+
allow_fragments: bool = True,
273+
*,
274+
missing_as_none: bool,
275+
) -> ParseResultBytes[bytes | None]: ...
276+
174277
@overload
175278
def urlsplit(url: str, scheme: str = "", allow_fragments: bool = True) -> SplitResult: ...
176279

@@ -186,15 +289,66 @@ else:
186289
url: bytes | bytearray | None, scheme: bytes | bytearray | None | Literal[""] = "", allow_fragments: bool = True
187290
) -> SplitResultBytes: ...
188291

189-
# Requires an iterable of length 6
190-
@overload
191-
def urlunparse(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap]
192-
@overload
193-
def urlunparse(components: Iterable[AnyStr | None]) -> AnyStr: ...
292+
if sys.version_info >= (3, 15):
293+
@overload
294+
def urlsplit(
295+
url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[True]
296+
) -> SplitResult[str | None]: ...
297+
@overload
298+
def urlsplit(
299+
url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: Literal[False] = False
300+
) -> SplitResult[str]: ...
301+
@overload
302+
def urlsplit(
303+
url: bytes | None,
304+
scheme: bytes | None | Literal[""] = "",
305+
allow_fragments: bool = True,
306+
*,
307+
missing_as_none: Literal[True],
308+
) -> SplitResultBytes[bytes | None]: ...
309+
@overload
310+
def urlsplit(
311+
url: bytes | None,
312+
scheme: bytes | None | Literal[""] = "",
313+
allow_fragments: bool = True,
314+
*,
315+
missing_as_none: Literal[False] = False,
316+
) -> SplitResultBytes[bytes]: ...
317+
@overload
318+
def urlsplit(
319+
url: str, scheme: str = "", allow_fragments: bool = True, *, missing_as_none: bool
320+
) -> SplitResult[str | None]: ...
321+
@overload
322+
def urlsplit(
323+
url: bytes | None, scheme: bytes | None | Literal[""] = "", allow_fragments: bool = True, *, missing_as_none: bool
324+
) -> SplitResultBytes[bytes | None]: ...
325+
326+
if sys.version_info >= (3, 15):
327+
# Requires an iterable of length 6
328+
@overload
329+
def urlunparse(components: Iterable[None], *, keep_empty: bool = ...) -> Literal[b""]: ... # type: ignore[overload-overlap]
330+
@overload
331+
def urlunparse(components: Iterable[AnyStr | None], *, keep_empty: bool = ...) -> AnyStr: ...
332+
333+
else:
334+
# Requires an iterable of length 6
335+
@overload
336+
def urlunparse(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap]
337+
@overload
338+
def urlunparse(components: Iterable[AnyStr | None]) -> AnyStr: ...
339+
340+
if sys.version_info >= (3, 15):
341+
# Requires an iterable of length 5
342+
@overload
343+
def urlunsplit(components: Iterable[None], *, keep_empty: bool = ...) -> Literal[b""]: ... # type: ignore[overload-overlap]
344+
@overload
345+
def urlunsplit(components: Iterable[AnyStr | None], *, keep_empty: bool = ...) -> AnyStr: ...
346+
347+
else:
348+
# Requires an iterable of length 5
349+
@overload
350+
def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap]
351+
@overload
352+
def urlunsplit(components: Iterable[AnyStr | None]) -> AnyStr: ...
194353

195-
# Requires an iterable of length 5
196-
@overload
197-
def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap]
198-
@overload
199-
def urlunsplit(components: Iterable[AnyStr | None]) -> AnyStr: ...
200354
def unwrap(url: str) -> str: ...

0 commit comments

Comments
 (0)