Skip to content

Commit de317e1

Browse files
authored
Simplify and fix urllib.parse.urlencode() (#13815)
Remove overloads and type vars. Introduce a protocol for the `quote_via` argument. This means that the interface accepted by the supplied `quote_via` is stricter, and is not dependent on the actual supplied types in the `query` argument, but must work with all possible query types.
1 parent 3941e06 commit de317e1

File tree

2 files changed

+32
-26
lines changed

2 files changed

+32
-26
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
from urllib.parse import quote, quote_plus, urlencode
4+
5+
urlencode({"a": "b"}, quote_via=quote)
6+
urlencode({b"a": b"b"}, quote_via=quote)
7+
urlencode({"a": b"b"}, quote_via=quote)
8+
urlencode({b"a": "b"}, quote_via=quote)
9+
mixed_dict: dict[str | bytes, str | bytes] = {}
10+
urlencode(mixed_dict, quote_via=quote)
11+
12+
urlencode({"a": "b"}, quote_via=quote_plus)

stdlib/urllib/parse.pyi

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import sys
2-
from collections.abc import Callable, Iterable, Mapping, Sequence
2+
from collections.abc import Iterable, Mapping, Sequence
33
from types import GenericAlias
4-
from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload
4+
from typing import Any, AnyStr, Generic, Literal, NamedTuple, Protocol, overload, type_check_only
55
from typing_extensions import TypeAlias
66

77
__all__ = [
@@ -132,38 +132,32 @@ def urldefrag(url: str) -> DefragResult: ...
132132
@overload
133133
def urldefrag(url: bytes | bytearray | None) -> DefragResultBytes: ...
134134

135-
_Q = TypeVar("_Q", bound=str | Iterable[int])
135+
# The values are passed through `str()` (unless they are bytes), so anything is valid.
136136
_QueryType: TypeAlias = (
137-
Mapping[Any, Any] | Mapping[Any, Sequence[Any]] | Sequence[tuple[Any, Any]] | Sequence[tuple[Any, Sequence[Any]]]
137+
Mapping[str, object]
138+
| Mapping[bytes, object]
139+
| Mapping[str | bytes, object]
140+
| Mapping[str, Sequence[object]]
141+
| Mapping[bytes, Sequence[object]]
142+
| Mapping[str | bytes, Sequence[object]]
143+
| Sequence[tuple[str | bytes, object]]
144+
| Sequence[tuple[str | bytes, Sequence[object]]]
138145
)
139146

140-
@overload
141-
def urlencode(
142-
query: _QueryType,
143-
doseq: bool = False,
144-
safe: str = "",
145-
encoding: str | None = None,
146-
errors: str | None = None,
147-
quote_via: Callable[[AnyStr, str, str, str], str] = ...,
148-
) -> str: ...
149-
@overload
150-
def urlencode(
151-
query: _QueryType,
152-
doseq: bool,
153-
safe: _Q,
154-
encoding: str | None = None,
155-
errors: str | None = None,
156-
quote_via: Callable[[AnyStr, _Q, str, str], str] = ...,
157-
) -> str: ...
158-
@overload
147+
@type_check_only
148+
class _QuoteVia(Protocol):
149+
@overload
150+
def __call__(self, string: str, safe: str | bytes, encoding: str, errors: str, /) -> str: ...
151+
@overload
152+
def __call__(self, string: bytes, safe: str | bytes, /) -> str: ...
153+
159154
def urlencode(
160155
query: _QueryType,
161156
doseq: bool = False,
162-
*,
163-
safe: _Q,
157+
safe: str | bytes = "",
164158
encoding: str | None = None,
165159
errors: str | None = None,
166-
quote_via: Callable[[AnyStr, _Q, str, str], str] = ...,
160+
quote_via: _QuoteVia = ...,
167161
) -> str: ...
168162
def urljoin(base: AnyStr, url: AnyStr | None, allow_fragments: bool = True) -> AnyStr: ...
169163
@overload

0 commit comments

Comments
 (0)