1130 lines
35 KiB
ReStructuredText
1130 lines
35 KiB
ReStructuredText
.. _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.
|