Updated version of PEP 484.
This commit is contained in:
parent
c087f9657f
commit
cc952ce033
557
pep-0484.txt
557
pep-0484.txt
|
@ -9,17 +9,29 @@ Status: Draft
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
Content-Type: text/x-rst
|
Content-Type: text/x-rst
|
||||||
Created: 29-Sep-2014
|
Created: 29-Sep-2014
|
||||||
Post-History: 16-Jan-2015,20-Mar-2015,17-Apr-2015
|
Post-History: 16-Jan-2015,20-Mar-2015,17-Apr-2015,20-May-2015
|
||||||
Resolution:
|
Resolution:
|
||||||
|
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
========
|
========
|
||||||
|
|
||||||
This PEP introduces a standard syntax for type hints using annotations
|
PEP 3107 introduced syntax for function annotations, but the semantics
|
||||||
(PEP 3107) on function definitions. For example, here is a simple
|
were deliberately left undefined. There has now been enough 3rd party
|
||||||
function whose argument and return type are declared in the
|
usage for static type analysis that the community would benefit from
|
||||||
annotations::
|
a standard vocabulary and baseline tools within the standard library.
|
||||||
|
|
||||||
|
This PEP introduces a provisional module to provide these standard
|
||||||
|
definitions and tools, along with some conventions for situations
|
||||||
|
where annotations are not available.
|
||||||
|
|
||||||
|
Note that this PEP still explicitly does NOT prevent other uses of
|
||||||
|
annotations, nor does it require (or forbid) any particular processing
|
||||||
|
of annotations, even when they conform to this specification. It
|
||||||
|
simply enables better coordination, as PEP 333 did for web frameworks.
|
||||||
|
|
||||||
|
For example, here is a simple function whose argument and return type
|
||||||
|
are declared in the annotations::
|
||||||
|
|
||||||
def greeting(name: str) -> str:
|
def greeting(name: str) -> str:
|
||||||
return 'Hello ' + name
|
return 'Hello ' + name
|
||||||
|
@ -29,13 +41,17 @@ While these annotations are available at runtime through the usual
|
||||||
Instead, the proposal assumes the existence of a separate off-line
|
Instead, the proposal assumes the existence of a separate off-line
|
||||||
type checker which users can run over their source code voluntarily.
|
type checker which users can run over their source code voluntarily.
|
||||||
Essentially, such a type checker acts as a very powerful linter.
|
Essentially, such a type checker acts as a very powerful linter.
|
||||||
|
(While it would of course be possible for individual users to employ
|
||||||
|
a similar checker at run time for Design By Contract enforcement or
|
||||||
|
JIT optimization, those tools are not yet as mature.)
|
||||||
|
|
||||||
The proposal is strongly inspired by mypy [mypy]_. For example, the
|
The proposal is strongly inspired by mypy [mypy]_. For example, the
|
||||||
type "sequence of integers" can be written as ``Sequence[int]``. 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
|
square brackets mean that no new syntax needs to be added to the
|
||||||
language. The example here uses a custom class ``Sequence``, imported
|
language. The example here uses a custom type ``Sequence``, imported
|
||||||
from a pure-Python module ``typing``. The ``Sequence[int]``
|
from a pure-Python module ``typing``. The ``Sequence[int]`` notation
|
||||||
notation works by implementing ``__getitem__()`` in the metaclass.
|
works at runtime by implementing ``__getitem__()`` in the metaclass
|
||||||
|
(but its significance is primarily to an offline type checker).
|
||||||
|
|
||||||
The type system supports unions, generic types, and a special type
|
The type system supports unions, generic types, and a special type
|
||||||
named ``Any`` which is consistent with (i.e. assignable to and from) all
|
named ``Any`` which is consistent with (i.e. assignable to and from) all
|
||||||
|
@ -49,15 +65,16 @@ compared and contrasted are described in PEP 482.
|
||||||
Rationale and Goals
|
Rationale and Goals
|
||||||
===================
|
===================
|
||||||
|
|
||||||
PEP 3107 added support for arbitrary annotations on parts of a function
|
PEP 3107 added support for arbitrary annotations on parts of a
|
||||||
definition. Although no meaning was assigned to annotations then, there
|
function definition. Although no meaning was assigned to annotations
|
||||||
has always been an implicit goal to use them for type hinting, which is
|
then, there has always been an implicit goal to use them for type
|
||||||
listed as the first possible use case in said PEP.
|
hinting [gvr-artima]_, which is listed as the first possible use case
|
||||||
|
in said PEP.
|
||||||
|
|
||||||
This PEP aims to provide a standard syntax for type annotations, opening
|
This PEP aims to provide a standard syntax for type annotations,
|
||||||
up Python code to easier static analysis and refactoring, potential
|
opening up Python code to easier static analysis and refactoring,
|
||||||
runtime type checking, and performance optimizations utilizing type
|
potential runtime type checking, and (perhaps, in some contexts)
|
||||||
information.
|
code generation utilizing type information.
|
||||||
|
|
||||||
Of these goals, static analysis is the most important. This includes
|
Of these goals, static analysis is the most important. This includes
|
||||||
support for off-line type checkers such as mypy, as well as providing
|
support for off-line type checkers such as mypy, as well as providing
|
||||||
|
@ -74,43 +91,43 @@ implement specific runtime type checking functionality, for example
|
||||||
using decorators or metaclasses. Using type hints for performance
|
using decorators or metaclasses. Using type hints for performance
|
||||||
optimizations is left as an exercise for the reader.
|
optimizations is left as an exercise for the reader.
|
||||||
|
|
||||||
It should also be emphasized that Python will remain a dynamically
|
It should also be emphasized that **Python will remain a dynamically
|
||||||
typed language, and the authors have no desire to ever make type hints
|
typed language, and the authors have no desire to ever make type hints
|
||||||
mandatory, even by convention.
|
mandatory, even by convention.**
|
||||||
|
|
||||||
|
|
||||||
What is checked?
|
The meaning of annotations
|
||||||
================
|
==========================
|
||||||
|
|
||||||
Any function (or method -- for brevity we won't be repeating this)
|
Any function without annotations should be treated as having the most
|
||||||
with at least one argument or return annotation is checked, unless
|
general type possible, or ignored, by any type checker. Functions
|
||||||
type checking is disabled by the ``@no_type_check`` decorator or a
|
with the ``@no_type_check`` decorator or with a ``# type: ignore``
|
||||||
``# type: ignore`` comment (see below).
|
comment should be treated as having no annotations.
|
||||||
|
|
||||||
A checked function should have annotations for all its arguments and
|
It is recommended but not required that checked functions have
|
||||||
its return type, with the exception that the ``self`` argument of a
|
annotations for all arguments and the return type. For a checked
|
||||||
method should not be annotated; it is assumed to have the type of the
|
function, the default annotation for arguments and for the return type
|
||||||
containing class. Notably, the return type of ``__init__`` should be
|
is ``Any``. An exception is that the first argument of instance and
|
||||||
annotated with ``-> None``.
|
class methods does not need to be annotated; it is assumed to have the
|
||||||
|
type of the containing class for instance methods, and ``type`` for
|
||||||
|
class methods.
|
||||||
|
|
||||||
The body of a checked function is checked for consistency with the
|
(Note that the return type of ``__init__`` ought to be annotated with
|
||||||
given annotations. The annotations are also used to check correctness
|
``-> None``. The reason for this is subtle. If ``__init__`` assumed
|
||||||
of calls appearing in other checked functions.
|
a return annotation of ``-> None``, would that mean that an
|
||||||
|
argument-less, un-annotated ``__init__`` method should still be
|
||||||
|
type-checked? Rather than leaving this ambiguous or introducing an
|
||||||
|
exception to the exception, we simply say that ``__init__`` ought to
|
||||||
|
have a return annotation; the default behavior is thus the same as for
|
||||||
|
other methods.)
|
||||||
|
|
||||||
Functions without any annotations (or whose checking is disabled) are
|
A type checker is expected to check the body of a checked function for
|
||||||
assumed to have type ``Any`` when they are referenced in checked
|
consistency with the given annotations. The annotations may also used
|
||||||
functions, and this should completely silence complaints from the
|
to check correctness of calls appearing in other checked functions.
|
||||||
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
|
Type checkers are expected to attempt to infer as much information as
|
||||||
annotations on decorator definitions. In particular, a type checker
|
necessary. The minimum requirement is to handle the builtin
|
||||||
should understand the built-in decorators ``@property``,
|
decorators ``@property``, ``@staticmethod`` and ``@classmethod``.
|
||||||
``@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
|
Type Definition Syntax
|
||||||
|
@ -137,17 +154,20 @@ Type hints may be built-in classes (including those defined in
|
||||||
standard library or third-party extension modules), abstract base
|
standard library or third-party extension modules), abstract base
|
||||||
classes, types available in the ``types`` module, and user-defined
|
classes, types available in the ``types`` module, and user-defined
|
||||||
classes (including those defined in the standard library or
|
classes (including those defined in the standard library or
|
||||||
third-party modules). Annotations for built-in classes (and other
|
third-party modules).
|
||||||
classes at the discretion of the developer) may be placed in stub
|
|
||||||
files (see below).
|
While annotations are normally the best format for type hints,
|
||||||
|
there are times when it is more appropriate to represent them
|
||||||
|
by a special comment, or in a separately distributed interface
|
||||||
|
file. (See below for examples.)
|
||||||
|
|
||||||
Annotations must be valid expressions that evaluate without raising
|
Annotations must be valid expressions that evaluate without raising
|
||||||
exceptions at the time the function is defined (but see below for
|
exceptions at the time the function is defined (but see below for
|
||||||
forward references).
|
forward references).
|
||||||
|
|
||||||
The needs of static analysis require that annotations must be simple
|
Annotations should be kept simple or static analysis tools may not be
|
||||||
enough to be interpreted by static analysis tools. In particular,
|
able to interpret the values. For example, dynamically computed types
|
||||||
dynamically computed types are not acceptable. (This is an
|
are unlikely to be understood. (This is an
|
||||||
intentionally somewhat vague requirement, specific inclusions and
|
intentionally somewhat vague requirement, specific inclusions and
|
||||||
exclusions may be added to future versions of this PEP as warranted by
|
exclusions may be added to future versions of this PEP as warranted by
|
||||||
the discussion.)
|
the discussion.)
|
||||||
|
@ -158,7 +178,7 @@ below may be used: ``None``, ``Any``, ``Union``, ``Tuple``,
|
||||||
from ``typing`` (e.g. ``Sequence`` and ``Dict``), type variables, and
|
from ``typing`` (e.g. ``Sequence`` and ``Dict``), type variables, and
|
||||||
type aliases.
|
type aliases.
|
||||||
|
|
||||||
All newly introducedw names used to support features described in
|
All newly introduced names used to support features described in
|
||||||
following sections (such as ``Any`` and ``Union``) are available in
|
following sections (such as ``Any`` and ``Union``) are available in
|
||||||
the ``typing`` module.
|
the ``typing`` module.
|
||||||
|
|
||||||
|
@ -280,7 +300,10 @@ The function ``concat`` can be called with either two ``str`` arguments
|
||||||
or two ``bytes`` arguments, but not with a mix of ``str`` and ``bytes``
|
or two ``bytes`` arguments, but not with a mix of ``str`` and ``bytes``
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
Note that subtypes of types constrained by a type variable are treated
|
There should be at least two constraints, if any; specifying a single
|
||||||
|
constraint is disallowed.
|
||||||
|
|
||||||
|
Subtypes of types constrained by a type variable should be treated
|
||||||
as their respective explicitly listed base types in the context of the
|
as their respective explicitly listed base types in the context of the
|
||||||
type variable. Consider this example::
|
type variable. Consider this example::
|
||||||
|
|
||||||
|
@ -374,6 +397,35 @@ You can use multiple inheritance with ``Generic``::
|
||||||
class LinkedList(Sized, Generic[T]):
|
class LinkedList(Sized, Generic[T]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
Subclassing a generic class without specifying type parameters assumes
|
||||||
|
``Any`` for each position. In the following example, ``MyIterable``
|
||||||
|
is not generic but implicitly inherits from ``Iterable[Any]``:
|
||||||
|
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
class MyIterable(Iterable): # Same as Iterable[Any]
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Instantiating generic classes and type erasure
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
Generic types like ``List`` or ``Sequence`` cannot be instantiated.
|
||||||
|
However, user-defined classes derived from them can be instantiated.
|
||||||
|
Given a generic class ``Node[T]`` there are three forms of
|
||||||
|
instantiation:
|
||||||
|
|
||||||
|
* ``x = Node()`` -- the type of x is ``Node[Any]``.
|
||||||
|
|
||||||
|
* ``x = Node[T]()`` -- the type of x is ``Node[T]``.
|
||||||
|
|
||||||
|
* ``x = Node[int]()`` -- the type of x is ``Node[int]``.
|
||||||
|
|
||||||
|
At runtime the type is not preserved, and the observable type of x is
|
||||||
|
just ``Node``. This is type erasure and common practice in languages
|
||||||
|
with generics (e.g. Java, Typescript).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Arbitrary generic types as base classes
|
Arbitrary generic types as base classes
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
@ -421,6 +473,17 @@ 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
|
multiple times in the base class list, as long as we don't use the
|
||||||
same type variable ``T`` multiple times within ``Generic[...]``.
|
same type variable ``T`` multiple times within ``Generic[...]``.
|
||||||
|
|
||||||
|
Also consider the following example::
|
||||||
|
|
||||||
|
from typing import TypeVar, Mapping
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
class MyDict(Mapping[str, T]):
|
||||||
|
...
|
||||||
|
|
||||||
|
In this case MyDict has a single parameter, T.
|
||||||
|
|
||||||
|
|
||||||
Abstract generic types
|
Abstract generic types
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -431,6 +494,164 @@ or properties, and generic classes can also have ABCs as base
|
||||||
classes without a metaclass conflict.
|
classes without a metaclass conflict.
|
||||||
|
|
||||||
|
|
||||||
|
Type variables with an upper bound
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
A type variable may specify an upper bound using ``bound=<type>``.
|
||||||
|
This means that an actual type substituted (explicitly or implictly)
|
||||||
|
for the type variable must be a subclass of the boundary type. A
|
||||||
|
common example is the definition of a Comparable type that works well
|
||||||
|
enough to catch the most common errors::
|
||||||
|
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
class Comparable(metaclass=ABCMeta):
|
||||||
|
@abstractmethod
|
||||||
|
def __lt__(self, other: Any) -> bool: ...
|
||||||
|
... # __gt__ etc. as well
|
||||||
|
|
||||||
|
CT = TypeVar('CT', bound=Comparable)
|
||||||
|
|
||||||
|
def min(x: CT, y: CT) -> CT:
|
||||||
|
if x < y:
|
||||||
|
return x
|
||||||
|
else:
|
||||||
|
return y
|
||||||
|
|
||||||
|
min(1, 2) # ok, return type int
|
||||||
|
min('x', 'y') # ok, return type str
|
||||||
|
|
||||||
|
(Note that this is not ideal -- for example ``min('x', 1)`` is invalid
|
||||||
|
at runtime but a type checker would simply infer the return type
|
||||||
|
``Comparable``. Unfortunately, addressing this would require
|
||||||
|
introducing a much more powerful and also much more complicated
|
||||||
|
concept, F-bounded polymorphism. We may revisit this in the future.)
|
||||||
|
|
||||||
|
An upper bound cannot be combined with type constraints (as in used
|
||||||
|
``AnyStr``, see the example earlier); type constraints cause the
|
||||||
|
inferred type to be _exactly_ one of the constraint types, while an
|
||||||
|
upper bound just requires that the actual type is a subclass of the
|
||||||
|
boundary type.
|
||||||
|
|
||||||
|
|
||||||
|
Covariance and contravariance
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Consider a class ``Employee`` with a subclass ``Manager``. Now
|
||||||
|
suppose we have a function with an argument annotated with
|
||||||
|
``List[Employee]``. Should we be allowed to call this function with a
|
||||||
|
variable of type ``List[Manager]`` as its argument? Many people would
|
||||||
|
answer "yes, of course" without even considering the consequences.
|
||||||
|
But unless we know more about the function, a type checker should
|
||||||
|
reject such a call: the function might append an ``Employee`` instance
|
||||||
|
to the list, which would violate the variable's type in the caller.
|
||||||
|
|
||||||
|
It turns out such an argument acts _contravariantly_, whereas the
|
||||||
|
intuitive answer (which is correct in case the function doesn't mutate
|
||||||
|
its argument!) requires the argument to act _covariantly_. A longer
|
||||||
|
introduction to these concepts can be found on Wikipedia
|
||||||
|
[wiki-variance]_; here we just show how to control a type checker's
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
By default type variables are considered _invariant_, which means that
|
||||||
|
arguments for arguments annotated with types like ``List[Employee]``
|
||||||
|
must exactly match the type annotation -- no subclasses or
|
||||||
|
superclasses of the type parameter (in this example ``Employee``) are
|
||||||
|
allowed.
|
||||||
|
|
||||||
|
To facilitate the declaration of container types where covariant type
|
||||||
|
checking is acceptable, a type variable can be declared using
|
||||||
|
``covariant=True``. For the (rare) case where contravariant behavior
|
||||||
|
is desirable, pass ``contravariant=True``. At most one of these may
|
||||||
|
be passed.
|
||||||
|
|
||||||
|
A typical example involves defining an immutable container class::
|
||||||
|
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
T = TypeVar('T', covariant=True)
|
||||||
|
|
||||||
|
class ImmutableList(Generic[T]):
|
||||||
|
def append(self, T): ...
|
||||||
|
...
|
||||||
|
|
||||||
|
class Employee: ...
|
||||||
|
|
||||||
|
class Manager(Employee): ...
|
||||||
|
|
||||||
|
def dump_employees(emps: ImmutableList[Employee]) -> None: ...
|
||||||
|
|
||||||
|
mgrs = ... # type: ImmutableList[Mananger]
|
||||||
|
mgrs.append(Manager())
|
||||||
|
|
||||||
|
dump_employees(mgrs) # OK
|
||||||
|
|
||||||
|
The immutable collection classes in ``typing`` are all defined using a
|
||||||
|
covariant type variable (e.g. ``Mapping`` and ``Sequence``). The
|
||||||
|
mutable collection classes (e.g. ``MutableMapping`` and
|
||||||
|
``MutableSequence``) are defined using regular invariant type
|
||||||
|
variables. The one example of a contravariant type variable is the
|
||||||
|
``Generator`` type, which is contravariant in the ``send()`` argument
|
||||||
|
type (see below).
|
||||||
|
|
||||||
|
Note: variance affects type parameters for generic types -- it does
|
||||||
|
not affect regular parameters. For example, the following example is
|
||||||
|
fine::
|
||||||
|
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
class Employee: ...
|
||||||
|
|
||||||
|
class Manager(Employee): ...
|
||||||
|
|
||||||
|
E = TypeVar('E', bound=Employee) # Invariant
|
||||||
|
|
||||||
|
def dump_employee(e: E) -> None: ...
|
||||||
|
|
||||||
|
dump_employee(Manager()) # OK
|
||||||
|
|
||||||
|
|
||||||
|
The numeric tower
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
PEP 3141 defines Python's numeric tower, and the stdlib module
|
||||||
|
``numbers`` implements the corresponding ABCs (``Number``,
|
||||||
|
``Complex``, ``Real``, ``Rational`` and ``Integral``). There are some
|
||||||
|
issues with these ABCs, but the built-in concrete numeric classes
|
||||||
|
``complex``, ``float`` and ``int`` are ubiquitous (especially the
|
||||||
|
latter two :-).
|
||||||
|
|
||||||
|
Rather than requiring that users write ``import numbers`` and then use
|
||||||
|
``numbers.Float`` etc., this PEP proposes a straightforward shortcut
|
||||||
|
that is almost as effective: when an argument is annotated as having
|
||||||
|
type ``float``, an argument of type ``int`` is acceptable; similar,
|
||||||
|
for an argument annotated as having type ``complex``, arguments of
|
||||||
|
type ``float`` or ``int`` are acceptable. This does not handle
|
||||||
|
classes implementing the corresponding ABCs or the
|
||||||
|
``fractions.Fraction`` class, but we believe those use cases are
|
||||||
|
exceedingly rare.
|
||||||
|
|
||||||
|
|
||||||
|
The bytes types
|
||||||
|
---------------
|
||||||
|
|
||||||
|
There are three different builtin classes used for arrays of bytes
|
||||||
|
(not counting the classes available in the ``array`` module):
|
||||||
|
``bytes``, ``bytearray`` and ``memoryview``. Of these, ``bytes`` and
|
||||||
|
``bytearray`` have many behaviors in common (though not all --
|
||||||
|
``bytearray`` is mutable).
|
||||||
|
|
||||||
|
While there is an ABC ``ByteString`` defined in ``collections.abc``
|
||||||
|
and a corresponding type in ``typing``, functions accepting bytes (of
|
||||||
|
some form) are so common that it would be cumbersome to have to write
|
||||||
|
``typing.ByteString`` everywhere. So, as a shortcut similar to that
|
||||||
|
for the builtin numeric classes, when an argument is annotated as
|
||||||
|
having type ``bytes``, arguments of type ``bytearray`` or
|
||||||
|
``memoryview`` are acceptable. (Again, there are situations where
|
||||||
|
this isn't sound, but we believe those are exceedingly rare in
|
||||||
|
practice.)
|
||||||
|
|
||||||
|
|
||||||
Forward references
|
Forward references
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -529,8 +750,8 @@ Example::
|
||||||
...
|
...
|
||||||
|
|
||||||
A type factored by ``Union[T1, T2, ...]`` responds ``True`` to
|
A type factored by ``Union[T1, T2, ...]`` responds ``True`` to
|
||||||
``issubclass`` checks for ``T1`` and any of its subclasses, ``T2`` and
|
``issubclass`` checks for ``T1`` and any of its subtypes, ``T2`` and
|
||||||
any of its subclasses, and so on.
|
any of its subtypes, and so on.
|
||||||
|
|
||||||
One common case of union types are *optional* types. By default,
|
One common case of union types are *optional* types. By default,
|
||||||
``None`` is an invalid value for any type, unless a default value of
|
``None`` is an invalid value for any type, unless a default value of
|
||||||
|
@ -557,8 +778,8 @@ This is equivalent to::
|
||||||
The ``Any`` type
|
The ``Any`` type
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
A special kind of type is ``Any``. Every class is a subclass of
|
A special kind of type is ``Any``. Every type is a subtype of
|
||||||
``Any``. This is also true for the builtin class ``object``.
|
``Any``. This is also true for the builtin type ``object``.
|
||||||
However, to the static type checker these are completely different.
|
However, to the static type checker these are completely different.
|
||||||
|
|
||||||
When the type of a value is ``object``, the type checker will reject
|
When the type of a value is ``object``, the type checker will reject
|
||||||
|
@ -596,6 +817,22 @@ and this is also the suggested default when the program is being
|
||||||
type-checked.
|
type-checked.
|
||||||
|
|
||||||
|
|
||||||
|
Default argument values
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
In stubs it may be useful to declare an argument as having a default
|
||||||
|
without specifying the actual default value. For example::
|
||||||
|
|
||||||
|
def foo(x: AnyStr, y: AnyStr = ...) -> AnyStr: ...
|
||||||
|
|
||||||
|
What should the default value look like? Any of the options ``""``,
|
||||||
|
``b""`` or ``None`` fails to satisfy the type constraint (actually,
|
||||||
|
``None`` will *modify* the type to become ``Optional[AnyStr]``).
|
||||||
|
|
||||||
|
In such cases the default value may be specified as a literal
|
||||||
|
ellipsis, i.e. the above example is literally what you would write.
|
||||||
|
|
||||||
|
|
||||||
Compatibility with other uses of function annotations
|
Compatibility with other uses of function annotations
|
||||||
=====================================================
|
=====================================================
|
||||||
|
|
||||||
|
@ -655,6 +892,28 @@ Examples of type comments on ``with`` and ``for`` statements::
|
||||||
# Here x and y are floats
|
# Here x and y are floats
|
||||||
...
|
...
|
||||||
|
|
||||||
|
In stubs it may be useful to declare the existence of a variable
|
||||||
|
without giving it an initial value. This can be done using a literal
|
||||||
|
ellipsis::
|
||||||
|
|
||||||
|
from typing import IO
|
||||||
|
|
||||||
|
stream = ... # type: IO[str]
|
||||||
|
|
||||||
|
In non-stub code, there is a similar special case:
|
||||||
|
|
||||||
|
from typing import IO
|
||||||
|
|
||||||
|
stream = None # type: IO[str]
|
||||||
|
|
||||||
|
Type checkers should not complain about this (despite the value
|
||||||
|
``None`` not matching the given type), nor should they change the
|
||||||
|
inferred type to ``Optional[...]`` (despite the rule that does this
|
||||||
|
for annotated arguments with a default value of ``None``). The
|
||||||
|
assumption here is that other code will ensure that the variable is
|
||||||
|
given a value of the proper type, and all uses can assume that the
|
||||||
|
variable has the given type.
|
||||||
|
|
||||||
The ``# type: ignore`` comment should be put on the line that the
|
The ``# type: ignore`` comment should be put on the line that the
|
||||||
error refers to::
|
error refers to::
|
||||||
|
|
||||||
|
@ -674,7 +933,7 @@ Casts
|
||||||
|
|
||||||
Occasionally the type checker may need a different kind of hint: the
|
Occasionally the type checker may need a different kind of hint: the
|
||||||
programmer may know that an expression is of a more constrained type
|
programmer may know that an expression is of a more constrained type
|
||||||
than the type checker infers. For example::
|
than a type checker may be able to infer. For example::
|
||||||
|
|
||||||
from typing import List, cast
|
from typing import List, cast
|
||||||
|
|
||||||
|
@ -683,7 +942,8 @@ than the type checker infers. For example::
|
||||||
# We only get here if there's at least one string in a
|
# We only get here if there's at least one string in a
|
||||||
return cast(str, a[index])
|
return cast(str, a[index])
|
||||||
|
|
||||||
The type checker infers the type ``object`` for ``a[index]``, but we
|
Some type checkers may not be able to infers that the type of
|
||||||
|
``a[index]`` is ``str`` and only infer ``object`` or ``Any``", but we
|
||||||
know that (if the code gets to that point) it must be a string. The
|
know that (if the code gets to that point) it must be a string. The
|
||||||
``cast(t, x)`` call tells the type checker that we are confident that
|
``cast(t, x)`` call tells the type checker that we are confident that
|
||||||
the type of ``x`` is ``t``. At runtime a cast always returns the
|
the type of ``x`` is ``t``. At runtime a cast always returns the
|
||||||
|
@ -720,8 +980,8 @@ feature of the ``typing`` module that may only be used in stub files:
|
||||||
the ``@overload`` decorator described below.
|
the ``@overload`` decorator described below.
|
||||||
|
|
||||||
The type checker should only check function signatures in stub files;
|
The type checker should only check function signatures in stub files;
|
||||||
function bodies in stub files should just be a single ``pass``
|
It is recommended that function bodies in stub files just be a single
|
||||||
statement.
|
ellipsis (``...``).
|
||||||
|
|
||||||
The type checker should have a configurable search path for stub files.
|
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
|
If a stub file is found the type checker should not read the
|
||||||
|
@ -732,6 +992,12 @@ While stub files are syntactically valid Python modules, they use the
|
||||||
same directory as the corresponding real module. This also reinforces
|
same directory as the corresponding real module. This also reinforces
|
||||||
the notion that no runtime behavior should be expected of stub files.
|
the notion that no runtime behavior should be expected of stub files.
|
||||||
|
|
||||||
|
Additional notes on stub files:
|
||||||
|
|
||||||
|
* Modules and variables imported into the stub are not considered
|
||||||
|
exported from the stub unless the import uses the ``import ... as
|
||||||
|
...`` form.
|
||||||
|
|
||||||
Function overloading
|
Function overloading
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -746,9 +1012,9 @@ follows::
|
||||||
class bytes:
|
class bytes:
|
||||||
...
|
...
|
||||||
@overload
|
@overload
|
||||||
def __getitem__(self, i: int) -> int: pass
|
def __getitem__(self, i: int) -> int: ...
|
||||||
@overload
|
@overload
|
||||||
def __getitem__(self, s: slice) -> bytes: pass
|
def __getitem__(self, s: slice) -> bytes: ...
|
||||||
|
|
||||||
This description is more precise than would be possible using unions
|
This description is more precise than would be possible using unions
|
||||||
(which cannot express the relationship between the argument and return
|
(which cannot express the relationship between the argument and return
|
||||||
|
@ -757,7 +1023,7 @@ types)::
|
||||||
from typing import Union
|
from typing import Union
|
||||||
class bytes:
|
class bytes:
|
||||||
...
|
...
|
||||||
def __getitem__(self, a: Union[int, slice]) -> Union[int, bytes]: pass
|
def __getitem__(self, a: Union[int, slice]) -> Union[int, bytes]: ...
|
||||||
|
|
||||||
Another example where ``@overload`` comes in handy is the type of the
|
Another example where ``@overload`` comes in handy is the type of the
|
||||||
builtin ``map()`` function, which takes a different number of
|
builtin ``map()`` function, which takes a different number of
|
||||||
|
@ -770,20 +1036,20 @@ arguments depending on the type of the callable::
|
||||||
S = TypeVar('S')
|
S = TypeVar('S')
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def map(func: Callable[[T1], S], iter1: Iterable[T1]) -> Iterator[S]: pass
|
def map(func: Callable[[T1], S], iter1: Iterable[T1]) -> Iterator[S]: ...
|
||||||
@overload
|
@overload
|
||||||
def map(func: Callable[[T1, T2], S],
|
def map(func: Callable[[T1, T2], S],
|
||||||
iter1: Iterable[T1], iter2: Iterable[T2]) -> Iterator[S]: pass
|
iter1: Iterable[T1], iter2: Iterable[T2]) -> Iterator[S]: ...
|
||||||
# ... and we could add more items to support more than two iterables
|
# ... and we could add more items to support more than two iterables
|
||||||
|
|
||||||
Note that we could also easily add items to support ``map(None, ...)``::
|
Note that we could also easily add items to support ``map(None, ...)``::
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def map(func: None, iter1: Iterable[T1]) -> Iterable[T1]: pass
|
def map(func: None, iter1: Iterable[T1]) -> Iterable[T1]: ...
|
||||||
@overload
|
@overload
|
||||||
def map(func: None,
|
def map(func: None,
|
||||||
iter1: Iterable[T1],
|
iter1: Iterable[T1],
|
||||||
iter2: Iterable[T2]) -> Iterable[Tuple[T1, T2]]: pass
|
iter2: Iterable[T2]) -> Iterable[Tuple[T1, T2]]: ...
|
||||||
|
|
||||||
The ``@overload`` decorator may only be used in stub files. While it
|
The ``@overload`` decorator may only be used in stub files. While it
|
||||||
would be possible to provide a multiple dispatch implementation using
|
would be possible to provide a multiple dispatch implementation using
|
||||||
|
@ -795,7 +1061,8 @@ is why previous attempts were abandoned in favor of
|
||||||
"Alternative approaches".) In the future we may come up with a
|
"Alternative approaches".) In the future we may come up with a
|
||||||
satisfactory multiple dispatch design, but we don't want such a design
|
satisfactory multiple dispatch design, but we don't want such a design
|
||||||
to be constrained by the overloading syntax defined for type hints in
|
to be constrained by the overloading syntax defined for type hints in
|
||||||
stub files.
|
stub files. In the meantime, using the ``@overload`` decorator or
|
||||||
|
calling ``overload()`` directly raises ``RuntimeError``.
|
||||||
|
|
||||||
Storing and distributing stub files
|
Storing and distributing stub files
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
@ -827,8 +1094,8 @@ Note that if the user decides to use the "latest" available source
|
||||||
package, using the "latest" stub files should generally also work if
|
package, using the "latest" stub files should generally also work if
|
||||||
they're updated often.
|
they're updated often.
|
||||||
|
|
||||||
Third-party stub packages can use any location for stub storage. The
|
Third-party stub packages can use any location for stub storage. Type
|
||||||
type checker will search for them using PYTHONPATH. A default fallback
|
checkers should search for them using PYTHONPATH. A default fallback
|
||||||
directory that is always checked is ``shared/typehints/python3.5/`` (or
|
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
|
3.6, etc.). Since there can only be one package installed for a given
|
||||||
Python version per environment, no additional versioning is performed
|
Python version per environment, no additional versioning is performed
|
||||||
|
@ -845,6 +1112,16 @@ snippet in ``setup.py``::
|
||||||
],
|
],
|
||||||
...
|
...
|
||||||
|
|
||||||
|
The Typeshed Repo
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
There is a shared repository where useful stubs are being collected
|
||||||
|
[typeshed]_. Note that stubs for a given package will not be included
|
||||||
|
here without the explicit consent of the package owner. Further
|
||||||
|
policies regarding the stubs collected here will be decided at a later
|
||||||
|
time, after discussion on python-dev, and reported in the typeshed
|
||||||
|
repo's README.
|
||||||
|
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
==========
|
==========
|
||||||
|
@ -860,8 +1137,32 @@ The ``typing`` Module
|
||||||
|
|
||||||
To open the usage of static type checking to Python 3.5 as well as older
|
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
|
versions, a uniform namespace is required. For this purpose, a new
|
||||||
module in the standard library is introduced called ``typing``. It
|
module in the standard library is introduced called ``typing``.
|
||||||
holds a set of classes representing builtin types with generics, namely:
|
|
||||||
|
It defines the fundamental building blocks for constructing types
|
||||||
|
(e.g. ``Any``), types representing generic variants of builtin
|
||||||
|
collections (e.g. ``List``), types representing generic
|
||||||
|
collection ABCs (e.g. ``Sequence``), and a small collection of
|
||||||
|
convenience definitions.
|
||||||
|
|
||||||
|
Fundamental building blocks:
|
||||||
|
|
||||||
|
* Any, used as ``def get(key: str) -> Any: ...``
|
||||||
|
|
||||||
|
* Union, used as ``Union[Type1, Type2, Type3]``
|
||||||
|
|
||||||
|
* Callable, used as ``Callable[[Arg1Type, Arg2Type], ReturnType]``
|
||||||
|
|
||||||
|
* Tuple, used by listing the element types, for example
|
||||||
|
``Tuple[int, int, str]``.
|
||||||
|
Arbitrary-length homogeneous tuples can be expressed
|
||||||
|
using one type and ellipsis, for example ``Tuple[int, ...]``.
|
||||||
|
(The ``...`` here are part of the syntax, a literal ellipsis.)
|
||||||
|
|
||||||
|
* TypeVar, used as ``X = TypeVar('X', Type1, Type2, Type3)`` or simply
|
||||||
|
``Y = TypeVar('Y')`` (see above for more details)
|
||||||
|
|
||||||
|
Generic variants of builtin collections:
|
||||||
|
|
||||||
* Dict, used as ``Dict[key_type, value_type]``
|
* Dict, used as ``Dict[key_type, value_type]``
|
||||||
|
|
||||||
|
@ -872,53 +1173,28 @@ holds a set of classes representing builtin types with generics, namely:
|
||||||
|
|
||||||
* FrozenSet, used as ``FrozenSet[element_type]``
|
* FrozenSet, used as ``FrozenSet[element_type]``
|
||||||
|
|
||||||
* Tuple, used by listing the element types, for example
|
Note: ``Dict``, ``List``, ``Set`` and ``FrozenSet`` are mainly useful
|
||||||
``Tuple[int, int, str]``.
|
for annotating return values. For arguments, prefer the abstract
|
||||||
Arbitrary-length homogeneous tuples can be expressed
|
collection types defined below, e.g. ``Mapping``, ``Sequence`` or
|
||||||
using one type and ellipsis, for example ``Tuple[int, ...]``.
|
``AbstractSet``.
|
||||||
(The ``...`` here are part of the syntax.)
|
|
||||||
|
|
||||||
* NamedTuple, used as
|
Generic variants of container ABCs (and a few non-containers):
|
||||||
``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:
|
|
||||||
|
|
||||||
* Any, used as ``def get(key: str) -> Any: ...``
|
|
||||||
|
|
||||||
* Union, used as ``Union[Type1, Type2, Type3]``
|
|
||||||
|
|
||||||
* TypeVar, used as ``X = TypeVar('X', Type1, Type2, Type3)`` or simply
|
|
||||||
``Y = TypeVar('Y')``
|
|
||||||
|
|
||||||
* Callable, used as ``Callable[[Arg1Type, Arg2Type], ReturnType]``
|
|
||||||
|
|
||||||
* AnyStr, defined as ``TypeVar('AnyStr', str, bytes)``
|
|
||||||
|
|
||||||
All abstract base classes available in ``collections.abc`` are
|
|
||||||
importable from the ``typing`` module, with added generics support:
|
|
||||||
|
|
||||||
* ByteString
|
* ByteString
|
||||||
|
|
||||||
* Callable (see above)
|
* Callable (see above, listed here for completeness)
|
||||||
|
|
||||||
* Container
|
* Container
|
||||||
|
|
||||||
|
* Generator, used as ``Generator[yield_type, send_type,
|
||||||
|
return_type]``. This represents the return value of generator
|
||||||
|
functions. It is a subtype of ``Iterable`` and it has additional
|
||||||
|
type variables for the type accepted by the ``send()`` method (which
|
||||||
|
is contravariant -- a generator that accepts sending it ``Employee``
|
||||||
|
instance is valid in a context where a generator is required that
|
||||||
|
accepts sending it ``Manager`` instances) and the return type of the
|
||||||
|
generator.
|
||||||
|
|
||||||
* Hashable (not generic, but present for completeness)
|
* Hashable (not generic, but present for completeness)
|
||||||
|
|
||||||
* ItemsView
|
* ItemsView
|
||||||
|
@ -956,13 +1232,17 @@ A few one-off types are defined that test for single special methods
|
||||||
|
|
||||||
* SupportsAbs, to test for ``__abs__``
|
* SupportsAbs, to test for ``__abs__``
|
||||||
|
|
||||||
|
* SupportsComplex, to test for ``__complex__``
|
||||||
|
|
||||||
* SupportsFloat, to test for ``__float__``
|
* SupportsFloat, to test for ``__float__``
|
||||||
|
|
||||||
* SupportsInt, to test for ``__int__``
|
* SupportsInt, to test for ``__int__``
|
||||||
|
|
||||||
* SupportsRound, to test for ``__round__``
|
* SupportsRound, to test for ``__round__``
|
||||||
|
|
||||||
The library includes literals for platform-specific type hinting:
|
* SupportsBytes, to test for ``__bytes__``
|
||||||
|
|
||||||
|
Constants for platform-specific type hinting:
|
||||||
|
|
||||||
* PY2
|
* PY2
|
||||||
|
|
||||||
|
@ -972,41 +1252,46 @@ The library includes literals for platform-specific type hinting:
|
||||||
|
|
||||||
* POSIX, equivalent to ``not WINDOWS``
|
* POSIX, equivalent to ``not WINDOWS``
|
||||||
|
|
||||||
The following conveniece functions and decorators are exported:
|
Convenience definitions:
|
||||||
|
|
||||||
* cast, described earlier
|
* AnyStr, defined as ``TypeVar('AnyStr', str, bytes)``
|
||||||
|
|
||||||
* no_type_check, a decorator to disable type checking per class or
|
* NamedTuple, used as
|
||||||
|
``NamedTuple(type_name, [(field_name, field_type), ...])``
|
||||||
|
and equivalent to
|
||||||
|
``collections.namedtuple(type_name, [field_name, ...])``.
|
||||||
|
This is useful to declare the types of the fields of a a named tuple
|
||||||
|
type.
|
||||||
|
|
||||||
|
* cast(), described earlier
|
||||||
|
|
||||||
|
* @no_type_check, a decorator to disable type checking per class or
|
||||||
function (see below)
|
function (see below)
|
||||||
|
|
||||||
* no_type_check_decorator, a decorator to create your own decorators
|
* @no_type_check_decorator, a decorator to create your own decorators
|
||||||
with the same meaning as ``@no_type_check`` (see below)
|
with the same meaning as ``@no_type_check`` (see below)
|
||||||
|
|
||||||
* overload, described earlier
|
* @overload, described earlier
|
||||||
|
|
||||||
* get_type_hints, a utility function to retrieve the type hints from a
|
* get_type_hints(), a utility function to retrieve the type hints from a
|
||||||
function or method. Given a function or method object, it returns
|
function or method. Given a function or method object, it returns
|
||||||
a dict with the same format as ``__annotations__``, but evaluating
|
a dict with the same format as ``__annotations__``, but evaluating
|
||||||
forward references (which are given as string literals) as expressions
|
forward references (which are given as string literals) as expressions
|
||||||
in the context of the original function or method definition.
|
in the context of the original function or method definition.
|
||||||
|
|
||||||
The following types are available in the ``typing.io`` module:
|
Types available in the ``typing.io`` submodule:
|
||||||
|
|
||||||
* IO (generic over ``AnyStr``)
|
* IO (generic over ``AnyStr``)
|
||||||
|
|
||||||
* BinaryIO (a simple subclass of ``IO[bytes]``)
|
* BinaryIO (a simple subtype of ``IO[bytes]``)
|
||||||
|
|
||||||
* TextIO (a simple subclass of ``IO[str]``)
|
* TextIO (a simple subtype of ``IO[str]``)
|
||||||
|
|
||||||
The following types are provided by the ``typing.re`` module:
|
Types available in the ``typing.re`` submodule:
|
||||||
|
|
||||||
* Match and Pattern, types of ``re.match()`` and ``re.compile()``
|
* Match and Pattern, types of ``re.match()`` and ``re.compile()``
|
||||||
results (generic over ``AnyStr``)
|
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.").
|
|
||||||
|
|
||||||
|
|
||||||
Rejected Alternatives
|
Rejected Alternatives
|
||||||
=====================
|
=====================
|
||||||
|
@ -1025,7 +1310,7 @@ Most people are familiar with the use of angular brackets
|
||||||
express the parametrization of generic types. The problem with these
|
express the parametrization of generic types. The problem with these
|
||||||
is that they are really hard to parse, especially for a simple-minded
|
is that they are really hard to parse, especially for a simple-minded
|
||||||
parser like Python. In most languages the ambiguities are usually
|
parser like Python. In most languages the ambiguities are usually
|
||||||
dealy with by only allowing angular brackets in specific syntactic
|
dealt with by only allowing angular brackets in specific syntactic
|
||||||
positions, where general expressions aren't allowed. (And also by
|
positions, where general expressions aren't allowed. (And also by
|
||||||
using very powerful parsing techniques that can backtrack over an
|
using very powerful parsing techniques that can backtrack over an
|
||||||
arbitrary section of code.)
|
arbitrary section of code.)
|
||||||
|
@ -1187,7 +1472,7 @@ evaluation. There are several things wrong with this idea, however.
|
||||||
well known from other programming languages. But this use of ``::``
|
well known from other programming languages. But this use of ``::``
|
||||||
is unheard of in English, and in other languages (e.g. C++) it is
|
is unheard of in English, and in other languages (e.g. C++) it is
|
||||||
used as a scoping operator, which is a very different beast. In
|
used as a scoping operator, which is a very different beast. In
|
||||||
contrast, the single colon for type hints reads natural -- and no
|
contrast, the single colon for type hints reads naturally -- and no
|
||||||
wonder, since it was carefully designed for this purpose (the idea
|
wonder, since it was carefully designed for this purpose (the idea
|
||||||
long predates PEP 3107 [gvr-artima]_). It is also used in the same
|
long predates PEP 3107 [gvr-artima]_). It is also used in the same
|
||||||
fashion in other languages from Pascal to Swift.
|
fashion in other languages from Pascal to Swift.
|
||||||
|
@ -1286,15 +1571,21 @@ References
|
||||||
.. [mypy]
|
.. [mypy]
|
||||||
http://mypy-lang.org
|
http://mypy-lang.org
|
||||||
|
|
||||||
|
.. [gvr-artima]
|
||||||
|
http://www.artima.com/weblogs/viewpost.jsp?thread=85551
|
||||||
|
|
||||||
|
.. [wiki-variance]
|
||||||
|
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
|
||||||
|
|
||||||
|
.. [typeshed]
|
||||||
|
https://github.com/JukkaL/typeshed/
|
||||||
|
|
||||||
.. [pyflakes]
|
.. [pyflakes]
|
||||||
https://github.com/pyflakes/pyflakes/
|
https://github.com/pyflakes/pyflakes/
|
||||||
|
|
||||||
.. [pylint]
|
.. [pylint]
|
||||||
http://www.pylint.org
|
http://www.pylint.org
|
||||||
|
|
||||||
.. [gvr-artima]
|
|
||||||
http://www.artima.com/weblogs/viewpost.jsp?thread=85551
|
|
||||||
|
|
||||||
.. [roberge]
|
.. [roberge]
|
||||||
http://aroberge.blogspot.com/2015/01/type-hinting-in-python-focus-on.html
|
http://aroberge.blogspot.com/2015/01/type-hinting-in-python-focus-on.html
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue