Skip to content
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

Protocols #3132

Merged
merged 152 commits into from
Aug 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
152 commits
Select commit Hold shift + click to select a range
884481d
Recognize protocols in semanal.py
Mar 26, 2017
c19b6d4
Add issubtype and basic constraints
ilevkivskyi Mar 26, 2017
83c1579
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 26, 2017
b01f5b0
Remove debugging code
ilevkivskyi Mar 26, 2017
16901fc
Minor fixes: semanal, constrains, lib-stub; add few tests
Mar 27, 2017
c8c1247
Allow mypy_extensions.runtime, revert change to test name.
Mar 27, 2017
f430c65
Fix typo
Mar 27, 2017
beea7d2
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 27, 2017
260237a
Enhance constraints, add join (meet seems to be already OK); minor fixes
ilevkivskyi Mar 27, 2017
5414eaf
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 27, 2017
7f8a514
Prohibit protocol instatiation except Type[]; start working on @runtime
ilevkivskyi Mar 28, 2017
92b71a2
Add some tests
ilevkivskyi Mar 28, 2017
83085c7
Restore docstring formstting
ilevkivskyi Mar 28, 2017
27b8570
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 28, 2017
41179c2
Fix serialization; add few more tests
ilevkivskyi Mar 28, 2017
5171844
Add notes for missing protocol members in assignment
ilevkivskyi Mar 30, 2017
db123a3
Add notes for missing members also for arguments and return types
ilevkivskyi Mar 30, 2017
31bf16c
Prohibit definition via self.x in protocol classes
ilevkivskyi Mar 30, 2017
03b5d72
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 30, 2017
65a8546
Make more things in fixtures structural subtypes
ilevkivskyi Mar 30, 2017
2768d76
Structural support for __call__; add more tests
ilevkivskyi Mar 30, 2017
93beb75
Fix lint and tests
ilevkivskyi Mar 30, 2017
5135fd9
Beautify error notes
ilevkivskyi Mar 30, 2017
fdeb89b
Bind self to potential subtype; add tests for structural self-types
ilevkivskyi Mar 31, 2017
1fbcb4a
One more test
ilevkivskyi Mar 31, 2017
d012e38
Move global data to nodes.TypeInfo
ilevkivskyi Mar 31, 2017
801770d
Add comments and docstrings; recognize and treat abstract variables
ilevkivskyi Mar 31, 2017
a625f37
Fix tests and lint
ilevkivskyi Mar 31, 2017
fb6b1ad
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 31, 2017
adb68eb
Add two more tests for recursive
ilevkivskyi Mar 31, 2017
b9a0b2d
Add test for Sized
ilevkivskyi Mar 31, 2017
969f76f
Fix crash in typeanal.py; ignore positional arg names for subtyping; …
ilevkivskyi Apr 1, 2017
2e5de3e
Change order of structural and nominal checks to boost speed; minor f…
ilevkivskyi Apr 1, 2017
1daaf8d
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 1, 2017
2b6d198
Add structural support for tuples and named tuples; add tests
ilevkivskyi Apr 1, 2017
937c629
Add support for staticmethod, classmethod, property, ClassVar + tests…
ilevkivskyi Apr 1, 2017
e8e6661
Speed improvements; more carefull treatment of strucutral inference
ilevkivskyi Apr 2, 2017
8e6306f
Minor fixes and one more test
ilevkivskyi Apr 2, 2017
3f2d707
Start adding some docs
ilevkivskyi Apr 2, 2017
a82413d
Merge remote-tracking branch 'upstream/master' into protocols
Apr 3, 2017
957c76d
Add one more test
Apr 3, 2017
8cf08ec
Try to sync typeshed
ilevkivskyi Apr 3, 2017
e0083d9
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 3, 2017
c9629da
Simplify one test
ilevkivskyi Apr 4, 2017
affec0c
Add more docs
ilevkivskyi Apr 4, 2017
01948dd
Allow incompatible __init__ in structural subtypes
ilevkivskyi Apr 4, 2017
78a62eb
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 4, 2017
0ae409a
Fix minor failure after merge
ilevkivskyi Apr 4, 2017
cb27279
Re-run Travis
ilevkivskyi Apr 4, 2017
5452227
Merge branch 'master' into protocols
ilevkivskyi Apr 5, 2017
798954a
Implement subtype cache
ilevkivskyi Apr 9, 2017
9db4e51
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 9, 2017
7d2327a
Restore correct typeshed commit
ilevkivskyi Apr 9, 2017
88d4fed
Typeshed again
ilevkivskyi Apr 9, 2017
d9187e2
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 13, 2017
3ee1c53
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 16, 2017
85082d5
Switch to NotImplemented instead of False in __eq__
ilevkivskyi Apr 16, 2017
2733e1a
Prohibit using NewType with protocols
ilevkivskyi Apr 17, 2017
232428f
Address CR
ilevkivskyi Apr 17, 2017
4c04e0b
Restore current typeshed commit
ilevkivskyi Apr 17, 2017
e81a3f9
Add some support for __dunder__ = None idiom
ilevkivskyi Apr 18, 2017
b4ca6f0
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 18, 2017
b063767
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 21, 2017
79d8e30
Trigger AppVeyor re-build
Apr 21, 2017
480b977
Disable covariant subtyping for mutable members
ilevkivskyi Apr 22, 2017
483f163
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 22, 2017
70c3ae0
Fix remaining test
ilevkivskyi Apr 22, 2017
509113a
Add much more detailed error messages
ilevkivskyi Apr 22, 2017
0cb0985
Fix one error message in one more test
ilevkivskyi Apr 22, 2017
9f554b6
Fix minor metaclass self-type bug, add various tests
ilevkivskyi Apr 23, 2017
70463a5
Add two more tests (including for Any compatibility in protocols)
ilevkivskyi Apr 23, 2017
78f2011
Merge branch 'master' into protocols
ilevkivskyi Apr 25, 2017
473a2d2
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Apr 27, 2017
8dfe8ea
Add two corner case tests: __getattr__; implicit types
ilevkivskyi Apr 27, 2017
f7e55fa
Merge branch 'master' into protocols
ilevkivskyi May 2, 2017
06a1680
Fix fixture after merge
ilevkivskyi May 2, 2017
204ec82
Merge branch 'master' into protocols
ilevkivskyi May 5, 2017
985d1f7
Add variance checks and modify tests accordingly
ilevkivskyi May 5, 2017
8d2e199
Minor update to docs
ilevkivskyi May 14, 2017
f0471c1
Merge branch 'master' into protocols
ilevkivskyi May 21, 2017
4dfa3e1
Fix trailing whitespace
ilevkivskyi May 21, 2017
6d05060
Detailed error messages also for structural subtyping of tuples and c…
ilevkivskyi May 22, 2017
759409f
Add more protocol variance checks
ilevkivskyi May 22, 2017
83501ff
Merge branch 'master' into protocols
ilevkivskyi May 25, 2017
166b6da
Fix careless merge (#N: formatting)
ilevkivskyi May 25, 2017
d25bcfc
Merge branch 'master' into protocols
ilevkivskyi Jun 1, 2017
d652a69
Fix merge
ilevkivskyi Jun 1, 2017
addac40
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Jun 4, 2017
803ce1e
Fix merge
ilevkivskyi Jun 4, 2017
1c9f6f9
Merge branch 'master' into protocols
ilevkivskyi Jun 8, 2017
3c0411c
Improve notes formatting: add offsets
ilevkivskyi Jun 11, 2017
491b31f
Add more incremental tests
ilevkivskyi Jun 11, 2017
98f0180
Add support for __setattr__ (and few docstrings)
ilevkivskyi Jun 11, 2017
0f55718
Simplify hashes
ilevkivskyi Jun 11, 2017
513c759
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Jun 11, 2017
36f3d6d
Restore typeshed commit
ilevkivskyi Jun 11, 2017
05b70ab
Add explanation of covariant mutable overriding to common issues docs
ilevkivskyi Jun 11, 2017
561856e
Merge branch 'master' into protocols
ilevkivskyi Jun 28, 2017
228f621
Fix brocken merge (now Mapping needs to be Iterable in typing-full fi…
ilevkivskyi Jun 28, 2017
0716b59
Merge branch 'master' into protocols
ilevkivskyi Jun 29, 2017
9cd4e29
Merge branch 'master' into protocols
ilevkivskyi Jul 4, 2017
eb06c55
Fix strict-optional
ilevkivskyi Jul 4, 2017
59aeed2
Fix other merge problems
ilevkivskyi Jul 4, 2017
73d2d69
Merge branch 'master' into protocols
ilevkivskyi Jul 5, 2017
ee18dde
Fix fixtures/dict.pyi fixture
ilevkivskyi Jul 5, 2017
34d1cd1
Fix TypedDict joins with protocols via fallback
ilevkivskyi Jul 5, 2017
fbbd169
Use typing_extensions instead of typing in the docs
Jul 5, 2017
3acd19e
Use pre-PEP-526 syntax in docs where possible
Jul 5, 2017
22ad771
Move generic protocols after nominal generics docs
Jul 5, 2017
4f2391e
Change recommendations about using protocols
Jul 5, 2017
359a43b
Restore correct typeshed commit
Jul 5, 2017
eacff5f
Switch from mypy_extensions to typing_extensions in semanal and in tests
ilevkivskyi Jul 6, 2017
afe1291
Fix lint
ilevkivskyi Jul 6, 2017
1858ed9
Move most protocol erro formatting to messages.py
ilevkivskyi Jul 6, 2017
057a871
Fix few error messages in tests
ilevkivskyi Jul 6, 2017
28c1b9d
Limit display of missing protocol members
ilevkivskyi Jul 6, 2017
ad2bcaa
Limit (and simplify) display of conflicting protocol member types
ilevkivskyi Jul 6, 2017
8af7248
Use pretty callable formatting. Credits to @markkohdev
ilevkivskyi Jul 6, 2017
9ca98b2
Truncate output (max 2 conflicts, max 2 overloads), better formatting…
ilevkivskyi Jul 6, 2017
0cb13b7
Fix lint
ilevkivskyi Jul 6, 2017
fd06408
Factor out some common code
ilevkivskyi Jul 6, 2017
969a64b
Remove some redundant code
ilevkivskyi Jul 6, 2017
e9cbba8
Merge branch 'master' into protocols
ilevkivskyi Jul 14, 2017
e93f839
Fix broken merge
Jul 14, 2017
9292971
First part of CR
Jul 16, 2017
4c3f4e2
Second part of CR (few more comments left)
Jul 16, 2017
e71dff0
Fix strict-optional check
ilevkivskyi Jul 17, 2017
d195964
Don't hash most types (including syntetic)
ilevkivskyi Jul 17, 2017
318bb70
Correct comment in test
ilevkivskyi Jul 17, 2017
3d8782f
Improve structural support for TypedDict and minor things
ilevkivskyi Jul 17, 2017
6e1100c
Don't show detailed erorrs for TypedDicts in simple cases
ilevkivskyi Jul 17, 2017
5bfa3b2
Add also an exception for NamedTuples
ilevkivskyi Jul 17, 2017
82f01b7
Add one more test
ilevkivskyi Jul 17, 2017
c7304bd
Dict is not a protocol
ilevkivskyi Jul 17, 2017
58e6b36
Two minor tweaks: more precise isinstance, require explicit types for…
ilevkivskyi Jul 18, 2017
eefe881
Restore typeshed commit
ilevkivskyi Jul 18, 2017
96a04ae
pythoneval tests should wait until typeshed PR is merged
ilevkivskyi Jul 18, 2017
b1c4d37
Merge branch 'master' into protocols
ilevkivskyi Jul 28, 2017
8b6006c
Fix a silly mistake in merge
ilevkivskyi Jul 28, 2017
f76349b
Replace one last mention of typing with typing_extensions
ilevkivskyi Jul 28, 2017
c3bfe2b
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Jul 29, 2017
e2f4f5d
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Aug 10, 2017
91fe9fd
Add TypeOfAny
ilevkivskyi Aug 10, 2017
aaea344
Remove some ocassionally added files
ilevkivskyi Aug 10, 2017
27c3e36
Limit unnecessary conflicts display and fix conflict count (with test)
ilevkivskyi Aug 10, 2017
cfa539d
Remove some strange file
ilevkivskyi Aug 12, 2017
713db0c
Implememt first part of recent comments
ilevkivskyi Aug 12, 2017
40d635f
Address second part of recent comments
ilevkivskyi Aug 12, 2017
19073e5
First part of todays comments
ilevkivskyi Aug 14, 2017
50d98c0
Second part of yesterdays comments
ilevkivskyi Aug 15, 2017
9411255
Last part of review comments
ilevkivskyi Aug 15, 2017
f1c915e
One last missing comment
ilevkivskyi Aug 15, 2017
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
171 changes: 169 additions & 2 deletions docs/source/class_basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,174 @@ concrete. As with normal overrides, a dynamically typed method can
implement a statically typed abstract method defined in an abstract
base class.

.. _protocol-types:

Protocols and structural subtyping
**********************************

.. note::

The support for structural subtyping is still experimental. Some features
might be not yet implemented, mypy could pass unsafe code or reject
working code.

There are two main type systems with respect to subtyping: nominal subtyping
and structural subtyping. The *nominal* subtyping is based on class hierarchy,
so that if class ``D`` inherits from class ``C``, then it is a subtype
of ``C``. This type system is primarily used in mypy since it allows
to produce clear and concise error messages, and since Python provides native
``isinstance()`` checks based on class hierarchy. The *structural* subtyping
however has its own advantages. In this system class ``D`` is a subtype
of class ``C`` if the former has all attributes of the latter with
compatible types.

This type system is a static equivalent of duck typing, well known by Python
programmers. Mypy provides an opt-in support for structural subtyping via
protocol classes described in this section.
See `PEP 544 <https://www.python.org/dev/peps/pep-0544/>`_ for
specification of protocols and structural subtyping in Python.

User defined protocols
**********************

To define a protocol class, one must inherit the special
``typing_extensions.Protocol`` class:

.. code-block:: python

from typing import Iterable
from typing_extensions import Protocol

class SupportsClose(Protocol):
def close(self) -> None:
...

class Resource: # Note, this class does not have 'SupportsClose' base.
# some methods
def close(self) -> None:
self.resource.release()

def close_all(things: Iterable[SupportsClose]) -> None:
for thing in things:
thing.close()

close_all([Resource(), open('some/file')]) # This passes type check

.. note::

The ``Protocol`` base class is currently provided in ``typing_extensions``
package. When structural subtyping is mature and
`PEP 544 <https://www.python.org/dev/peps/pep-0544/>`_ is accepted,
``Protocol`` will be included in the ``typing`` module. As well, several
types such as ``typing.Sized``, ``typing.Iterable`` etc. will be made
protocols.

Defining subprotocols
*********************

Subprotocols are also supported. Existing protocols can be extended
and merged using multiple inheritance. For example:

.. code-block:: python

# continuing from previous example

class SupportsRead(Protocol):
def read(self, amount: int) -> bytes: ...

class TaggedReadableResource(SupportsClose, SupportsRead, Protocol):
label: str

class AdvancedResource(Resource):
def __init__(self, label: str) -> None:
self.label = label
def read(self, amount: int) -> bytes:
# some implementation
...

resource = None # type: TaggedReadableResource

# some code

resource = AdvancedResource('handle with care') # OK

Note that inheriting from existing protocols does not automatically turn
a subclass into a protocol, it just creates a usual (non-protocol) ABC that
implements given protocols. The ``typing_extensions.Protocol`` base must always
be explicitly present:

.. code-block:: python

class NewProtocol(SupportsClose): # This is NOT a protocol
new_attr: int

class Concrete:
new_attr = None # type: int
def close(self) -> None:
...
# Below is an error, since nominal subtyping is used by default
x = Concrete() # type: NewProtocol # Error!

.. note::

The `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_ variable
annotations can be used to declare protocol attributes. However, protocols
are also supported on Python 2.7 and Python 3.3+ with the help of type
comments and properties, see
`backwards compatibility in PEP 544 <https://www.python.org/dev/peps/pep-0544/#backwards-compatibility>`_.

Recursive protocols
*******************

Protocols can be recursive and mutually recursive. This could be useful for
declaring abstract recursive collections such as trees and linked lists:

.. code-block:: python

from typing import TypeVar, Optional
from typing_extensions import Protocol

class TreeLike(Protocol):
value: int
@property
def left(self) -> Optional['TreeLike']: ...
@property
def right(self) -> Optional['TreeLike']: ...

class SimpleTree:
def __init__(self, value: int) -> None:
self.value = value
self.left: Optional['SimpleTree'] = None
self.right: Optional['SimpleTree'] = None

root = SimpleTree(0) # type: TreeLike # OK

Using ``isinstance()`` with protocols
*************************************

To use a protocol class with ``isinstance()``, one needs to decorate it with
a special ``typing_extensions.runtime`` decorator. It will add support for
basic runtime structural checks:

.. code-block:: python

from typing_extensions import Protocol, runtime

@runtime
class Portable(Protocol):
handles: int

class Mug:
def __init__(self) -> None:
self.handles = 1

mug = Mug()
if isinstance(mug, Portable):
use(mug.handles) # Works statically and at runtime.

.. note::
``isinstance()`` is with protocols not completely safe at runtime.
For example, signatures of methods are not checked. The runtime
implementation only checks the presence of all protocol members
in object's MRO.

There are also plans to support more Python-style "duck typing" in
the type system. The details are still open.
42 changes: 42 additions & 0 deletions docs/source/common_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,48 @@ Possible strategies in such situations are:
return x[0]
f_good(new_lst) # OK

Covariant subtyping of mutable protocol members is rejected
-----------------------------------------------------------

Mypy rejects this because this is potentially unsafe.
Consider this example:

.. code-block:: python

from typing_extensions import Protocol

class P(Protocol):
x: float

def fun(arg: P) -> None:
arg.x = 3.14

class C:
x = 42
c = C()
fun(c) # This is not safe
c.x << 5 # Since this will fail!

To work around this problem consider whether "mutating" is actually part
of a protocol. If not, then one can use a ``@property`` in
the protocol definition:

.. code-block:: python

from typing_extensions import Protocol

class P(Protocol):
@property
def x(self) -> float:
pass

def fun(arg: P) -> None:
...

class C:
x = 42
fun(C()) # OK

Declaring a supertype as variable type
--------------------------------------

Expand Down
37 changes: 20 additions & 17 deletions docs/source/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,35 +101,38 @@ Is mypy free?
Yes. Mypy is free software, and it can also be used for commercial and
proprietary projects. Mypy is available under the MIT license.

Why not use structural subtyping?
*********************************
Can I use structural subtyping?
*******************************

Mypy primarily uses `nominal subtyping
<https://en.wikipedia.org/wiki/Nominative_type_system>`_ instead of
Mypy provides support for both `nominal subtyping
<https://en.wikipedia.org/wiki/Nominative_type_system>`_ and
`structural subtyping
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mention that structural subtyping is still experimental.

<https://en.wikipedia.org/wiki/Structural_type_system>`_. Some argue
that structural subtyping is better suited for languages with duck
typing such as Python.

Here are some reasons why mypy uses nominal subtyping:
<https://en.wikipedia.org/wiki/Structural_type_system>`_.
Support for structural subtyping is considered experimental.
Some argue that structural subtyping is better suited for languages with duck
typing such as Python. Mypy however primarily uses nominal subtyping,
leaving structural subtyping opt-in. Here are some reasons why:

1. It is easy to generate short and informative error messages when
using a nominal type system. This is especially important when
using type inference.

2. Python supports basically nominal isinstance tests and they are
widely used in programs. It is not clear how to support isinstance
in a purely structural type system while remaining compatible with
Python idioms.
2. Python provides built-in support for nominal ``isinstance()`` tests and
they are widely used in programs. Only limited support for structural
``isinstance()`` exists for ABCs in ``collections.abc`` and ``typing``
standard library modules.

3. Many programmers are already familiar with nominal subtyping and it
has been successfully used in languages such as Java, C++ and
C#. Only few languages use structural subtyping.

However, structural subtyping can also be useful. Structural subtyping
is a likely feature to be added to mypy in the future, even though we
expect that most mypy programs will still primarily use nominal
subtyping.
However, structural subtyping can also be useful. For example, a "public API"
may be more flexible if it is typed with protocols. Also, using protocol types
removes the necessity to explicitly declare implementations of ABCs.
As a rule of thumb, we recommend using nominal classes where possible, and
protocols where necessary. For more details about protocol types and structural
subtyping see :ref:`protocol-types` and
`PEP 544 <https://www.python.org/dev/peps/pep-0544/>`_.

I like Python and I have no need for static typing
**************************************************
Expand Down
69 changes: 69 additions & 0 deletions docs/source/generics.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Generics
========

.. _generic-classes:

Defining generic classes
************************

Expand Down Expand Up @@ -489,6 +491,73 @@ restrict the valid values for the type parameter in the same way.
A type variable may not have both a value restriction (see
:ref:`type-variable-value-restriction`) and an upper bound.

Generic protocols
*****************

Generic protocols (see :ref:`protocol-types`) are also supported, generic
protocols mostly follow the normal rules for generic classes, the main
difference is that mypy checks that declared variance of type variables is
compatible with the class definition. Examples:

.. code-block:: python

from typing import TypeVar
from typing_extensions import Protocol

T = TypeVar('T')

class Box(Protocol[T]):
content: T

def do_stuff(one: Box[str], other: Box[bytes]) -> None:
...

class StringWrapper:
def __init__(self, content: str) -> None:
self.content = content

class BytesWrapper:
def __init__(self, content: bytes) -> None:
self.content = content

do_stuff(StringWrapper('one'), BytesWrapper(b'other')) # OK

x = None # type: Box[float]
y = None # type: Box[int]
x = y # Error, since the protocol 'Box' is invariant.

class AnotherBox(Protocol[T]): # Error, covariant type variable expected
def content(self) -> T:
...

T_co = TypeVar('T_co', covariant=True)
class AnotherBox(Protocol[T_co]): # OK
def content(self) -> T_co:
...

ax = None # type: AnotherBox[float]
ay = None # type: AnotherBox[int]
ax = ay # OK for covariant protocols

See :ref:`variance-of-generics` above for more details on variance.
Generic protocols can be recursive, for example:

.. code-block:: python

T = TypeVar('T')
class Linked(Protocol[T]):
val: T
def next(self) -> 'Linked[T]': ...

class L:
val: int
def next(self) -> 'L': ...

def last(seq: Linked[T]) -> T:
...

result = last(L()) # The inferred type of 'result' is 'int'

.. _declaring-decorators:

Declaring decorators
Expand Down
Loading