Introduce Mapping-like protocols (#4325)

typing.Mapping is not a protocol, which has caused problems in the past.
(E.g. python/typeshed#3569, see also python/typeshed#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.
This commit is contained in:
Sebastian Rittau
2020-07-12 02:11:08 +02:00
committed by GitHub
parent 028f0d5293
commit 9ad8ed3b75
3 changed files with 37 additions and 12 deletions

View File

@@ -15,12 +15,28 @@
import array
import mmap
import sys
from typing import Protocol, Text, TypeVar, Union
from typing import AbstractSet, Container, Protocol, Text, Tuple, TypeVar, Union
from typing_extensions import Literal
_KT_co = TypeVar("_KT_co", covariant=True)
_KT_contra = TypeVar("_KT_contra", contravariant=True)
_VT = TypeVar("_VT")
_VT_co = TypeVar("_VT_co", covariant=True)
_T_co = TypeVar("_T_co", covariant=True)
_T_contra = TypeVar("_T_contra", contravariant=True)
# Mapping-like protocols
class SupportsItems(Protocol[_KT_co, _VT_co]):
def items(self) -> AbstractSet[Tuple[_KT_co, _VT_co]]: ...
class SupportsGetItem(Container[_KT_contra], Protocol[_KT_contra, _VT_co]):
def __getitem__(self, __k: _KT_contra) -> _VT_co: ...
class SupportsItemAccess(SupportsGetItem[_KT_contra, _VT], Protocol[_KT_contra, _VT]):
def __setitem__(self, __k: _KT_contra, __v: _VT) -> None: ...
def __delitem__(self, __v: _KT_contra) -> None: ...
# StrPath and AnyPath can be used in places where a
# path can be used instead of a string, starting with Python 3.6.
if sys.version_info >= (3, 6):

View File

@@ -1,10 +1,14 @@
import sys
from typing import IO, Any, AnyStr, Dict, Iterator, List, Mapping, Optional, Tuple, TypeVar, Union
from _typeshed import SupportsGetItem, SupportsItemAccess
from typing import IO, Any, AnyStr, Dict, Iterable, Iterator, List, Mapping, Optional, Protocol, Tuple, TypeVar, Union
_T = TypeVar("_T", bound=FieldStorage)
def parse(
fp: Optional[IO[Any]] = ..., environ: Mapping[str, str] = ..., keep_blank_values: bool = ..., strict_parsing: bool = ...
fp: Optional[IO[Any]] = ...,
environ: SupportsItemAccess[str, str] = ...,
keep_blank_values: bool = ...,
strict_parsing: bool = ...,
) -> Dict[str, List[str]]: ...
if sys.version_info < (3, 8):
@@ -13,15 +17,19 @@ if sys.version_info < (3, 8):
if sys.version_info >= (3, 7):
def parse_multipart(
fp: IO[Any], pdict: Mapping[str, bytes], encoding: str = ..., errors: str = ...
fp: IO[Any], pdict: SupportsGetItem[str, bytes], encoding: str = ..., errors: str = ...
) -> Dict[str, List[Any]]: ...
else:
def parse_multipart(fp: IO[Any], pdict: Mapping[str, bytes]) -> Dict[str, List[bytes]]: ...
def parse_multipart(fp: IO[Any], pdict: SupportsGetItem[str, bytes]) -> Dict[str, List[bytes]]: ...
class _Environ(Protocol):
def __getitem__(self, __k: str) -> str: ...
def keys(self) -> Iterable[str]: ...
def parse_header(line: str) -> Tuple[str, Dict[str, str]]: ...
def test(environ: Mapping[str, str] = ...) -> None: ...
def print_environ(environ: Mapping[str, str] = ...) -> None: ...
def test(environ: _Environ = ...) -> None: ...
def print_environ(environ: _Environ = ...) -> None: ...
def print_form(form: Dict[str, Any]) -> None: ...
def print_directory() -> None: ...
def print_environ_usage() -> None: ...
@@ -77,7 +85,7 @@ class FieldStorage(object):
fp: Optional[IO[Any]] = ...,
headers: Optional[Mapping[str, str]] = ...,
outerboundary: bytes = ...,
environ: Mapping[str, str] = ...,
environ: SupportsGetItem[str, str] = ...,
keep_blank_values: int = ...,
strict_parsing: int = ...,
limit: Optional[int] = ...,
@@ -91,7 +99,7 @@ class FieldStorage(object):
fp: Optional[IO[Any]] = ...,
headers: Optional[Mapping[str, str]] = ...,
outerboundary: bytes = ...,
environ: Mapping[str, str] = ...,
environ: SupportsGetItem[str, str] = ...,
keep_blank_values: int = ...,
strict_parsing: int = ...,
limit: Optional[int] = ...,
@@ -104,7 +112,7 @@ class FieldStorage(object):
fp: IO[Any] = ...,
headers: Mapping[str, str] = ...,
outerboundary: bytes = ...,
environ: Mapping[str, str] = ...,
environ: SupportsGetItem[str, str] = ...,
keep_blank_values: int = ...,
strict_parsing: int = ...,
) -> None: ...

View File

@@ -1,9 +1,10 @@
from typing import Generic, Iterable, Mapping, Optional, Tuple, TypeVar
from _typeshed import SupportsItems
from typing import Generic, Iterable, Optional, Tuple, TypeVar
_T = TypeVar("_T")
class TopologicalSorter(Generic[_T]):
def __init__(self, graph: Optional[Mapping[_T, Iterable[_T]]] = ...) -> None: ...
def __init__(self, graph: Optional[SupportsItems[_T, Iterable[_T]]] = ...) -> None: ...
def add(self, node: _T, *predecessors: _T) -> None: ...
def prepare(self) -> None: ...
def is_active(self) -> bool: ...