From 39f9f0de168b0149456a1aca09ef3f442f436bbf Mon Sep 17 00:00:00 2001 From: Bryan Forbes Date: Wed, 10 Feb 2021 14:37:35 -0600 Subject: Refactor typings * Improve typing of `__init__()` * Update typing of `Map`-producing functions to produce the correct type * Update typing of other methods to more closely align with `Mapping` * Add protocol classes for unexposed data structures * Export protocol classes for ease of use in typed code * Update stub file to pass in mypy strict mode --- immutables/__init__.py | 25 ++++++++-- immutables/_map.pyi | 125 +++++++++++++++++++---------------------------- immutables/_protocols.py | 85 ++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 79 deletions(-) create mode 100644 immutables/_protocols.py (limited to 'immutables') diff --git a/immutables/__init__.py b/immutables/__init__.py index eaf0230..fc7764b 100644 --- a/immutables/__init__.py +++ b/immutables/__init__.py @@ -1,12 +1,27 @@ # flake8: noqa -try: +import sys + +if sys.version_info >= (3, 5, 2): + from typing import TYPE_CHECKING +else: + from typing_extensions import TYPE_CHECKING + +if TYPE_CHECKING: from ._map import Map -except ImportError: - from .map import Map else: - import collections.abc as _abc - _abc.Mapping.register(Map) + try: + from ._map import Map + except ImportError: + from .map import Map + else: + import collections.abc as _abc + _abc.Mapping.register(Map) + +from ._protocols import MapKeys as MapKeys +from ._protocols import MapValues as MapValues +from ._protocols import MapItems as MapItems +from ._protocols import MapMutation as MapMutation from ._version import __version__ diff --git a/immutables/_map.pyi b/immutables/_map.pyi index b8a6de3..4590cd4 100644 --- a/immutables/_map.pyi +++ b/immutables/_map.pyi @@ -1,96 +1,73 @@ from typing import Any +from typing import Dict from typing import Generic -from typing import Hashable from typing import Iterable from typing import Iterator from typing import Mapping -from typing import MutableMapping -from typing import NoReturn -from typing import overload +from typing import Optional from typing import Tuple from typing import Type -from typing import TypeVar from typing import Union +from typing import overload - -K = TypeVar('K', bound=Hashable) -V = TypeVar('V', bound=Any) -D = TypeVar('D', bound=Any) - - -class BitmapNode: ... - - -class MapKeys(Generic[K]): - def __init__(self, c: int, m: BitmapNode) -> None: ... - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[K]: ... - - -class MapValues(Generic[V]): - def __init__(self, c: int, m: BitmapNode) -> None: ... - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[V]: ... - - -class MapItems(Generic[K, V]): - def __init__(self, c: int, m: BitmapNode) -> None: ... - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[Tuple[K, V]]: ... +from ._protocols import IterableItems +from ._protocols import MapItems +from ._protocols import MapKeys +from ._protocols import MapMutation +from ._protocols import MapValues +from ._protocols import HT +from ._protocols import KT +from ._protocols import T +from ._protocols import VT_co -class Map(Mapping[K, V]): +class Map(Mapping[KT, VT_co]): @overload - def __init__(self, **kw: V) -> None: ... + def __init__(self) -> None: ... + @overload + def __init__(self: Map[str, VT_co], **kw: VT_co) -> None: ... + @overload + def __init__( + self, __col: Union[IterableItems[KT, VT_co], Iterable[Tuple[KT, VT_co]]] + ) -> None: ... @overload def __init__( - self, col: Union[Mapping[K, V], Iterable[Tuple[K, V]]], **kw: V + self: Map[Union[KT, str], VT_co], + __col: Union[IterableItems[KT, VT_co], Iterable[Tuple[KT, VT_co]]], + **kw: VT_co ) -> None: ... - def __reduce__(self) -> Tuple[Type[Map], Tuple[dict]]: ... + def __reduce__(self) -> Tuple[Type[Map[KT, VT_co]], Tuple[Dict[KT, VT_co]]]: ... def __len__(self) -> int: ... def __eq__(self, other: Any) -> bool: ... @overload - def update(self, **kw: V) -> Map[str, V]: ... - @overload def update( - self, col: Union[Mapping[K, V], Iterable[Tuple[K, V]]], **kw: V - ) -> Map[K, V]: ... - def mutate(self) -> MapMutation[K, V]: ... - def set(self, key: K, val: V) -> Map[K, V]: ... - def delete(self, key: K) -> Map[K, V]: ... - def get(self, key: K, default: D = ...) -> Union[V, D]: ... - def __getitem__(self, key: K) -> V: ... - def __contains__(self, key: object) -> bool: ... - def __iter__(self) -> Iterator[K]: ... - def keys(self) -> MapKeys[K]: ... - def values(self) -> MapValues[V]: ... - def items(self) -> MapItems[K, V]: ... - def __hash__(self) -> int: ... - def __dump__(self) -> str: ... - def __class_getitem__(cls, item: Any) -> Type[Map]: ... - - -S = TypeVar('S', bound='MapMutation') - - -class MapMutation(MutableMapping[K, V]): - def __init__(self, count: int, root: BitmapNode) -> None: ... - def set(self, key: K, val: V) -> None: ... - def __enter__(self: S) -> S: ... - def __exit__(self, *exc: Any): ... - def __iter__(self) -> NoReturn: ... - def __delitem__(self, key: K) -> None: ... - def __setitem__(self, key: K, val: V) -> None: ... - def pop(self, __key: K, __default: D = ...) -> Union[V, D]: ... - def get(self, key: K, default: D = ...) -> Union[V, D]: ... - def __getitem__(self, key: K) -> V: ... - def __contains__(self, key: Any) -> bool: ... + self, + __col: Union[IterableItems[KT, VT_co], Iterable[Tuple[KT, VT_co]]] + ) -> Map[KT, VT_co]: ... @overload - def update(self, **kw: V) -> None: ... + def update( + self: Map[Union[HT, str], Any], + __col: Union[IterableItems[KT, VT_co], Iterable[Tuple[KT, VT_co]]], + **kw: VT_co # type: ignore[misc] + ) -> Map[KT, VT_co]: ... @overload def update( - self, col: Union[Mapping[K, V], Iterable[Tuple[K, V]]], **kw: V - ) -> None: ... - def finish(self) -> Map[K, V]: ... - def __len__(self) -> int: ... - def __eq__(self, other: Any) -> bool: ... + self: Map[Union[HT, str], Any], + **kw: VT_co # type: ignore[misc] + ) -> Map[KT, VT_co]: ... + def mutate(self) -> MapMutation[KT, VT_co]: ... + def set(self, key: KT, val: VT_co) -> Map[KT, VT_co]: ... # type: ignore[misc] + def delete(self, key: KT) -> Map[KT, VT_co]: ... + @overload + def get(self, key: KT) -> Optional[VT_co]: ... + @overload + def get(self, key: KT, default: Union[VT_co, T]) -> Union[VT_co, T]: ... + def __getitem__(self, key: KT) -> VT_co: ... + def __contains__(self, key: Any) -> bool: ... + def __iter__(self) -> Iterator[KT]: ... + def keys(self) -> MapKeys[KT]: ... # type: ignore[override] + def values(self) -> MapValues[VT_co]: ... # type: ignore[override] + def items(self) -> MapItems[KT, VT_co]: ... # type: ignore[override] + def __hash__(self) -> int: ... + def __dump__(self) -> str: ... + def __class_getitem__(cls, item: Any) -> Type[Map[Any, Any]]: ... diff --git a/immutables/_protocols.py b/immutables/_protocols.py new file mode 100644 index 0000000..de87d23 --- /dev/null +++ b/immutables/_protocols.py @@ -0,0 +1,85 @@ +import sys +from typing import Any +from typing import Hashable +from typing import Iterable +from typing import Iterator +from typing import NoReturn +from typing import Optional +from typing import Tuple +from typing import TypeVar +from typing import Union +from typing import overload + +if sys.version_info >= (3, 8): + from typing import Protocol + from typing import TYPE_CHECKING +else: + from typing_extensions import Protocol + from typing_extensions import TYPE_CHECKING + +if TYPE_CHECKING: + from ._map import Map + +HT = TypeVar('HT', bound=Hashable) +KT = TypeVar('KT', bound=Hashable) +KT_co = TypeVar('KT_co', covariant=True) +MM = TypeVar('MM', bound='MapMutation[Any, Any]') +T = TypeVar('T') +VT = TypeVar('VT') +VT_co = TypeVar('VT_co', covariant=True) + + +class MapKeys(Protocol[KT_co]): + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[KT_co]: ... + + +class MapValues(Protocol[VT_co]): + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[VT_co]: ... + + +class MapItems(Protocol[KT_co, VT_co]): + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[Tuple[KT_co, VT_co]]: ... + + +class IterableItems(Protocol[KT_co, VT_co]): + def items(self) -> Iterable[Tuple[KT_co, VT_co]]: ... + + +class MapMutation(Protocol[KT, VT]): + def set(self, key: KT, val: VT) -> None: ... + def __enter__(self: MM) -> MM: ... + def __exit__(self, *exc: Any) -> bool: ... + def __iter__(self) -> NoReturn: ... + def __delitem__(self, key: KT) -> None: ... + def __setitem__(self, key: KT, val: VT) -> None: ... + @overload + def pop(self, __key: KT) -> VT: ... + @overload + def pop(self, __key: KT, __default: T) -> Union[VT, T]: ... + @overload + def get(self, key: KT) -> Optional[VT]: ... + @overload + def get(self, key: KT, default: Union[VT, T]) -> Union[VT, T]: ... + def __getitem__(self, key: KT) -> VT: ... + def __contains__(self, key: object) -> bool: ... + + @overload + def update( + self, + __col: Union[IterableItems[KT, VT], Iterable[Tuple[KT, VT]]] + ) -> None: ... + + @overload + def update( + self: 'MapMutation[Union[HT, str], Any]', + __col: Union[IterableItems[KT, VT], Iterable[Tuple[KT, VT]]], + **kw: VT + ) -> None: ... + @overload + def update(self: 'MapMutation[Union[HT, str], Any]', **kw: VT) -> None: ... + def finish(self) -> 'Map[KT, VT]': ... + def __len__(self) -> int: ... + def __eq__(self, other: Any) -> bool: ... -- cgit v1.2.3