Skip to content

Minor tweaks to PEP 544 #1046

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
May 13, 2019
Merged
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
38 changes: 28 additions & 10 deletions pep-0544.txt
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,8 @@ intersection type construct could be added in future as specified by PEP 483,
see `rejected`_ ideas for more details.


``Type[]`` with protocols
-------------------------
``Type[]`` and class objects vs protocols
-----------------------------------------

Variables and parameters annotated with ``Type[Proto]`` accept only concrete
(non-protocol) subtypes of ``Proto``. The main reason for this is to allow
Expand Down Expand Up @@ -735,6 +735,23 @@ not explicitly typed, and such assignment creates a type alias.
For normal (non-abstract) classes, the behavior of ``Type[]`` is
not changed.

A class object is considered an implementation of a protocol if accessing
all members on it results in types compatible with the protocol members.
For example::

from typing import Any, Protocol

class ProtoA(Protocol):
def meth(self, x: int) -> int: ...
class ProtoB(Protocol):
def meth(self, obj: Any, x: int) -> int: ...

class C:
def meth(self, x: int) -> int: ...

a: ProtoA = C # Type check error, signatures don't match!
b: ProtoB = C # OK


``NewType()`` and type aliases
------------------------------
Expand Down Expand Up @@ -762,8 +779,8 @@ aliases::
CompatReversible = Union[Reversible[T], SizedIterable[T]]


Modules as subtypes of protocols
--------------------------------
Modules as implementations of protocols
---------------------------------------

A module object is accepted where a protocol is expected if the public
interface of the given module is compatible with the expected protocol.
Expand Down Expand Up @@ -1177,7 +1194,8 @@ For example::

def do_stuff(o: Union[P, X]) -> int:
if isinstance(o, P):
return o.common_method_name(1) # oops, what if it's an X instance?
return o.common_method_name(1) # Results in TypeError not caught
# statically if o is an X instance.

Another potentially problematic case is assignment of attributes
*after* instantiation::
Expand All @@ -1196,13 +1214,13 @@ Another potentially problematic case is assignment of attributes

def f(x: Union[P, int]) -> None:
if isinstance(x, P):
# static type of x is P here
# Static type of x is P here.
...
else:
# type of x is "int" here?
# Static type of x is int, but can be other type at runtime...
print(x + 1)

f(C()) # oops
f(C()) # ...causing a TypeError.

We argue that requiring an explicit class decorator would be better, since
one can then attach warnings about problems like this in the documentation.
Expand Down Expand Up @@ -1273,7 +1291,7 @@ Consider this example::

c = C()
f(c) # Would typecheck if covariant subtyping
# of mutable attributes were allowed
# of mutable attributes were allowed.
c.x >> 1 # But this fails at runtime

It was initially proposed to allow this for practical reasons, but it was
Expand All @@ -1292,7 +1310,7 @@ However, it was decided not to do this because of several downsides:

T = TypeVar('T')

class P(Protocol[T]): # Declared as invariant
class P(Protocol[T]): # Protocol is declared as invariant.
def meth(self) -> T:
...
class C:
Expand Down