8000 Convert selected ABCs to Protocols by ilevkivskyi · Pull Request #1220 · python/typeshed · GitHub
[go: up one dir, main page]

Skip to content

Convert selected ABCs to Protocols #1220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions stdlib/2/__builtin__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class type(object):
def __instancecheck__(self, instance: Any) -> bool: ...
def __subclasscheck__(self, subclass: type) -> bool: ...

class int(SupportsInt, SupportsFloat, SupportsAbs[int]):
class int:
@overload
def __init__(self, x: SupportsInt = ...) -> None: ...
@overload
Expand Down Expand Up @@ -138,7 +138,7 @@ class int(SupportsInt, SupportsFloat, SupportsAbs[int]):
def __hash__(self) -> int: ...
def __nonzero__(self) -> bool: ...

class float(SupportsFloat, SupportsInt, SupportsAbs[float]):
class float:
def __init__(self, x: Union[SupportsFloat, str, unicode, bytearray] = ...) -> None: ...
def as_integer_ratio(self) -> Tuple[int, int]: ...
def hex(self) -> str: ...
Expand Down Expand Up @@ -179,7 +179,7 @@ class float(SupportsFloat, SupportsInt, SupportsAbs[float]):
def __hash__(self) -> int: ...
def __nonzero__(self) -> bool: ...

class complex(SupportsAbs[float]):
class complex:
@overload
def __init__(self, re: float = ..., im: float = ...) -> None: ...
@overload
Expand Down Expand Up @@ -470,7 +470,7 @@ class bytearray(MutableSequence[int]):
def __gt__(self, x: str) -> bool: ...
def __ge__(self, x: str) -> bool: ...

class bool(int, SupportsInt, SupportsFloat):
class bool(int):
def __init__(self, o: object = ...) -> None: ...

class slice(object):
Expand Down
44 changes: 30 additions & 14 deletions stdlib/2/typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class _SpecialForm(object):

Tuple: _SpecialForm = ...
Generic: _SpecialForm = ...
Protocol: _SpecialForm = ...
Callable: _SpecialForm = ...
Type: _SpecialForm = ...
ClassVar: _SpecialForm = ...
Expand Down Expand Up @@ -61,47 +62,60 @@ _V_co = TypeVar('_V_co', covariant=True) # Any type covariant containers.
_KT_co = TypeVar('_KT_co', covariant=True) # Key type covariant containers.
_VT_co = TypeVar('_VT_co', covariant=True) # Value type covariant containers.
_T_contra = TypeVar('_T_contra', contravariant=True) # Ditto contravariant.
_TC = TypeVar('_TC', bound=Type[object])

class SupportsInt(metaclass=ABCMeta):
def runtime(cls: _TC) -> _TC: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC @runtime is not defined (yet) in typing.py but (only) in typing_extensions.py?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and the same for Protocol (there is only old internal _Protocol in typing). We however need them in typeshed to avoid circular dependencies with typing_extensions. We discussed this with @JukkaL few moths ago, the plan (which I like) is this:

  1. Iterate on PR Make python 2 subprocess consistent with python 3 #3132 until it's approved.
  2. Publish Protocol in typing_extensions. Also include it in the stub for typing_extensions.
  3. Merge PR Make python 2 subprocess consistent with python 3 #3132 but don't merge typeshed changes yet. This lets mypy users (at least those who use GitHub master) experiment with user-defined protocols.
  4. Get an okay from PyCharm and Pytype that they are fine with merging the typeshed PR. They might need to implement some support for the Protocol special form first, but if they don't use the typing typeshed stub this may be a non-issue.
  5. If major issues are found in the mypy implementation of protocols, fix them.
  6. Release a new mypy version with protocols as an experimental feature!
  7. Merge the typeshed PR and use it in mypy.
  8. Seek acceptance of the protocols PEP once we have some practical experience and protocols seem to work fine.
  9. Officially support using protocols in typeshed once Pytype and PyCharm are okay with it.
  10. Release Protocol in Python stdlib typing module.

This PR is step number 7


@runtime
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a stub, why does it need @runtime?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise mypy will complain about e.g. issubclass(it, collections.abc.Iterable) something like isinstance() can be used only with @runtime protocols.

class SupportsInt(Protocol, metaclass=ABCMeta):
@abstractmethod
def __int__(self) -> int: ...

class SupportsFloat(metaclass=ABCMeta):
@runtime
class SupportsFloat(Protocol, metaclass=ABCMeta):
@abstractmethod
def __float__(self) -> float: ...

class SupportsComplex(metaclass=ABCMeta):
@runtime
class SupportsComplex(Protocol, metaclass=ABCMeta):
@abstractmethod
def __complex__(self) -> complex: ...

class SupportsAbs(Generic[_T]):
@runtime
class SupportsAbs(Protocol[_T_co]):
@abstractmethod
def __abs__(self) -> _T: ...
def __abs__(self) -> _T_co: ...

class SupportsRound(Generic[_T]):
@runtime
class SupportsRound(Protocol[_T_co]):
@abstractmethod
def __round__(self, ndigits: int = ...) -> _T: ...
def __round__(self, ndigits: int = ...) -> _T_co: ...

class Reversible(Generic[_T_co]):
@runtime
class Reversible(Protocol[_T_co]):
@abstractmethod
def __reversed__(self) -> Iterator[_T_co]: ...

class Sized(metaclass=ABCMeta):
@runtime
class Sized(Protocol, metaclass=ABCMeta):
@abstractmethod
def __len__(self) -> int: ...

class Hashable(metaclass=ABCMeta):
@runtime
class Hashable(Protocol, metaclass=ABCMeta):
# TODO: This is special, in that a subclass of a hashable class may not be hashable
# (for example, list vs. object). It's not obvious how to represent this. This class
# is currently mostly useless for static checking.
@abstractmethod
def __hash__(self) -> int: ...

class Iterable(Generic[_T_co]):
@runtime
class Iterable(Protocol[_T_co]):
@abstractmethod
def __iter__(self) -> Iterator[_T_co]: ...

class Iterator(Iterable[_T_co], Generic[_T_co]):
@runtime
class Iterator(Iterable[_T_co], Protocol[_T_co]):
@abstractmethod
def next(self) -> _T_co: ...

Expand All @@ -124,7 +138,8 @@ class Generator(Iterator[_T_co], Generic[_T_co, _T_contra, _V_co]):
gi_frame = ... # type: FrameType
gi_running = ... # type: bool

class Container(Generic[_T_co]):
@runtime
class Container(Protocol[_T_co]):
@abstractmethod
def __contains__(self, x: object) -> bool: ...

Expand Down Expand Up @@ -209,7 +224,8 @@ class ValuesView(MappingView, Iterable[_VT_co], Generic[_VT_co]):
def __contains__(self, o: object) -> bool: ...
def __iter__(self) -> Iterator[_VT_co]: ...

class ContextManager(Generic[_T_co]):
@runtime
class ContextManager(Protocol[_T_co]):
def __enter__(self) -> _T_co: ...
def __exit__(self, exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
Expand Down
8 changes: 4 additions & 4 deletions stdlib/3/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class super:
@overload
def __init__(self) -> None: ...

class int(SupportsInt, SupportsFloat, SupportsAbs[int]):
class int:
@overload
def __init__(self, x: Union[str, bytes, SupportsInt] = ...) -> None: ...
@overload
Expand Down Expand Up @@ -157,7 +157,7 @@ class int(SupportsInt, SupportsFloat, SupportsAbs[int]):
def __hash__(self) -> int: ...
def __bool__(self) -> bool: ...

class float(SupportsFloat, SupportsInt, SupportsAbs[float]):
class float:
def __init__(self, x: Union[SupportsFloat, str, bytes] = ...) -> None: ...
def as_integer_ratio(self) -> Tuple[int, int]: ...
def hex(self) -> str: ...
Expand Down Expand Up @@ -196,7 +196,7 @@ class float(SupportsFloat, SupportsInt, SupportsAbs[float]):
def __hash__(self) -> int: ...
def __bool__(self) -> bool: ...

class complex(SupportsAbs[float]):
class complex:
@overload
def __init__(self, re: float = ..., im: float = ...) -> None: ...
@overload
Expand Down Expand Up @@ -535,7 +535,7 @@ class memoryview(Sized, Container[bytes]):
def hex(self) -> str: ...


class bool(int, SupportsInt, SupportsFloat):
class bool(int):
def __init__(self, o: object = ...) -> None: ...

class slice:
Expand Down
65 changes: 44 additions & 21 deletions stdlib/3/typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class _SpecialForm:

Tuple: _SpecialForm = ...
Generic: _SpecialForm = ...
Protocol: _SpecialForm = ...
Callable: _SpecialForm = ...
Type: _SpecialForm = ...
ClassVar: _SpecialForm = ...
Expand Down Expand Up @@ -64,51 +65,65 @@ _V_co = TypeVar('_V_co', covariant=True) # Any type covariant containers.
_KT_co = TypeVar('_KT_co', covariant=True) # Key type covariant containers.
_VT_co = TypeVar('_VT_co', covariant=True) # Value type covariant containers.
_T_contra = TypeVar('_T_contra', contravariant=True) # Ditto contravariant.
_TC = TypeVar('_TC', bound=Type[object])

class SupportsInt(metaclass=ABCMeta):
def runtime(cls: _TC) -> _TC: ...

@runtime
class SupportsInt(Protocol, metaclass=ABCMeta):
@abstractmethod
def __int__(self) -> int: ...

class SupportsFloat(metaclass=ABCMeta):
@runtime
class SupportsFloat(Protocol, metaclass=ABCMeta):
@abstractmethod
def __float__(self) -> float: ...

class SupportsComplex(metaclass=ABCMeta):
@runtime
class SupportsComplex(Protocol, metaclass=ABCMeta):
@abstractmethod
def __complex__(self) -> complex: ...

class SupportsBytes(metaclass=ABCMeta):
@runtime
class SupportsBytes(Protocol, metaclass=ABCMeta):
@abstractmethod
def __bytes__(self) -> bytes: ...

class SupportsAbs(Generic[_T]):
@runtime
class SupportsAbs(Protocol[_T_co]):
@abstractmethod
def __abs__(self) -> _T: ...
def __abs__(self) -> _T_co: ...

class SupportsRound(Generic[_T]):
@runtime
class SupportsRound(Protocol[_T_co]):
@abstractmethod
def __round__(self, ndigits: int = ...) -> _T: ...
def __round__(self, ndigits: int = ...) -> _T_co: ...

class Reversible(Generic[_T_co]):
@runtime
class Reversible(Protocol[_T_co]):
@abstractmethod
def __reversed__(self) -> Iterator[_T_co]: ...

class Sized(metaclass=ABCMeta):
@runtime
class Sized(Protocol, metaclass=ABCMeta):
@abstractmethod
def __len__(self) -> int: ...

class Hashable(metaclass=ABCMeta):
@runtime
class Hashable(Protocol, metaclass=ABCMeta):
# TODO: This is special, in that a subclass of a hashable class may not be hashable
# (for example, list vs. object). It's not obvious how to represent this. This class
# is currently mostly useless for static checking.
@abstractmethod
def __hash__(self) -> int: ...

class Iterable(Generic[_T_co]):
@runtime
class Iterable(Protocol[_T_co]):
@abstractmethod
def __iter__(self) -> Iterator[_T_co]: ...

class Iterator(Iterable[_T_co], Generic[_T_co]):
@runtime
class Iterator(Iterable[_T_co], Protocol[_T_co]):
@abstractmethod
def __next__(self) -> _T_co: ...
def __iter__(self) -> 'Iterator[_T_co]': ...
Expand Down Expand Up @@ -139,7 +154,8 @@ class Generator(Iterator[_T_co], Generic[_T_co, _T_contra, _V_co]):
# Awaitable, AsyncIterator, AsyncIterable, Coroutine, Collection.
# See https: //github.com/python/typeshed/issues/655 for why this is not easy.

class Awaitable(Generic[_T_co]):
@runtime
class Awaitable(Protocol[_T_co]):
@abstractmethod
def __await__(self) -> Generator[Any, None, _T_co]: ...

Expand All @@ -161,12 +177,14 @@ class AwaitableGenerator(Generator[_T_co, _T_contra, _V_co], Awaitable[_V_co],
Generic[_T_co, _T_contra, _V_co, _S]):
pass

class AsyncIterable(Generic[_T_co]):
@runtime
class AsyncIterable(Protocol[_T_co]):
@abstractmethod
def __aiter__(self) -> 'AsyncIterator[_T_co]': ...

@runtime
class AsyncIterator(AsyncIterable[_T_co],
Generic[_T_co]):
Protocol[_T_co]):
@abstractmethod
def __anext__(self) -> Awaitable[_T_co]: ...
def __aiter__(self) -> 'AsyncIterator[_T_co]': ...
Expand Down Expand Up @@ -194,16 +212,19 @@ if sys.version_info >= (3, 6):
ag_frame = ... # type: FrameType
ag_running = ... # type: bool

class Container(Generic[_T_co]):
@runtime
class Container(Protocol[_T_co]):
@abstractmethod
def __contains__(self, x: object) -> bool: ...


if sys.version_info >= (3, 6):
class Collection(Sized, Iterable[_T_co], Container[_T_co], Generic[_T_co]): ...
@runtime
class Collection(Sized, Iterable[_T_co], Container[_T_co], Protocol[_T_co]): ...
_Collection = Collection
else:
class _Collection(Sized, Iterable[_T_co], Container[_T_co], Generic[_T_co]): ...
@runtime
class _Collection(Sized, Iterable[_T_co], Container[_T_co], Protocol[_T_co]): ...

class Sequence(_Collection[_T_co], Reversible[_T_co], Generic[_T_co]):
@overload
Expand Down Expand Up @@ -289,14 +310,16 @@ class ValuesView(MappingView, Iterable[_VT_co], Generic[_VT_co]):
def __contains__(self, o: object) -> bool: ...
def __iter__(self) -> Iterator[_VT_co]: ...

class ContextManager(Generic[_T_co]):
@runtime
class ContextManager(Protocol[_T_co]):
def __enter__(self) -> _T_co: ...
def __exit__(self, exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType]) -> Optional[bool]: ...

if sys.version_info >= (3, 5):
class AsyncContextManager(Generic[_T_co]):
@runtime
class AsyncContextManager(Protocol[_T_co]):
def __aenter__(self) -> Awaitable[_T_co]: ...
def __aexit__(self, exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
Expand Down
0