Skip to content

Commit

Permalink
Introduce Mapping-like protocols
Browse files Browse the repository at this point in the history
typing.Mapping is not a protocol, which has caused problems in the past.
(E.g. python#3569, see also python#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.
  • Loading branch information
srittau committed Jul 10, 2020
1 parent 209b6bb commit 68e097f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 12 deletions.
17 changes: 16 additions & 1 deletion stdlib/2and3/_typeshed/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,27 @@
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 = TypeVar("_KT")
_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, _VT_co]):
def items(self) -> AbstractSet[Tuple[_KT, _VT_co]]: ...

class SupportsGetItem(Container[_KT], Protocol[_KT, _VT_co]):
def __getitem__(self, __k: _KT) -> _VT_co: ...

class SupportsItemAccess(SupportsGetItem[_KT, _VT], Protocol[_KT, _VT]):
def __setitem__(self, __k: _KT, __v: _VT) -> None: ...
def __delitem__(self, __v: _KT) -> 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):
Expand Down
26 changes: 17 additions & 9 deletions stdlib/2and3/cgi.pyi
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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: ...
Expand Down Expand Up @@ -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] = ...,
Expand All @@ -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] = ...,
Expand All @@ -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: ...
Expand Down
5 changes: 3 additions & 2 deletions stdlib/3.9/graphlib.pyi
Original file line number Diff line number Diff line change
@@ -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: ...
Expand Down

0 comments on commit 68e097f

Please sign in to comment.