Commit 74d7f76e authored by Ivan Levkivskyi's avatar Ivan Levkivskyi Committed by GitHub

bpo-37058: PEP 544: Add Protocol to typing module (GH-13585)

I tried to get rid of the `_ProtocolMeta`, but unfortunately it didn'y work. My idea to return a generic alias from `@runtime_checkable` made runtime protocols unpickleable. I am not sure what is worse (a custom metaclass or having some classes unpickleable), so I decided to stick with the status quo (since there were no complains so far). So essentially this is a copy of the implementation in `typing_extensions` with two modifications:
* Rename `@runtime` to `@runtime_checkable` (plus corresponding updates).
* Allow protocols that extend `collections.abc.Iterable` etc.
parent 3880f263
......@@ -17,7 +17,8 @@
--------------
This module supports type hints as specified by :pep:`484` and :pep:`526`.
This module provides runtime support for type hints as specified by
:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, and :pep:`591`.
The most fundamental support consists of the types :data:`Any`, :data:`Union`,
:data:`Tuple`, :data:`Callable`, :class:`TypeVar`, and
:class:`Generic`. For full specification please see :pep:`484`. For
......@@ -392,6 +393,48 @@ it as a return value) of a more specialized type is a type error. For example::
Use :class:`object` to indicate that a value could be any type in a typesafe
manner. Use :data:`Any` to indicate that a value is dynamically typed.
Nominal vs structural subtyping
-------------------------------
Initially :pep:`484` defined Python static type system as using
*nominal subtyping*. This means that a class ``A`` is allowed where
a class ``B`` is expected if and only if ``A`` is a subclass of ``B``.
This requirement previously also applied to abstract base classes, such as
:class:`Iterable`. The problem with this approach is that a class had
to be explicitly marked to support them, which is unpythonic and unlike
what one would normally do in idiomatic dynamically typed Python code.
For example, this conforms to the :pep:`484`::
from typing import Sized, Iterable, Iterator
class Bucket(Sized, Iterable[int]):
...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[int]: ...
:pep:`544` allows to solve this problem by allowing users to write
the above code without explicit base classes in the class definition,
allowing ``Bucket`` to be implicitly considered a subtype of both ``Sized``
and ``Iterable[int]`` by static type checkers. This is known as
*structural subtyping* (or static duck-typing)::
from typing import Iterator, Iterable
class Bucket: # Note: no base classes
...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[int]: ...
def collect(items: Iterable[int]) -> int: ...
result = collect(Bucket()) # Passes type check
Moreover, by subclassing a special class :class:`Protocol`, a user
can define new custom protocols to fully enjoy structural subtyping
(see examples below).
Classes, functions, and decorators
----------------------------------
......@@ -459,6 +502,39 @@ The module defines the following classes, functions and decorators:
except KeyError:
return default
.. class:: Protocol(Generic)
Base class for protocol classes. Protocol classes are defined like this::
class Proto(Protocol):
def meth(self) -> int:
...
Such classes are primarily used with static type checkers that recognize
structural subtyping (static duck-typing), for example::
class C:
def meth(self) -> int:
return 0
def func(x: Proto) -> int:
return x.meth()
func(C()) # Passes static type check
See :pep:`544` for details. Protocol classes decorated with
:func:`runtime_checkable` (described later) act as simple-minded runtime
protocols that check only the presence of given attributes, ignoring their
type signatures.
Protocol classes can be generic, for example::
class GenProto(Protocol[T]):
def meth(self) -> T:
...
.. versionadded:: 3.8
.. class:: Type(Generic[CT_co])
A variable annotated with ``C`` may accept a value of type ``C``. In
......@@ -1033,6 +1109,26 @@ The module defines the following classes, functions and decorators:
Note that returning instances of private classes is not recommended.
It is usually preferable to make such classes public.
.. decorator:: runtime_checkable
Mark a protocol class as a runtime protocol.
Such a protocol can be used with :func:`isinstance` and :func:`issubclass`.
This raises :exc:`TypeError` when applied to a non-protocol class. This
allows a simple-minded structural check, very similar to "one trick ponies"
in :mod:`collections.abc` such as :class:`Iterable`. For example::
@runtime_checkable
class Closable(Protocol):
def close(self): ...
assert isinstance(open('/some/file'), Closable)
**Warning:** this will check only the presence of the required methods,
not their type signatures!
.. versionadded:: 3.8
.. data:: Any
Special type indicating an unconstrained type.
......
This diff is collapsed.
This diff is collapsed.
PEP 544: Add ``Protocol`` and ``@runtime_checkable`` to the ``typing`` module.
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment