8000 Introduce Mapping-like protocols (#4325) · python/typeshed@9ad8ed3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9ad8ed3

Browse files
authored
Introduce Mapping-like protocols (#4325)
typing.Mapping is not a protocol, which has caused problems in the past. (E.g. #3569, see also #3576.) This introduces a few narrow protocols to _typeshed.pyi that can be used in place of Mapping. Not all uses of Mapping can be replaced. For example, cgi.FieldStorage explictly checks whether the supplied headers argument is a Mapping instance.
1 parent 028f0d5 commit 9ad8ed3

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

stdlib/2and3/_typeshed/__init__.pyi

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,28 @@
1515
import array
1616
import mmap
1717
import sys
18-
from typing import Protocol, Text, TypeVar, Union
18+
from typing import AbstractSet, Container, Protocol, Text, Tuple, TypeVar, Union
1919
from typing_extensions import Literal
2020

21+
_KT_co = TypeVar("_KT_co", covariant=True)
22+
_KT_contra = TypeVar("_KT_contra", contravariant=True)
23+
_VT = TypeVar("_VT")
24+
_VT_co = TypeVar("_VT_co", covariant=True)
2125
_T_co = TypeVar("_T_co", covariant=True)
2226
_T_contra = TypeVar("_T_contra", contravariant=True)
2327

28+
# Mapping-like protocols
29+
30+
class SupportsItems(Protocol[_KT_co, _VT_co]):
31+
def items(self) -> AbstractSet[Tuple[_KT_co, _VT_co]]: ...
32+
33+
class SupportsGetItem(Container[_KT_contra], Protocol[_KT_contra, _VT_co]):
34+
def __getitem__(self, __k: _KT_contra) -> _VT_co: ...
35+
36+
class SupportsItemAccess(SupportsGetItem[_KT_contra, _VT], Protocol[_KT_contra, _VT]):
37+
def __setitem__(self, __k: _KT_contra, __v: _VT) -> None: ...
38+
def __delitem__(self, __v: _KT_contra) -> None: ...
39+
2440
# StrPath and AnyPath can be used in places where a
2541
# path can be used instead of a string, starting with Python 3.6.
2642
if sys.version_info >= (3, 6):

stdlib/2and3/cgi.pyi

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import sys
2-
from typing import IO, Any, AnyStr, Dict, Iterator, List, Mapping, Optional, Tuple, TypeVar, Union
2+
from _typeshed import SupportsGetItem, SupportsItemAccess
3+
from typing import IO, Any, AnyStr, Dict, Iterable, Iterator, List, Mapping, Optional, Protocol, Tuple, TypeVar, Union
34

45
_T = TypeVar("_T", bound=FieldStorage)
56

67
def parse(
7-
fp: Optional[IO[Any]] = ..., environ: Mapping[str, str] = ..., keep_blank_values: bool = ..., strict_parsing: bool = ...
8+
fp: Optional[IO[Any]] = ...,
9+
environ: SupportsItemAccess[str, str] = ...,
10+
keep_blank_values: bool = ...,
11+
strict_parsing: bool = ...,
812
) -> Dict[str, List[str]]: ...
913

1014
if sys.version_info < (3, 8):
@@ -13,15 +17,19 @@ if sys.version_info < (3, 8):
1317

1418
if sys.version_info >= (3, 7):
1519
def parse_multipart(
16-
fp: IO[Any], pdict: Mapping[str, bytes], encoding: str = ..., errors: str = ...
20+
fp: IO[Any], pdict: SupportsGetItem[str, bytes], encoding: str = ..., errors: str = ...
1721
) -> Dict[str, List[Any]]: ...
1822

1923
else:
20-
def parse_multipart(fp: IO[Any], pdict: Mapping[str, bytes]) -> Dict[str, List[bytes]]: ...
24+
def parse_multipart(fp: IO[Any], pdict: SupportsGetItem[str, bytes]) -> Dict[str, List[bytes]]: ...
25+
26+
class _Environ(Protocol):
27+
def __getitem__(self, __k: str) -> str: ...
28+
def keys(self) -> Iterable[str]: ...
2129

2230
def parse_header(line: str) -> Tuple[str, Dict[str, str]]: ...
23-
def test(environ: Mapping[str, str] = ...) -> None: ...
24-
def print_environ(environ: Mapping[str, str] = ...) -> None: ...
31+
def test(environ: _Environ = ...) -> None: ...
32+
def print_environ(environ: _Environ = ...) -> None: ...
2533
def print_form(form: Dict[str, Any]) -> None: ...
2634
def print_directory() -> None: ...
2735
def print_environ_usage() -> None: ...
@@ -77,7 +85,7 @@ class FieldStorage(object):
7785
fp: Optional[IO[Any]] = ...,
7886
headers: Optional[Mapping[str, str]] = ...,
7987
outerboundary: bytes = ...,
80-
environ: Mapping[str, str] = ...,
88+
environ: SupportsGetItem[str, str] = ...,
8189
keep_blank_values: int = ...,
8290
strict_parsing: int = ...,
8391
limit: Optional[int] = ...,
@@ -91,7 +99,7 @@ class FieldStorage(object):
9199
fp: Optional[IO[Any]] = ...,
92100
headers: Optional[Mapping[str, str]] = ...,
93101
outerboundary: bytes = ...,
94-
environ: Mapping[str, str] = ...,
102+
environ: SupportsGetItem[str, str] = ...,
95103
keep_blank_values: int = ...,
96104
strict_parsing: int = ...,
97105
limit: Optional[int] = ...,
@@ -104,7 +112,7 @@ class FieldStorage(object):
104112
fp: IO[Any] = ...,
105113
headers: Mapping[str, str] = ...,
106114
outerboundary: bytes = ...,
107-
environ: Mapping[str, str] = ...,
115+
environ: SupportsGetItem[str, str] = ...,
108116
keep_blank_values: int = ...,
109117
strict_parsing: int = ...,
110118
) -> None: ...

stdlib/3.9/graphlib.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from typing import Generic, Iterable, Mapping, Optional, Tuple, TypeVar
1+
from _typeshed import SupportsItems
2+
from typing import Generic, Iterable, Optional, Tuple, TypeVar
23
34
_T = TypeVar("_T")
45

56
class TopologicalSorter(Generic[_T]):
6-
def __init__(self, graph: Optional[Mapping[_T, Iterable[_T]]] = ...) -> None: ...
7+
def __init__(self, graph: Optional[SupportsItems[_T, Iterable[_T]]] = ...) -> None: ...
78
def add(self, node: _T, *predecessors: _T) -> None: ...
89
def prepare(self) -> None: ...
910
def is_active(self) -> bool: ...

0 commit comments

Comments
 (0)
0