blob: ffd6c9b86e14d8298cf3f1fb8cabb4ed7953e14f [file] [log] [blame]
.. _stubs:
**********
Type Stubs
**********
Introduction
============
*type stubs*, also called *stub files*, provide type information for untyped
Python packages and modules. Type stubs serve multiple purposes:
* They are the only way to add type information to extension modules.
* They can provide type information for packages that do not wish to
add them inline.
* They can be distributed separately from the implementation.
This allows stubs to be developed at a different pace or by different
authors, which is especially useful when adding type annotations to
existing packages.
* They can act as documentation, succinctly explaining the external
API of a package, without including the implementation or private
members.
This document aims to give guidance to both authors of type stubs and developers
of type checkers and other tools. It describes the constructs that can be used safely in type stubs,
suggests a style guide for them, and lists constructs that type
checkers are expected to support.
Type stubs that only use constructs described in this document should work with
all type checkers that also follow this document.
Type stub authors can elect to use additional constructs, but
must be prepared that some type checkers will not parse them as expected.
A type checker that conforms to this document will parse a type stub that only uses
constructs described here without error and will not interpret any
construct in a contradictory manner. However, type checkers are not
required to implement checks for all these constructs, and
can elect to ignore unsupported ones. Additionally type checkers
can support constructs not described in this document and tool authors are
encouraged to experiment with additional features.
Syntax
======
Type stubs are syntactically valid Python 3.7 files with a ``.pyi`` suffix.
The Python syntax used for type stubs is independent from the Python
versions supported by the implementation, and from the Python version the type
checker runs under (if any). Therefore, type stub authors should use the
latest available syntax features in stubs (up to Python 3.7), even if the
implementation supports older, pre-3.7 Python versions.
Type checker authors are encouraged to support syntax features from
post-3.7 Python versions, although type stub authors should not use such
features if they wish to maintain compatibility with all type checkers.
For example, Python 3.7 added the ``async`` keyword (see PEP 492 [#pep492]_).
Stub authors should use it to mark coroutines, even if the implementation
still uses the ``@coroutine`` decorator. On the other hand, type stubs should
not use the positional-only syntax from PEP 570 [#pep570]_, introduced in
Python 3.8, although type checker authors are encouraged to support it.
Stubs are treated as if ``from __future__ import annotations`` is enabled.
In particular, built-in generics, pipe union syntax (``X | Y``), and forward
references can be used.
Starting with Python 3.8, the :py:mod:`ast` module from the standard library supports
all syntax features required by this PEP. Older Python versions can use the
`typed_ast <https://pypi.org/project/typed-ast/>`_ package from the
Python Package Index, which also supports Python 3.7 syntax and ``# type``
comments.
Distribution
============
Type stubs can be distributed with or separately from the implementation;
see PEP 561 [#pep561]_ for more information. The
`typeshed <https://github.com/python/typeshed>`_ project
includes stubs for Python's standard library and several third-party
packages. The stubs for the standard library are usually distributed with type checkers and do not
require separate installation. Stubs for third-party libraries are
available on the `Python Package Index <https://pypi.org>`_. A stub package for
a library called ``widget`` will be called ``types-widget``.
Supported Constructs
====================
This sections lists constructs that type checkers will accept in type stubs.
Type stub authors can safely use these constructs. If a
construct is marked as "unspecified", type checkers may handle it
as they best see fit or report an error. Linters should usually
flag those constructs. Type stub authors should avoid using them to
ensure compatibility across type checkers.
Unless otherwise mentioned, type stubs support all features from the
``typing`` module of the latest released Python version. If a stub uses
typing features from a later Python version than what the implementation
supports, these features can be imported from ``typing_extensions`` instead
of ``typing``.
For example, a stub could use ``Literal``, introduced in Python 3.8,
for a library supporting Python 3.7+::
from typing_extensions import Literal
def foo(x: Literal[""]) -> int: ...
Comments
--------
Standard Python comments are accepted everywhere Python syntax allows them.
Two kinds of structured comments are accepted:
* A ``# type: X`` comment at the end of a line that defines a variable,
declaring that the variable has type ``X``. However, PEP 526-style [#pep526]_
variable annotations are preferred over type comments.
* A ``# type: ignore`` comment at the end of any line, which suppresses all type
errors in that line. The type checker mypy supports suppressing certain
type errors by using ``# type: ignore[error-type]``. This is not supported
by other type checkers and should not be used in stubs.
Imports
-------
Type stubs distinguish between imports that are re-exported and those
that are only used internally. Imports are re-exported if they use one of these
forms:[#pep484]_
* ``import X as X``
* ``from Y import X as X``
* ``from Y import *``
Here are some examples of imports that make names available for internal use in
a stub but do not re-export them::
import X
from Y import X
from Y import X as OtherX
Type aliases can be used to re-export an import under a different name::
from foo import bar as _bar
new_bar = _bar # "bar" gets re-exported with the name "new_bar"
Sub-modules are always exported when they are imported in a module.
For example, consider the following file structure::
foo/
__init__.pyi
bar.pyi
Then ``foo`` will export ``bar`` when one of the following constructs is used in
``__init__.pyi``::
from . import bar
from .bar import Bar
Stubs support customizing star import semantics by defining a module-level
variable called ``__all__``. In stubs, this must be a string list literal.
Other types are not supported. Neither is the dynamic creation of this
variable (for example by concatenation).
By default, ``from foo import *`` imports all names in ``foo`` that
do not begin with an underscore. When ``__all__`` is defined, only those names
specified in ``__all__`` are imported::
__all__ = ['public_attr', '_private_looking_public_attr']
public_attr: int
_private_looking_public_attr: int
private_attr: int
Type checkers support cyclic imports in stub files.
Built-in Generics
-----------------
PEP 585 [#pep585]_ built-in generics are supported and should be used instead
of the corresponding types from ``typing``::
from collections import defaultdict
def foo(t: type[MyClass]) -> list[int]: ...
x: defaultdict[int]
Using imports from ``collections.abc`` instead of ``typing`` is
generally possible and recommended::
from collections.abc import Iterable
def foo(iter: Iterable[int]) -> None: ...
Unions
------
Declaring unions with ``Union`` and ``Optional`` is supported by all
type checkers. With a few exceptions [#ts-4819]_, the shorthand syntax
is also supported::
def foo(x: int | str) -> int | None: ... # recommended
def foo(x: Union[int, str]) -> Optional[int]: ... # ok
Module Level Attributes
-----------------------
Module level variables and constants can be annotated using either
type comments or variable annotation syntax::
x: int # recommended
x: int = 0
x = 0 # type: int
x = ... # type: int
The type of a variable is unspecified when the variable is unannotated or
when the annotation
and the assigned value disagree. As an exception, the ellipsis literal can
stand in for any type::
x = 0 # type is unspecified
x = ... # type is unspecified
x: int = "" # type is unspecified
x: int = ... # type is int
Classes
-------
Class definition syntax follows general Python syntax, but type checkers
are only expected to understand the following constructs in class bodies:
* The ellipsis literal ``...`` is ignored and used for empty
class bodies. Using ``pass`` in class bodies is undefined.
* Instance attributes follow the same rules as module level attributes
(see above).
* Method definitions (see below) and properties.
* Method aliases.
* Inner class definitions.
More complex statements don't need to be supported::
class Simple: ...
class Complex(Base):
read_write: int
@property
def read_only(self) -> int: ...
def do_stuff(self, y: str) -> None: ...
doStuff = do_stuff
The type of generic classes can be narrowed by annotating the ``self``
argument of the ``__init__`` method::
class Foo(Generic[_T]):
@overload
def __init__(self: Foo[str], type: Literal["s"]) -> None: ...
@overload
def __init__(self: Foo[int], type: Literal["i"]) -> None: ...
@overload
def __init__(self, type: str) -> None: ...
The class must match the class in which it is declared. Using other classes,
including sub or super classes, will not work. In addition, the ``self``
annotation cannot contain type variables.
.. _supported-functions:
Functions and Methods
---------------------
Function and method definition syntax follows general Python syntax.
Unless an argument name is prefixed with two underscores (but not suffixed
with two underscores), it can be used as a keyword argument [#pep484]_::
# x is positional-only
# y can be used positionally or as keyword argument
# z is keyword-only
def foo(__x, y, *, z): ...
PEP 570 [#pep570]_ style positional-only parameters are currently not
supported.
If an argument or return type is unannotated, per PEP 484 [#pep484]_ its
type is assumed to be ``Any``. It is preferred to leave unknown
types unannotated rather than explicitly marking them as ``Any``, as some
type checkers can optionally warn about unannotated arguments.
If an argument has a literal or constant default value, it must match the implementation
and the type of the argument (if specified) must match the default value.
Alternatively, ``...`` can be used in place of any default value::
# The following arguments all have type Any.
def unannotated(a, b=42, c=...): ...
# The following arguments all have type int.
def annotated(a: int, b: int = 42, c: int = ...): ...
# The following default values are invalid and the types are unspecified.
def invalid(a: int = "", b: Foo = Foo()): ...
For a class ``C``, the type of the first argument to a classmethod is
assumed to be ``type[C]``, if unannotated. For other non-static methods,
its type is assumed to be ``C``::
class Foo:
def do_things(self): ... # self has type Foo
@classmethod
def create_it(cls): ... # cls has type Type[Foo]
@staticmethod
def utility(x): ... # x has type Any
But::
_T = TypeVar("_T")
class Foo:
def do_things(self: _T) -> _T: ... # self has type _T
@classmethod
def create_it(cls: _T) -> _T: ... # cls has type _T
PEP 612 [#pep612]_ parameter specification variables (``ParamSpec``)
are supported in argument and return types::
_P = ParamSpec("_P")
_R = TypeVar("_R")
def foo(cb: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ...
However, ``Concatenate`` from PEP 612 is not yet supported; nor is using
a ``ParamSpec`` to parameterize a generic class.
PEP 647 [#pep647]_ type guards are supported.
Using a function or method body other than the ellipsis literal is currently
unspecified. Stub authors may experiment with other bodies, but it is up to
individual type checkers how to interpret them::
def foo(): ... # compatible
def bar(): pass # behavior undefined
All variants of overloaded functions and methods must have an ``@overload``
decorator::
@overload
def foo(x: str) -> str: ...
@overload
def foo(x: float) -> int: ...
The following (which would be used in the implementation) is wrong in
type stubs::
@overload
def foo(x: str) -> str: ...
@overload
def foo(x: float) -> int: ...
def foo(x: str | float) -> Any: ...
Aliases and NewType
-------------------
Type checkers should accept module-level type aliases, optionally using
``TypeAlias`` (PEP 613 [#pep613]_), e.g.::
_IntList = list[int]
_StrList: TypeAlias = list[str]
Type checkers should also accept regular module-level or class-level aliases,
e.g.::
def a() -> None: ...
b = a
class C:
def f(self) -> int: ...
g = f
A type alias may contain type variables. As per PEP 484 [#pep484]_,
all type variables must be substituted when the alias is used::
_K = TypeVar("_K")
_V = TypeVar("_V")
_MyMap: TypeAlias = dict[str, dict[_K, _V]]
# either concrete types or other type variables can be substituted
def f(x: _MyMap[str, _V]) -> _V: ...
# explicitly substitute in Any rather than using a bare alias
def g(x: _MyMap[Any, Any]) -> Any: ...
Otherwise, type variables in aliases follow the same rules as type variables in
generic class definitions.
``typing.NewType`` is also supported in stubs.
Decorators
----------
Type stubs may only use decorators defined in the ``typing`` module, plus a
fixed set of additional ones:
* ``classmethod``
* ``staticmethod``
* ``property`` (including ``.setter``)
* ``abc.abstractmethod``
* ``dataclasses.dataclass``
* ``asyncio.coroutine`` (although ``async`` should be used instead)
The behavior of other decorators should instead be incorporated into the types.
For example, for the following function::
import contextlib
@contextlib.contextmanager
def f():
yield 42
the stub definition should be::
from contextlib import AbstractContextManager
def f() -> AbstractContextManager[int]: ...
Version and Platform Checks
---------------------------
Type stubs for libraries that support multiple Python versions can use version
checks to supply version-specific type hints. Type stubs for different Python
versions should still conform to the most recent supported Python version's
syntax, as explain in the Syntax_ section above.
Version checks are if-statements that use ``sys.version_info`` to determine the
current Python version. Version checks should only check against the ``major`` and
``minor`` parts of ``sys.version_info``. Type checkers are only required to
support the tuple-based version check syntax::
if sys.version_info >= (3,):
# Python 3-specific type hints. This tuple-based syntax is recommended.
else:
# Python 2-specific type hints.
if sys.version_info >= (3, 5):
# Specific minor version features can be easily checked with tuples.
if sys.version_info < (3,):
# This is only necessary when a feature has no Python 3 equivalent.
Type stubs should avoid checking against ``sys.version_info.major``
directly and should not use comparison operators other than ``<`` and ``>=``.
No::
if sys.version_info.major >= 3:
# Semantically the same as the first tuple check.
if sys.version_info[0] >= 3:
# This is also the same.
if sys.version_info <= (2, 7):
# This does not work because e.g. (2, 7, 1) > (2, 7).
Some type stubs also may need to specify type hints for different platforms.
Platform checks must be equality comparisons between ``sys.platform`` and the name
of a platform as a string literal:
Yes::
if sys.platform == 'win32':
# Windows-specific type hints.
else:
# Posix-specific type hints.
No::
if sys.platform.startswith('linux'):
# Not necessary since Python 3.3.
if sys.platform in ['linux', 'cygwin', 'darwin']:
# Only '==' or '!=' should be used in platform checks.
Version and platform comparisons can be chained using the ``and`` and ``or``
operators::
if sys.platform == 'linux' and (sys.version_info < (3,) or sys,version_info >= (3, 7)): ...
Enums
-----
Enum classes are supported in stubs, regardless of the Python version targeted by
the stubs.
Enum members may be specified just like other forms of assignments, for example as
``x: int``, ``x = 0``, or ``x = ...``. The first syntax is preferred because it
allows type checkers to correctly type the ``.value`` attribute of enum members,
without providing unnecessary information like the runtime value of the enum member.
Additional properties on enum members should be specified with ``@property``, so they
do not get interpreted by type checkers as enum members.
Yes::
from enum import Enum
class Color(Enum):
RED: int
BLUE: int
@property
def rgb_value(self) -> int: ...
class Color(Enum):
# discouraged; type checkers will not understand that Color.RED.value is an int
RED = ...
BLUE = ...
@property
def rgb_value(self) -> int: ...
No::
from enum import Enum
class Color(Enum):
RED: int
BLUE: int
rgb_value: int # no way for type checkers to know that this is not an enum member
Unsupported Features
--------------------
Currently, the following features are not supported by all type checkers
and should not be used in stubs:
* Positional-only argument syntax (PEP 570 [#pep570]_). Instead, use
the syntax described in the section :ref:`supported-functions`.
[#ts-4972]_
Type Stub Content
=================
This section documents best practices on what elements to include or
leave out of type stubs.
Modules excluded fom stubs
--------------------------
Not all modules should be included into stubs.
It is recommended to exclude:
1. Implementation details, with `multiprocessing/popen_spawn_win32.py <https://github.com/python/cpython/blob/main/Lib/multiprocessing/popen_spawn_win32.py>`_ as a notable example
2. Modules that are not supposed to be imported, such as ``__main__.py``
3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below)
Public Interface
----------------
Stubs should include the complete public interface (classes, functions,
constants, etc.) of the module they cover, but it is not always
clear exactly what is part of the interface.
The following should always be included:
* All objects listed in the module's documentation.
* All objects included in ``__all__`` (if present).
Other objects may be included if they are not prefixed with an underscore
or if they are being used in practice. (See the next section.)
.. _undocumented-objects:
Undocumented Objects
--------------------
Undocumented objects may be included as long as they are marked with a comment
of the form ``# undocumented``.
Example::
def list2cmdline(seq: Sequence[str]) -> str: ... # undocumented
Such undocumented objects are allowed because omitting objects can confuse
users. Users who see an error like "module X has no attribute Y" will
not know whether the error appeared because their code had a bug or
because the stub is wrong. Although it may also be helpful for a type
checker to point out usage of private objects, false negatives (no errors for
wrong code) are preferable over false positives (type errors
for correct code). In addition, even for private objects a type checker
can be helpful in pointing out that an incorrect type was used.
``__all__``
------------
A type stub should contain an ``__all__`` variable if and only if it also
present at runtime. In that case, the contents of ``__all__`` should be
identical in the stub and at runtime. If the runtime dynamically adds
or removes elements (for example if certain functions are only available on
some platforms), include all possible elements in the stubs.
Stub-Only Objects
-----------------
Definitions that do not exist at runtime may be included in stubs to aid in
expressing types. Sometimes, it is desirable to make a stub-only class available
to a stub's users - for example, to allow them to type the return value of a
public method for which a library does not provided a usable runtime type::
from typing import Protocol
class _Readable(Protocol):
def read(self) -> str: ...
def get_reader() -> _Readable: ...
Structural Types
----------------
As seen in the example with ``_Readable`` in the previous section, a common use
of stub-only objects is to model types that are best described by their
structure. These objects are called protocols [#pep544]_, and it is encouraged
to use them freely to describe simple structural types.
It is `recommended <#private-definitions>`_ to prefix stubs-only object names with ``_``.
Incomplete Stubs
----------------
Partial stubs can be useful, especially for larger packages, but they should
follow the following guidelines:
* Included functions and methods should list all arguments, but the arguments
can be left unannotated.
* Do not use ``Any`` to mark unannotated arguments or return values.
* Partial classes should include a ``__getattr__()`` method marked with an
``# incomplete`` comment (see example below).
* Partial modules (i.e. modules that are missing some or all classes,
functions, or attributes) should include a top-level ``__getattr__()``
function marked with an ``# incomplete`` comment (see example below).
* Partial packages (i.e. packages that are missing one or more sub-modules)
should have a ``__init__.pyi`` stub that is marked as incomplete (see above).
A better alternative is to create empty stubs for all sub-modules and
mark them as incomplete individually.
Example of a partial module with a partial class ``Foo`` and a partially
annotated function ``bar()``::
def __getattr__(name: str) -> Any: ... # incomplete
class Foo:
def __getattr__(self, name: str) -> Any: # incomplete
x: int
y: str
def bar(x: str, y, *, z=...): ...
The ``# incomplete`` comment is mainly intended as a reminder for stub
authors, but can be used by tools to flag such items.
Attribute Access
----------------
Python has several methods for customizing attribute access: ``__getattr__``,
``__getattribute__``, ``__setattr__``, and ``__delattr__``. Of these,
``__getattr__`` and ``__setattr___`` should sometimes be included in stubs.
In addition to marking incomplete definitions, ``__getattr__`` should be
included when a class or module allows any name to be accessed. For example, consider
the following class::
class Foo:
def __getattribute__(self, name):
return self.__dict__.setdefault(name)
An appropriate stub definition is::
from typing import Any
class Foo:
def __getattr__(self, name: str) -> Any | None: ...
Note that only ``__getattr__``, not ``__getattribute__``, is guaranteed to be
supported in stubs.
On the other hand, ``__getattr__`` should be omitted even if the source code
includes it, if only limited names are allowed. For example, consider this class::
class ComplexNumber:
def __init__(self, n):
self._n = n
def __getattr__(self, name):
if name in ("real", "imag"):
return getattr(self._n, name)
raise AttributeError(name)
In this case, the stub should list the attributes individually::
class ComplexNumber:
@property
def real(self) -> float: ...
@property
def imag(self) -> float: ...
def __init__(self, n: complex) -> None: ...
``__setattr___`` should be included when a class allows any name to be set and
restricts the type. For example::
class IntHolder:
def __setattr__(self, name, value):
if isinstance(value, int):
return super().__setattr__(name, value)
raise ValueError(value)
A good stub definition would be::
class IntHolder:
def __setattr__(self, name: str, value: int) -> None: ...
``__delattr__`` should not be included in stubs.
Finally, even in the presence of ``__getattr__`` and ``__setattr__``, it is
still recommended to separately define known attributes.
Constants
---------
When the value of a constant is important, annotate it using ``Literal``
instead of its type.
Yes::
TEL_LANDLINE: Literal["landline"]
TEL_MOBILE: Literal["mobile"]
DAY_FLAG: Literal[0x01]
NIGHT_FLAG: Literal[0x02]
No::
TEL_LANDLINE: str
TEL_MOBILE: str
DAY_FLAG: int
NIGHT_FLAG: int
Documentation or Implementation
-------------------------------
Sometimes a library's documented types will differ from the actual types in the
code. In such cases, type stub authors should use their best judgment. Consider
these two examples::
def print_elements(x):
"""Print every element of list x."""
for y in x:
print(y)
def maybe_raise(x):
"""Raise an error if x (a boolean) is true."""
if x:
raise ValueError()
The implementation of ``print_elements`` takes any iterable, despite the
documented type of ``list``. In this case, annotate the argument as
``Iterable[Any]``, to follow this PEP's style recommendation of preferring
abstract types.
For ``maybe_raise``, on the other hand, it is better to annotate the argument as
``bool`` even though the implementation accepts any object. This guards against
common mistakes like unintentionally passing in ``None``.
If in doubt, consider asking the library maintainers about their intent.
Style Guide
===========
The recommendations in this section are aimed at type stub authors
who wish to provide a consistent style for type stubs. Type checkers
should not reject stubs that do not follow these recommendations, but
linters can warn about them.
Stub files should generally follow the Style Guide for Python Code (PEP 8)
[#pep8]_. There are a few exceptions, outlined below, that take the
different structure of stub files into account and are aimed to create
more concise files.
Maximum Line Length
-------------------
Type stubs should be limited to 130 characters per line.
Blank Lines
-----------
Do not use empty lines between functions, methods, and fields, except to
group them with one empty line. Use one empty line around classes, but do not
use empty lines between body-less classes, except for grouping.
Yes::
def time_func() -> None: ...
def date_func() -> None: ...
def ip_func() -> None: ...
class Foo:
x: int
y: int
def __init__(self) -> None: ...
class MyError(Exception): ...
class AnotherError(Exception): ...
No::
def time_func() -> None: ...
def date_func() -> None: ... # do no leave unnecessary empty lines
def ip_func() -> None: ...
class Foo: # leave only one empty line above
x: int
class MyError(Exception): ... # leave an empty line between the classes
Shorthand Syntax
----------------
Where possible, use shorthand syntax for unions instead of
``Union`` or ``Optional``. ``None`` should be the last
element of an union.
Yes::
def foo(x: str | int) -> None: ...
def bar(x: str | None) -> int | None: ...
No::
def foo(x: Union[str, int]) -> None: ...
def bar(x: Optional[str]) -> Optional[int]: ...
def baz(x: None | str) -> None: ...
Module Level Attributes
-----------------------
Do not use an assignment for module-level attributes.
Yes::
CONST: Literal["const"]
x: int
No::
CONST = "const"
x: int = 0
y: float = ...
z = 0 # type: int
a = ... # type: int
Type Aliases
------------
Use ``TypeAlias`` for type aliases (but not for regular aliases).
Yes::
_IntList: TypeAlias = list[int]
g = os.stat
Path = pathlib.Path
ERROR = errno.EEXIST
No::
_IntList = list[int]
g: TypeAlias = os.stat
Path: TypeAlias = pathlib.Path
ERROR: TypeAlias = errno.EEXIST
Classes
-------
Classes without bodies should use the ellipsis literal ``...`` in place
of the body on the same line as the class definition.
Yes::
class MyError(Exception): ...
No::
class MyError(Exception):
...
class AnotherError(Exception): pass
Instance attributes and class variables follow the same recommendations as
module level attributes:
Yes::
class Foo:
c: ClassVar[str]
x: int
No::
class Foo:
c: ClassVar[str] = ""
d: ClassVar[int] = ...
x = 4
y: int = ...
Functions and Methods
---------------------
Use the same argument names as in the implementation, because
otherwise using keyword arguments will fail. Of course, this
does not apply to positional-only arguments, which are marked with a double
underscore.
Use the ellipsis literal ``...`` in place of actual default argument
values. Use an explicit ``X | None`` annotation instead of
a ``None`` default.
Yes::
def foo(x: int = ...) -> None: ...
def bar(y: str | None = ...) -> None: ...
No::
def foo(x: int = 0) -> None: ...
def bar(y: str = None) -> None: ...
def baz(z: str | None = None) -> None: ...
Do not annotate ``self`` and ``cls`` in method definitions, except when
referencing a type variable.
Yes::
_T = TypeVar("_T")
class Foo:
def bar(self) -> None: ...
@classmethod
def create(cls: type[_T]) -> _T: ...
No::
class Foo:
def bar(self: Foo) -> None: ...
@classmethod
def baz(cls: type[Foo]) -> int: ...
The bodies of functions and methods should consist of only the ellipsis
literal ``...`` on the same line as the closing parenthesis and colon.
Yes::
def to_int1(x: str) -> int: ...
def to_int2(
x: str,
) -> int: ...
No::
def to_int1(x: str) -> int:
return int(x)
def to_int2(x: str) -> int:
...
def to_int3(x: str) -> int: pass
.. _private-definitions:
Private Definitions
-------------------
Type variables, type aliases, and other definitions that should not
be used outside the stub should be marked as private by prefixing them
with an underscore.
Yes::
_T = TypeVar("_T")
_DictList = Dict[str, List[Optional[int]]
No::
T = TypeVar("T")
DictList = Dict[str, List[Optional[int]]]
Language Features
-----------------
Use the latest language features available as outlined
in the Syntax_ section, even for stubs targeting older Python versions.
Do not use quotes around forward references and do not use ``__future__``
imports.
Yes::
class Py35Class:
x: int
forward_reference: OtherClass
class OtherClass: ...
No::
class Py35Class:
x = 0 # type: int
forward_reference: 'OtherClass'
class OtherClass: ...
Types
-----
Generally, use ``Any`` when a type cannot be expressed appropriately
with the current type system or using the correct type is unergonomic.
Use ``float`` instead of ``int | float``.
Use ``None`` instead of ``Literal[None]``.
For argument types,
use ``bytes`` instead of ``bytes | memoryview | bytearray``.
Use ``Text`` in stubs that support Python 2 when something accepts both
``str`` and ``unicode``. Avoid using ``Text`` in stubs or branches for
Python 3 only.
Yes::
if sys.version_info < (3,):
def foo(s: Text) -> None: ...
else:
def foo(s: str, *, i: int) -> None: ...
def bar(s: Text) -> None: ...
No::
if sys.version_info < (3,):
def foo(s: unicode) -> None: ...
else:
def foo(s: Text, *, i: int) -> None: ...
For arguments, prefer protocols and abstract types (``Mapping``,
``Sequence``, ``Iterable``, etc.). If an argument accepts literally any value,
use ``object`` instead of ``Any``.
For return values, prefer concrete types (``list``, ``dict``, etc.) for
concrete implementations. The return values of protocols
and abstract base classes must be judged on a case-by-case basis.
Yes::
def map_it(input: Iterable[str]) -> list[int]: ...
def create_map() -> dict[str, int]: ...
def to_string(o: object) -> str: ... # accepts any object
No::
def map_it(input: list[str]) -> list[int]: ...
def create_map() -> MutableMapping[str, int]: ...
def to_string(o: Any) -> str: ...
Maybe::
class MyProto(Protocol):
def foo(self) -> list[int]: ...
def bar(self) -> Mapping[str]: ...
Avoid union return types, since they require ``isinstance()`` checks.
Use ``Any`` or ``X | Any`` if necessary.
Use built-in generics instead of the aliases from ``typing``,
where possible. See the section `Built-in Generics`_ for cases,
where it's not possible to use them.
Yes::
from collections.abc import Iterable
def foo(x: type[MyClass]) -> list[str]: ...
def bar(x: Iterable[str]) -> None: ...
No::
from typing import Iterable, List, Type
def foo(x: Type[MyClass]) -> List[str]: ...
def bar(x: Iterable[str]) -> None: ...
NamedTuple and TypedDict
------------------------
Use the class-based syntax for ``typing.NamedTuple`` and
``typing.TypedDict``, following the Classes section of this style guide.
Yes::
from typing import NamedTuple, TypedDict
class Point(NamedTuple):
x: float
y: float
class Thing(TypedDict):
stuff: str
index: int
No::
from typing import NamedTuple, TypedDict
Point = NamedTuple("Point", [('x', float), ('y', float)])
Thing = TypedDict("Thing", {'stuff': str, 'index': int})
References
==========
PEPs
----
.. [#pep8] PEP 8 -- Style Guide for Python Code, van Rossum et al. (https://www.python.org/dev/peps/pep-0008/)
.. [#pep484] PEP 484 -- Type Hints, van Rossum et al. (https://www.python.org/dev/peps/pep-0484)
.. [#pep492] PEP 492 -- Coroutines with async and await syntax, Selivanov (https://www.python.org/dev/peps/pep-0492/)
.. [#pep526] PEP 526 -- Syntax for Variable Annotations, Gonzalez et al. (https://www.python.org/dev/peps/pep-0526)
.. [#pep544] PEP 544 -- Protocols: Structural Subtyping, Levkivskyi et al. (https://www.python.org/dev/peps/pep-0544)
.. [#pep561] PEP 561 -- Distributing and Packaging Type Information, Smith (https://www.python.org/dev/peps/pep-0561)
.. [#pep570] PEP 570 -- Python Positional-Only Parameters, Hastings et al. (https://www.python.org/dev/peps/pep-0570)
.. [#pep585] PEP 585 -- Type Hinting Generics In Standard Collections, Langa (https://www.python.org/dev/peps/pep-0585)
.. [#pep604] PEP 604 -- Allow writing union types as X | Y, Prados and Moss (https://www.python.org/dev/peps/pep-0604)
.. [#pep612] PEP 612 -- Parameter Specification Variables, Mendoza (https://www.python.org/dev/peps/pep-0612)
.. [#pep613] PEP 613 -- Explicit Type Aliases, Zhu (https://www.python.org/dev/peps/pep-0613)
.. [#pep647] PEP 647 -- User-Defined Type Guards, Traut (https://www.python.org/dev/peps/pep-0647)
.. [#pep3107] PEP 3107 -- Function Annotations, Winter and Lownds (https://www.python.org/dev/peps/pep-3107)
Bugs
----
.. [#ts-4819] typeshed issue #4819 -- PEP 604 tracker (https://github.com/python/typeshed/issues/4819)
.. [#ts-4972] typeshed issue #4972 -- PEP 570 tracker (https://github.com/python/typeshed/issues/4972)
Copyright
=========
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.