New version of PEP 484 for review by python-dev.
This commit is contained in:
parent
7447d4fe83
commit
4f41b1f9e3
696
pep-0484.txt
696
pep-0484.txt
|
@ -3,12 +3,13 @@ Title: Type Hints
|
|||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Guido van Rossum <guido@python.org>, Jukka Lehtosalo <jukka.lehtosalo@iki.fi>, Łukasz Langa <lukasz@langa.pl>
|
||||
BDFL-delegate: Mark Shannon
|
||||
Discussions-To: Python-Dev <python-dev@python.org>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 29-Sep-2014
|
||||
Post-History: 16-Jan-2015,20-Mar-2015
|
||||
Post-History: 16-Jan-2015,20-Mar-2015,17-Apr-2015
|
||||
Resolution:
|
||||
|
||||
|
||||
|
@ -33,7 +34,7 @@ The proposal is strongly inspired by mypy [mypy]_. For example, the
|
|||
type "sequence of integers" can be written as ``Sequence[int]``. The
|
||||
square brackets mean that no new syntax needs to be added to the
|
||||
language. The example here uses a custom class ``Sequence``, imported
|
||||
from a pure-Python module ``typing.py``. The ``Sequence[int]``
|
||||
from a pure-Python module ``typing``. The ``Sequence[int]``
|
||||
notation works by implementing ``__getitem__()`` in the metaclass.
|
||||
|
||||
The type system supports unions, generic types, and a special type
|
||||
|
@ -78,47 +79,134 @@ typed language, and the authors have no desire to ever make type hints
|
|||
mandatory, even by convention.
|
||||
|
||||
|
||||
What is checked?
|
||||
================
|
||||
|
||||
Any function (or method -- for brevity we won't be repeating this)
|
||||
with at least one argument or return annotation is checked, unless
|
||||
type checking is disabled by the ``@no_type_check`` decorator or a
|
||||
``# type: ignore`` comment (see below).
|
||||
|
||||
A checked function should have annotations for all its arguments and
|
||||
its return type, with the exception that the ``self`` argument of a
|
||||
method should not be annotated; it is assumed to have the type of the
|
||||
containing class. Notably, the return type of ``__init__`` should be
|
||||
annotated with ``-> None``.
|
||||
|
||||
The body of a checked function is checked for consistency with the
|
||||
given annotations. The annotations are also used to check correctness
|
||||
of calls appearing in other checked functions.
|
||||
|
||||
Functions without any annotations (or whose checking is disabled) are
|
||||
assumed to have type ``Any`` when they are referenced in checked
|
||||
functions, and this should completely silence complaints from the
|
||||
checker regarding those references (although a checker may still
|
||||
request that a type be specified using a cast or a ``# type:`` comment
|
||||
if a more specific type than ``Any`` is needed for analysis of
|
||||
subsequent code).
|
||||
|
||||
A type checker should understand decorators; this may require
|
||||
annotations on decorator definitions. In particular, a type checker
|
||||
should understand the built-in decorators ``@property``,
|
||||
``@staticmethod`` and ``@classmethod``. The first argument of a class
|
||||
method should not be annotated; it is assumed to be a subclass of the
|
||||
defining class.
|
||||
|
||||
|
||||
Type Definition Syntax
|
||||
======================
|
||||
|
||||
The syntax leverages PEP 3107-style annotations with a number of
|
||||
extensions described in sections below. In its basic form, type hinting
|
||||
is used by filling function annotations with classes::
|
||||
extensions described in sections below. In its basic form, type
|
||||
hinting is used by filling function annotation slots with classes::
|
||||
|
||||
def greeting(name: str) -> str:
|
||||
return 'Hello ' + name
|
||||
|
||||
This denotes that the expected type of the ``name`` argument is ``str``.
|
||||
Analogically, the expected return type is ``str``. Subclasses of
|
||||
a specified argument type are also accepted as valid types for that
|
||||
argument.
|
||||
This states that the expected type of the ``name`` argument is
|
||||
``str``. Analogically, the expected return type is ``str``.
|
||||
|
||||
Abstract base classes, types available in the ``types`` module, and
|
||||
user-defined classes may be used as type hints as well. Annotations
|
||||
must be valid expressions that evaluate without raising exceptions at
|
||||
the time the function is defined. In addition, the needs of static
|
||||
analysis require that annotations must be simple enough to be
|
||||
interpreted by static analysis tools. (This is an intentionally
|
||||
somewhat vague requirement.)
|
||||
|
||||
.. FIXME: Define rigorously what is/isn't supported.
|
||||
|
||||
When used as an annotation, the expression ``None`` is considered
|
||||
equivalent to ``NoneType`` (i.e., ``type(None)`` for type hinting
|
||||
purposes.
|
||||
|
||||
Type aliases are also valid type hints::
|
||||
|
||||
integer = int
|
||||
|
||||
def retry(url: str, retry_count: integer) -> None: ...
|
||||
|
||||
New names that are added to support features described in following
|
||||
sections are available in the ``typing`` package.
|
||||
Expressions whose type is a subtype of a specific argument type are
|
||||
also accepted for that argument.
|
||||
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
Acceptable type hints
|
||||
---------------------
|
||||
|
||||
Type hints may be built-in classes (including those defined in
|
||||
standard library or third-party extension modules), abstract base
|
||||
classes, types available in the ``types`` module, and user-defined
|
||||
classes (including those defined in the standard library or
|
||||
third-party modules). Annotations for built-in classes (and other
|
||||
classes at the discretion of the developer) may be placed in stub
|
||||
files (see below).
|
||||
|
||||
Annotations must be valid expressions that evaluate without raising
|
||||
exceptions at the time the function is defined (but see below for
|
||||
forward references).
|
||||
|
||||
The needs of static analysis require that annotations must be simple
|
||||
enough to be interpreted by static analysis tools. In particular,
|
||||
dynamically computed types are not acceptable. (This is an
|
||||
intentionally somewhat vague requirement, specific inclusions and
|
||||
exclusions may be added to future versions of this PEP as warranted by
|
||||
the discussion.)
|
||||
|
||||
In addition to the above, the following special constructs defined
|
||||
below may be used: ``None``, ``Any``, ``Union``, ``Tuple``,
|
||||
``Callable``, all ABCs and stand-ins for concrete classes exported
|
||||
from ``typing`` (e.g. ``Sequence`` and ``Dict``), type variables, and
|
||||
type aliases.
|
||||
|
||||
All newly introducedw names used to support features described in
|
||||
following sections (such as ``Any`` and ``Union``) are available in
|
||||
the ``typing`` module.
|
||||
|
||||
|
||||
Using None
|
||||
----------
|
||||
|
||||
When used in a type hint, the expression ``None`` is considered
|
||||
equivalent to ``type(None)``.
|
||||
|
||||
|
||||
Type aliases
|
||||
------------
|
||||
|
||||
Type aliases are defined by simple variable assignments::
|
||||
|
||||
Url = str
|
||||
|
||||
def retry(url: Url, retry_count: int) -> None: ...
|
||||
|
||||
Note that we recommend capitalizing alias names, since they represent
|
||||
user-defined types, which (like user-defined classes) are typically
|
||||
spelled that way.
|
||||
|
||||
Type aliases may be as complex as type hints in annotations --
|
||||
anything that is acceptable as a type hint is acceptable in a type
|
||||
alias::
|
||||
|
||||
from typing import TypeVar, Iterable, Tuple
|
||||
|
||||
T = TypeVar('T', int, float, complex)
|
||||
Vector = Iterable[Tuple[T, T]]
|
||||
|
||||
def inproduct(v: Vector) -> T:
|
||||
return sum(x*y for x, y in v)
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
from typing import TypeVar, Iterable, Tuple
|
||||
|
||||
T = TypeVar('T', int, float, complex)
|
||||
|
||||
def inproduct(v: Iterable[Tuple[T, T]]) -> T:
|
||||
return sum(x*y for x, y in v)
|
||||
|
||||
|
||||
Callable
|
||||
--------
|
||||
|
||||
Frameworks expecting callback functions of specific signatures might be
|
||||
type hinted using ``Callable[[Arg1Type, Arg2Type], ReturnType]``.
|
||||
|
@ -173,44 +261,217 @@ Generics can be parametrized by using a new factory available in
|
|||
def first(l: Sequence[T]) -> T: # Generic function
|
||||
return l[0]
|
||||
|
||||
In this case the contract is that the returning value is consistent with
|
||||
In this case the contract is that the returned value is consistent with
|
||||
the elements held by the collection.
|
||||
|
||||
``TypeVar`` supports constraining parametric types to classes with any of
|
||||
the specified bases. Example::
|
||||
``TypeVar`` supports constraining parametric types to a fixed set of
|
||||
possible types. For example, we can define a type variable that ranges
|
||||
over just ``str`` and ``bytes``. By default, a type variable ranges
|
||||
over all possible types. Example of constraining a type variable::
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
AnyStr = TypeVar('AnyStr', str, bytes)
|
||||
|
||||
def concat(x: AnyStr, y: AnyStr) -> AnyStr:
|
||||
return x + y
|
||||
|
||||
The function ``concat`` can be called with either two ``str`` arguments
|
||||
or two ``bytes`` arguments, but not with a mix of ``str`` and ``bytes``
|
||||
arguments.
|
||||
|
||||
Note that subtypes of types constrained by a type variable are treated
|
||||
as their respective explicitly listed base types in the context of the
|
||||
type variable. Consider this example::
|
||||
|
||||
class MyStr(str): ...
|
||||
|
||||
x = concat(MyStr('apple'), MyStr('pie'))
|
||||
|
||||
The call is valid but the type variable ``AnyStr`` will be set to
|
||||
``str`` and not ``MyStr``. In effect, the inferred type of the return
|
||||
value assigned to ``x`` will also be ``str``.
|
||||
|
||||
Additionally, ``Any`` is a valid value for every type variable.
|
||||
Consider the following::
|
||||
|
||||
def count_truthy(elements: List[Any]) -> int:
|
||||
return sum(1 for elem in elements if element)
|
||||
|
||||
This is equivalent to omitting the generic notation and just saying
|
||||
``elements: List``.
|
||||
|
||||
|
||||
User-defined generic types
|
||||
--------------------------
|
||||
|
||||
You can include a ``Generic`` base class to define a user-defined class
|
||||
as generic. Example::
|
||||
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class LoggedVar(Generic[T]):
|
||||
def __init__(self, value: T, name: str, logger: Logger) -> None:
|
||||
self.name = name
|
||||
self.logger = logger
|
||||
self.value = value
|
||||
|
||||
def set(self, new: T) -> None:
|
||||
self.log('Set ' + repr(self.value))
|
||||
self.value = new
|
||||
|
||||
def get(self) -> T:
|
||||
self.log('Get ' + repr(self.value))
|
||||
return self.value
|
||||
|
||||
def log(self, message: str) -> None:
|
||||
self.logger.info('{}: {}'.format(self.name message))
|
||||
|
||||
``Generic[T]`` as a base class defines that the class ``LoggedVar``
|
||||
takes a single type parameter ``T``. This also makes ``T`` valid as
|
||||
a type within the class body.
|
||||
|
||||
The ``Generic`` base class uses a metaclass that defines ``__getitem__``
|
||||
so that ``LoggedVar[t]`` is valid as a type::
|
||||
|
||||
from typing import Iterable
|
||||
|
||||
X = TypeVar('X')
|
||||
Y = TypeVar('Y', Iterable[X])
|
||||
def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
|
||||
for var in vars:
|
||||
var.set(0)
|
||||
|
||||
def filter(rule: Callable[[X], bool], input: Y) -> Y:
|
||||
A generic type can have any number of type variables, and type variables
|
||||
may be constrained. This is valid::
|
||||
|
||||
from typing import TypeVar, Generic
|
||||
...
|
||||
|
||||
T = TypeVar('T')
|
||||
S = TypeVar('S')
|
||||
|
||||
class Pair(Generic[T, S]):
|
||||
...
|
||||
|
||||
.. FIXME: Add an example with multiple bases defined.
|
||||
Each type variable argument to ``Generic`` must be distinct. This is
|
||||
thus invalid::
|
||||
|
||||
In the example above we specify that ``Y`` can be any subclass of
|
||||
Iterable with elements of type ``X``, as long as the return type of
|
||||
``filter()`` will be the same as the type of the ``input``
|
||||
argument.
|
||||
from typing import TypeVar, Generic
|
||||
...
|
||||
|
||||
.. FIXME: Explain more about how this works.
|
||||
T = TypeVar('T')
|
||||
|
||||
class Pair(Generic[T, T]): # INVALID
|
||||
...
|
||||
|
||||
You can use multiple inheritance with ``Generic``::
|
||||
|
||||
from typing import TypeVar, Generic, Sized
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class LinkedList(Sized, Generic[T]):
|
||||
...
|
||||
|
||||
|
||||
Arbitrary generic types as base classes
|
||||
---------------------------------------
|
||||
|
||||
``Generic[T]`` is only valid as a base class -- it's not a proper type.
|
||||
However, user-defined generic types such as ``LinkedList[T]`` from the
|
||||
above example and built-in generic types and ABCs such as ``List[T]``
|
||||
and ``Iterable[T]`` are valid both as types and as base classes. For
|
||||
example, we can define a subclass of ``Dict`` that specializes type
|
||||
arguments::
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
class Node:
|
||||
...
|
||||
|
||||
class SymbolTable(Dict[str, List[Node]]):
|
||||
def push(self, name: str, node: Node) -> None:
|
||||
self.setdefault(name, []).append(node)
|
||||
|
||||
def pop(self, name: str) -> Node:
|
||||
return self[name].pop()
|
||||
|
||||
def lookup(self, name: str) -> Optional[Node]:
|
||||
nodes = self.get(name)
|
||||
if nodes:
|
||||
return nodes[-1]
|
||||
return None
|
||||
|
||||
``SymbolTable`` is a subclass of ``dict`` and a subtype of ``Dict[str,
|
||||
List[Node]]``.
|
||||
|
||||
If a generic base class has a type variable as a type argument, this
|
||||
makes the defined class generic. For example, we can define a generic
|
||||
``LinkedList`` class that is iterable and a container::
|
||||
|
||||
from typing import TypeVar, Iterable, Container
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class LinkedList(Iterable[T], Container[T]):
|
||||
...
|
||||
|
||||
Now ``LinkedList[int]`` is a valid type. Note that we can use ``T``
|
||||
multiple times in the base class list, as long as we don't use the
|
||||
same type variable ``T`` multiple times within ``Generic[...]``.
|
||||
|
||||
|
||||
Abstract generic types
|
||||
----------------------
|
||||
|
||||
The metaclass used by ``Generic`` is a subclass of ``abc.ABCMeta``.
|
||||
A generic class can be an ABC by including abstract methods
|
||||
or properties, and generic classes can also have ABCs as base
|
||||
classes without a metaclass conflict.
|
||||
|
||||
|
||||
Forward references
|
||||
------------------
|
||||
|
||||
When a type hint contains names that have not been defined yet, that
|
||||
definition may be expressed as a string, to be resolved later. For
|
||||
example, instead of writing::
|
||||
definition may be expressed as a string literal, to be resolved later.
|
||||
|
||||
def notify_by_email(employees: Set[Employee]) -> None: ...
|
||||
A situation where this occurs commonly is the definition of a
|
||||
container class, where the class being defined occurs in the signature
|
||||
of some of the methods. For example, the following code (the start of
|
||||
a simple binary tree implementation) does not work::
|
||||
|
||||
one might write::
|
||||
class Tree:
|
||||
def __init__(self, left: Tree, right: Tree):
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def notify_by_email(employees: 'Set[Employee]') -> None: ...
|
||||
To address this, we write::
|
||||
|
||||
.. FIXME: Rigorously define this, and give a motivational example.
|
||||
class Tree:
|
||||
def __init__(self, left: 'Tree', right: 'Tree'):
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
The string literal should contain a valid Python expression (i.e.,
|
||||
``compile(lit, '', 'expr')`` should be a valid code object) and it
|
||||
should evaluate without errors once the module has been fully loaded.
|
||||
The local and global namespace in which it is evaluated should be the
|
||||
same namespaces in which default arguments to the same function would
|
||||
be evaluated.
|
||||
|
||||
Moreover, the expression should be parseable as a valid type hint, i.e.,
|
||||
it is constrained by the rules from the section `Acceptable type hints`_
|
||||
above.
|
||||
|
||||
It is allowable to use string literals as *part* of a type hint, for
|
||||
example::
|
||||
|
||||
class Tree:
|
||||
...
|
||||
def leaves(self) -> List['Tree']:
|
||||
...
|
||||
|
||||
|
||||
Union types
|
||||
|
@ -264,18 +525,17 @@ When the type of a value is ``object``, the type checker will reject
|
|||
almost all operations on it, and assigning it to a variable (or using
|
||||
it as a return value) of a more specialized type is a type error. On
|
||||
the other hand, when a value has type ``Any``, the type checker will
|
||||
allow all operations on it, and a value of type `Any`` can be assigned
|
||||
allow all operations on it, and a value of type ``Any`` can be assigned
|
||||
to a variable (or used as a return value) of a more constrained type.
|
||||
|
||||
|
||||
Platform-specific type checking
|
||||
-------------------------------
|
||||
Predefined constants
|
||||
--------------------
|
||||
|
||||
In some cases the typing information will depend on the platform that
|
||||
the program is being executed on. To enable specifying those
|
||||
differences, simple conditionals can be used::
|
||||
Some predefined Boolean constants are defined in the ``typing``
|
||||
module to enable platform-specific type definitions and such::
|
||||
|
||||
from typing import PY2, WINDOWS
|
||||
from typing import PY2, PY3, WINDOWS, POSIX
|
||||
|
||||
if PY2:
|
||||
text = unicode
|
||||
|
@ -289,66 +549,82 @@ differences, simple conditionals can be used::
|
|||
else:
|
||||
loop = UnixSelectorEventLoop
|
||||
|
||||
.. FIXME: Also define PY3 and POSIX?
|
||||
|
||||
Arbitrary literals defined in the form of ``NAME = True`` will also be
|
||||
accepted by the type checker to differentiate type resolution::
|
||||
|
||||
DEBUG = False
|
||||
...
|
||||
if DEBUG:
|
||||
class Tracer:
|
||||
<verbose implementation>
|
||||
else:
|
||||
class Tracer:
|
||||
<dummy implementation>
|
||||
|
||||
For the purposes of type hinting, the type checker assumes ``__debug__``
|
||||
is set to ``True``, in other words the ``-O`` command-line option is not
|
||||
used while type checking.
|
||||
It is up to the type checker implementation to define their values, as
|
||||
long as ``PY2 == not PY3`` and ``WINDOWS == not POSIX``. When the
|
||||
program is being executed these always reflect the current platform,
|
||||
and this is also the suggested default when the program is being
|
||||
type-checked.
|
||||
|
||||
|
||||
Compatibility with other uses of function annotations
|
||||
-----------------------------------------------------
|
||||
=====================================================
|
||||
|
||||
A number of existing or potential use cases for function annotations
|
||||
exist, which are incompatible with type hinting. These may confuse a
|
||||
static type checker. However, since type hinting annotations have no
|
||||
runtime behavior (other than evaluation of the annotation expression
|
||||
and storing annotations in the ``__annotations__`` attribute of the
|
||||
function object), this does not make the program incorrect -- it just
|
||||
makes it issue warnings when a static analyzer is used.
|
||||
exist, which are incompatible with type hinting. These may confuse
|
||||
a static type checker. However, since type hinting annotations have no
|
||||
runtime behavior (other than evaluation of the annotation expression and
|
||||
storing annotations in the ``__annotations__`` attribute of the function
|
||||
object), this does not make the program incorrect -- it just may cause
|
||||
a type checker to emit spurious warnings or errors.
|
||||
|
||||
To mark portions of the program that should not be covered by type
|
||||
hinting, use the following:
|
||||
hinting, you can use one or more of the following:
|
||||
|
||||
* a ``@no_type_check`` decorator on classes and functions
|
||||
* a ``# type: ignore`` comment;
|
||||
|
||||
* a ``# type: ignore`` comment on arbitrary lines
|
||||
* a ``@no_type_check`` decorator on a class or function;
|
||||
|
||||
.. FIXME: should we have a module-wide comment as well?
|
||||
* a custom class or function decorator marked with
|
||||
``@no_type_check_decorator``.
|
||||
|
||||
.. FIXME: suggest that other uses of annotations be replaced with decorators
|
||||
For more details see later sections.
|
||||
|
||||
.. FIXME: add reference to "rejected alternatives"
|
||||
In order for maximal compatibility with offline type checking it may
|
||||
eventually be a good idea to change interfaces that rely on annotations
|
||||
to switch to a different mechanism, for example a decorator. In Python
|
||||
3.5 there is no pressure to do this, however. See also the longer
|
||||
discussion under `Rejected alternatives`_ below.
|
||||
|
||||
|
||||
Type Hints on Local and Global Variables
|
||||
========================================
|
||||
Type comments
|
||||
=============
|
||||
|
||||
No first-class syntax support for explicitly marking variables as being
|
||||
of a specific type is added by this PEP. To help with type inference in
|
||||
complex cases, a comment of the following format may be used::
|
||||
|
||||
x = [] # type: List[Employee]
|
||||
x, y, z = [], [], [] # type: List[int], List[int], List[str]
|
||||
x, y, z = [], [], [] # type: (List[int], List[int], List[str])
|
||||
x = [
|
||||
1,
|
||||
2,
|
||||
] # type: List[int]
|
||||
|
||||
In the case where type information for a local variable is needed before
|
||||
it is declared, an ``Undefined`` placeholder might be used::
|
||||
Type comments should be put on the last line of the statement that
|
||||
contains the variable definition. They can also be placed on
|
||||
``with`` statements and ``for`` statements, right after the colon.
|
||||
|
||||
from typing import Undefined
|
||||
Examples of type comments on ``with`` and ``for`` statements::
|
||||
|
||||
x = Undefined # type: List[Employee]
|
||||
y = Undefined(int)
|
||||
with frobnicate() as foo: # type: int
|
||||
# Here foo is an int
|
||||
...
|
||||
|
||||
for x, y in points: # type: float, float
|
||||
# Here x and y are floats
|
||||
...
|
||||
|
||||
The ``# type: ignore`` comment should be put on the line that the
|
||||
error refers to::
|
||||
|
||||
import http.client
|
||||
errors = {
|
||||
'not_found': http.client.NOT_FOUND # type: ignore
|
||||
}
|
||||
|
||||
A ``# type: ignore`` comment on a line by itself disables all type
|
||||
checking for the rest of the file.
|
||||
|
||||
If type hinting proves useful in general, a syntax for typing variables
|
||||
may be provided in a future Python version.
|
||||
|
@ -360,7 +636,7 @@ Occasionally the type checker may need a different kind of hint: the
|
|||
programmer may know that an expression is of a more constrained type
|
||||
than the type checker infers. For example::
|
||||
|
||||
from typing import List
|
||||
from typing import List, cast
|
||||
|
||||
def find_first_str(a: List[object]) -> str:
|
||||
index = next(i for i, x in enumerate(a) if isinstance(x, str))
|
||||
|
@ -374,11 +650,11 @@ the type of ``x`` is ``t``. At runtime a cast always returns the
|
|||
expression unchanged -- it does not check the type, and it does not
|
||||
convert or coerce the value.
|
||||
|
||||
Casts differ from type comments (see the previous section). When
|
||||
using a type comment, the type checker should still verify that the
|
||||
inferred type is consistent with the stated type. When using a cast,
|
||||
the type checker trusts the programmer. Also, casts can be used in
|
||||
expressions, while type comments only apply to assignments.
|
||||
Casts differ from type comments (see the previous section). When using
|
||||
a type comment, the type checker should still verify that the inferred
|
||||
type is consistent with the stated type. When using a cast, the type
|
||||
checker should blindly believe the programmer. Also, casts can be used
|
||||
in expressions, while type comments only apply to assignments.
|
||||
|
||||
|
||||
Stub Files
|
||||
|
@ -390,28 +666,31 @@ stub files:
|
|||
|
||||
* Extension modules
|
||||
|
||||
* 3rd party modules whose authors have not yet added type hints
|
||||
* Third-party modules whose authors have not yet added type hints
|
||||
|
||||
* Standard library modules for which type hints have not yet been written
|
||||
* Standard library modules for which type hints have not yet been
|
||||
written
|
||||
|
||||
* Modules that must be compatible with Python 2 and 3
|
||||
|
||||
* Modules that use annotations for other purposes
|
||||
|
||||
Stub files have the same syntax as regular Python modules. There is
|
||||
one feature of the ``typing`` module that may only be used in stub
|
||||
files: the ``@overload`` decorator described below.
|
||||
Stub files have the same syntax as regular Python modules. There is one
|
||||
feature of the ``typing`` module that may only be used in stub files:
|
||||
the ``@overload`` decorator described below.
|
||||
|
||||
The type checker should only check function signatures in stub files;
|
||||
function bodies in stub files should just be a single ``pass`` statement.
|
||||
function bodies in stub files should just be a single ``pass``
|
||||
statement.
|
||||
|
||||
The type checker should have a configurable search path for stub
|
||||
files. If a stub file is found the type checker should not read the
|
||||
The type checker should have a configurable search path for stub files.
|
||||
If a stub file is found the type checker should not read the
|
||||
corresponding "real" module.
|
||||
|
||||
Stub files may use the ``.py`` extension or alternatively may use the
|
||||
``.pyi`` extension. The latter makes it possible to maintain stub
|
||||
files in the same directory as the corresponding real module.
|
||||
While stub files are syntactically valid Python modules, they use the
|
||||
``.pyi`` extension to make it possible to maintain stub files in the
|
||||
same directory as the corresponding real module. This also reinforces
|
||||
the notion that no runtime behavior should be expected of stub files.
|
||||
|
||||
Function overloading
|
||||
--------------------
|
||||
|
@ -478,6 +757,54 @@ satisfactory multiple dispatch design, but we don't want such a design
|
|||
to be constrained by the overloading syntax defined for type hints in
|
||||
stub files.
|
||||
|
||||
Storing and distributing stub files
|
||||
-----------------------------------
|
||||
|
||||
The easiest form of stub file storage and distribution is to put them
|
||||
alongside Python modules in the same directory. This makes them easy to
|
||||
find by both programmers and the tools. However, since package
|
||||
maintainers are free not to add type hinting to their packages,
|
||||
third-party stubs installable by ``pip`` from PyPI are also supported.
|
||||
In this case we have to consider three issues: naming, versioning,
|
||||
installation path.
|
||||
|
||||
This PEP does not provide a recommendation on a naming scheme that
|
||||
should be used for third-party stub file packages. Discoverability will
|
||||
hopefully be based on package popularity, like with Django packages for
|
||||
example.
|
||||
|
||||
Third-party stubs have to be versioned using the lowest version of the
|
||||
source package that is compatible. Example: FooPackage has versions
|
||||
1.0, 1.1, 1.2, 1.3, 2.0, 2.1, 2.2. There are API changes in versions
|
||||
1.1, 2.0 and 2.2. The stub file package maintainer is free to release
|
||||
stubs for all versions but at least 1.0, 1.1, 2.0 and 2.2 are needed
|
||||
to enable the end user type check all versions. This is because the
|
||||
user knows that the closest *lower or equal* version of stubs is
|
||||
compatible. In the provided example, for FooPackage 1.3 the user would
|
||||
choose stubs version 1.1.
|
||||
|
||||
Note that if the user decides to use the "latest" available source
|
||||
package, using the "latest" stub files should generally also work if
|
||||
they're updated often.
|
||||
|
||||
Third-party stub packages can use any location for stub storage. The
|
||||
type checker will search for them using PYTHONPATH. A default fallback
|
||||
directory that is always checked is ``shared/typehints/python3.5/`` (or
|
||||
3.6, etc.). Since there can only be one package installed for a given
|
||||
Python version per environment, no additional versioning is performed
|
||||
under that directory (just like bare directory installs by ``pip`` in
|
||||
site-packages). Stub file package authors might use the following
|
||||
snippet in ``setup.py``::
|
||||
|
||||
...
|
||||
data_files=[
|
||||
(
|
||||
'shared/typehints/python{}.{}'.format(*sys.version_info[:2]),
|
||||
pathlib.Path(SRC_PATH).glob('**/*.pyi'),
|
||||
),
|
||||
],
|
||||
...
|
||||
|
||||
|
||||
Exceptions
|
||||
==========
|
||||
|
@ -488,12 +815,12 @@ in which case the recommendation is to put this information in a
|
|||
docstring.
|
||||
|
||||
|
||||
The ``typing`` Package
|
||||
======================
|
||||
The ``typing`` Module
|
||||
=====================
|
||||
|
||||
To open the usage of static type checking to Python 3.5 as well as older
|
||||
versions, a uniform namespace is required. For this purpose, a new
|
||||
package in the standard library is introduced called ``typing``. It
|
||||
module in the standard library is introduced called ``typing``. It
|
||||
holds a set of classes representing builtin types with generics, namely:
|
||||
|
||||
* Dict, used as ``Dict[key_type, value_type]``
|
||||
|
@ -511,12 +838,24 @@ holds a set of classes representing builtin types with generics, namely:
|
|||
using one type and ellipsis, for example ``Tuple[int, ...]``.
|
||||
(The ``...`` here are part of the syntax.)
|
||||
|
||||
* NamedTuple, used as
|
||||
``NamedTuple(type_name, [(field_name, field_type), ...])``
|
||||
and equivalent to
|
||||
``collections.namedtuple(type_name, [field_name, ...])``.
|
||||
|
||||
The generic versions of concrete collection types (``Dict``, ``List``,
|
||||
``Set``, ``FrozenSet``, and homogeneous arbitrary-length ``Tuple``)
|
||||
are mainly useful for annotating return values. For arguments, prefer
|
||||
the abstract collection types defined below, e.g. ``Mapping``,
|
||||
``Sequence`` or ``AbstractSet``.
|
||||
|
||||
The ``typing`` module defines the ``Generator`` type for return values
|
||||
of generator functions. It is a subtype of ``Iterable`` and it has
|
||||
additional type variables for the type accepted by the ``send()``
|
||||
method and the return type of the generator:
|
||||
|
||||
* Generator, used as ``Generator[yield_type, send_type, return_type]``
|
||||
|
||||
It also introduces factories and helper members needed to express
|
||||
generics and union types:
|
||||
|
||||
|
@ -527,24 +866,20 @@ generics and union types:
|
|||
* TypeVar, used as ``X = TypeVar('X', Type1, Type2, Type3)`` or simply
|
||||
``Y = TypeVar('Y')``
|
||||
|
||||
* Undefined, used as ``local_variable = Undefined # type: List[int]`` or
|
||||
``local_variable = Undefined(List[int])`` (the latter being slower
|
||||
during runtime)
|
||||
|
||||
* Callable, used as ``Callable[[Arg1Type, Arg2Type], ReturnType]``
|
||||
|
||||
* AnyStr, equivalent to ``TypeVar('AnyStr', str, bytes)``
|
||||
* AnyStr, defined as ``TypeVar('AnyStr', str, bytes)``
|
||||
|
||||
All abstract base classes available in ``collections.abc`` are
|
||||
importable from the ``typing`` package, with added generics support:
|
||||
importable from the ``typing`` module, with added generics support:
|
||||
|
||||
* ByteString
|
||||
|
||||
* Callable
|
||||
* Callable (see above)
|
||||
|
||||
* Container
|
||||
|
||||
* Hashable
|
||||
* Hashable (not generic, but present for completeness)
|
||||
|
||||
* ItemsView
|
||||
|
||||
|
@ -566,14 +901,26 @@ importable from the ``typing`` package, with added generics support:
|
|||
|
||||
* Sequence
|
||||
|
||||
* Set as ``AbstractSet``. This name change was required because ``Set``
|
||||
in the ``typing`` module means ``set()`` with generics.
|
||||
* Set, renamed to ``AbstractSet``. This name change was required
|
||||
because ``Set`` in the ``typing`` module means ``set()`` with
|
||||
generics.
|
||||
|
||||
* Sized
|
||||
* Sized (not generic, but present for completeness)
|
||||
|
||||
* ValuesView
|
||||
|
||||
* Mapping
|
||||
A few one-off types are defined that test for single special methods
|
||||
(similar to ``Hashable`` or ``Sized``):
|
||||
|
||||
* Reversible, to test for ``__reversed__``
|
||||
|
||||
* SupportsAbs, to test for ``__abs__``
|
||||
|
||||
* SupportsFloat, to test for ``__float__``
|
||||
|
||||
* SupportsInt, to test for ``__int__``
|
||||
|
||||
* SupportsRound, to test for ``__round__``
|
||||
|
||||
The library includes literals for platform-specific type hinting:
|
||||
|
||||
|
@ -585,55 +932,42 @@ The library includes literals for platform-specific type hinting:
|
|||
|
||||
* POSIX, equivalent to ``not WINDOWS``
|
||||
|
||||
The following conveniece functions and decorators are exported:
|
||||
|
||||
* cast, described earlier
|
||||
|
||||
* no_type_check, a decorator to disable type checking per class or
|
||||
function (see below)
|
||||
|
||||
* no_type_check_decorator, a decorator to create your own decorators
|
||||
with the same meaning as ``@no_type_check`` (see below)
|
||||
|
||||
* overload, described earlier
|
||||
|
||||
* get_type_hints, a utility function to retrieve the type hints from a
|
||||
function or method. Given a function or method object, it returns
|
||||
a dict with the same format as ``__annotations__``, but evaluating
|
||||
forward references (which are given as string literals) as expressions
|
||||
in the context of the original function or method definition.
|
||||
|
||||
The following types are available in the ``typing.io`` module:
|
||||
|
||||
* IO
|
||||
* IO (generic over ``AnyStr``)
|
||||
|
||||
* BinaryIO
|
||||
* BinaryIO (a simple subclass of ``IO[bytes]``)
|
||||
|
||||
* TextIO
|
||||
* TextIO (a simple subclass of ``IO[str]``)
|
||||
|
||||
The following types are provided by the ``typing.re`` module:
|
||||
|
||||
* Match and Pattern, types of ``re.match()`` and ``re.compile()``
|
||||
results
|
||||
results (generic over ``AnyStr``)
|
||||
|
||||
As a convenience measure, types from ``typing.io`` and ``typing.re`` are
|
||||
also available in ``typing`` (quoting Guido, "There's a reason those
|
||||
modules have two-letter names.").
|
||||
|
||||
|
||||
The place of the ``typing`` module in the standard library
|
||||
----------------------------------------------------------
|
||||
|
||||
.. FIXME: complete this section (or discard?)
|
||||
|
||||
|
||||
Usage Patterns
|
||||
==============
|
||||
|
||||
The main use case of type hinting is static analysis using an external
|
||||
tool without executing the analyzed program. Existing tools used for
|
||||
that purpose like ``pyflakes`` [pyflakes]_ or ``pylint`` [pylint]_
|
||||
might be extended to support type checking. New tools, like mypy [mypy]_,
|
||||
can be adopted specifically for this purpose.
|
||||
|
||||
Type checking based on type hints is understood as a best-effort
|
||||
mechanism. In other words, whenever types are not annotated and cannot
|
||||
be inferred, the type checker considers such code valid. Type errors
|
||||
are only reported in case of explicit or inferred conflict. Moreover,
|
||||
as a mechanism that is not tied to execution of the code, it does not
|
||||
affect runtime behaviour. In other words, even in the case of a typing
|
||||
error, the program will continue running.
|
||||
|
||||
The implementation of a type checker, whether linting source files or
|
||||
enforcing type information during runtime, is out of scope for this PEP.
|
||||
|
||||
.. FIXME: This is somewhat redundant with the updated initial sections.
|
||||
|
||||
.. FIXME: Describe run-time behavior of generic types.
|
||||
|
||||
|
||||
Rejected Alternatives
|
||||
=====================
|
||||
|
||||
|
@ -771,7 +1105,7 @@ As written this will not work, because of the peculiarity in Python
|
|||
that class names become defined once the entire body of the class has
|
||||
been executed. Our solution, which isn't particularly elegant, but
|
||||
gets the job done, is to allow using string literals in annotations.
|
||||
Most of the time you won't have to use this though -- most _uses_ of
|
||||
Most of the time you won't have to use this though -- most *uses* of
|
||||
type hints are expected to reference builtin types or types defined in
|
||||
other modules.
|
||||
|
||||
|
@ -782,6 +1116,21 @@ all). This of course would run afoul of backwards compatibility,
|
|||
since the Python interpreter doesn't actually know whether a
|
||||
particular annotation is meant to be a type hint or something else.
|
||||
|
||||
A compromise is possible where a ``__future__`` import could enable
|
||||
turning *all* annotations in a given module into string literals, as
|
||||
follows::
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
class ImSet:
|
||||
def add(self, a: ImSet) -> List[ImSet]: ...
|
||||
|
||||
assert ImSet.add.__annotations__ == {'a': 'ImSet', 'return': 'List[ImSet]'}
|
||||
|
||||
Such a ``__future__`` import statement will be proposed in a separate
|
||||
PEP.
|
||||
|
||||
|
||||
The double colon
|
||||
----------------
|
||||
|
||||
|
@ -864,26 +1213,6 @@ It's also been proposed to simply wait another release. But what
|
|||
problem would that solve? It would just be procrastination.
|
||||
|
||||
|
||||
Is Type Hinting Pythonic?
|
||||
=========================
|
||||
|
||||
.. FIXME: Do we really need this section?
|
||||
|
||||
Type annotations provide important documentation for how a unit of code
|
||||
should be used. Programmers should therefore provide type hints on
|
||||
public APIs, namely argument and return types on functions and methods
|
||||
considered public. However, because types of local and global variables
|
||||
can be often inferred, they are rarely necessary.
|
||||
|
||||
The kind of information that type hints hold has always been possible to
|
||||
achieve by means of docstrings. In fact, a number of formalized
|
||||
mini-languages for describing accepted arguments have evolved. Moving
|
||||
this information to the function declaration makes it more visible and
|
||||
easier to access both at runtime and by static analysis. Adding to that
|
||||
the notion that “explicit is better than implicit”, type hints are
|
||||
indeed *Pythonic*.
|
||||
|
||||
|
||||
PEP Development Process
|
||||
=======================
|
||||
|
||||
|
@ -901,7 +1230,8 @@ Acknowledgements
|
|||
|
||||
This document could not be completed without valuable input,
|
||||
encouragement and advice from Jim Baker, Jeremy Siek, Michael Matson
|
||||
Vitousek, Andrey Vlasovskikh, and Radomir Dopieralski.
|
||||
Vitousek, Andrey Vlasovskikh, Radomir Dopieralski, Peter Ludemann,
|
||||
and the BDFL-Delegate, Mark Shannon.
|
||||
|
||||
Influences include existing languages, libraries and frameworks
|
||||
mentioned in PEP 482. Many thanks to their creators, in alphabetical
|
||||
|
|
Loading…
Reference in New Issue