|
4 | 4 | Typing Python Libraries |
5 | 5 | *********************** |
6 | 6 |
|
7 | | -Much of Python’s popularity can be attributed to the rich collection of |
| 7 | +Much of Python's popularity can be attributed to the rich collection of |
8 | 8 | Python libraries available to developers. Authors of these libraries |
9 | 9 | play an important role in improving the experience for Python |
10 | 10 | developers. This document provides some recommendations and guidance for |
11 | 11 | Python library authors. |
12 | 12 |
|
13 | | -These recommendations are intended to provide the following benefits: |
14 | | - |
15 | | -1. Consumers of libraries should have a great coding experience with |
16 | | - fast and accurate completion suggestions, class and function |
17 | | - documentation, signature help (including parameter default values), |
18 | | - hover text, and auto-imports. This should happen by default without |
19 | | - needing to download extra packages and without any special |
20 | | - configuration. These features should be consistent across the Python |
21 | | - ecosystem regardless of a developer’s choice of editor, IDE, notebook |
22 | | - environment, etc. |
23 | | -2. Consumers of libraries should be able to rely on complete and |
24 | | - accurate type information so static type checkers can detect and |
25 | | - report type inconsistencies and other violations of the interface |
26 | | - contract. |
27 | | -3. Library authors should be able to specify a well-defined interface |
28 | | - contract that is enforced by tools. This allows a library |
29 | | - implementation to evolve and improve without breaking consumers of |
30 | | - the library. |
31 | | -4. Library authors should have the benefits of static type checking to |
| 13 | +Why provide type annotations? |
| 14 | +============================= |
| 15 | + |
| 16 | +Providing type annotations has the following benefits: |
| 17 | + |
| 18 | +1. Type annotations help provide users of libraries a better coding |
| 19 | + experience by enabling fast and accurate completion suggestions, class and |
| 20 | + function documentation, signature help, hover text, auto-imports, etc. |
| 21 | +2. Users of libraries are able to use static type checkers to detect issues |
| 22 | + with their use of libraries. |
| 23 | +3. Type annotations allow library authors to specify an interface contract that |
| 24 | + is enforced by tools. This lets the library implementation evolve with less |
| 25 | + fear that users are depending on implementation details. In the event of |
| 26 | + changes to the library interface, type checkers are able to warn users when |
| 27 | + their code is affected. |
| 28 | +4. Library authors are able to use static type checking themselves to help |
32 | 29 | produce high-quality, bug-free implementations. |
33 | 30 |
|
34 | | -Inlined Type Annotations and Type Stubs |
35 | | -======================================= |
| 31 | +How to provide type annotations? |
| 32 | +================================ |
36 | 33 |
|
37 | | -`PEP 561 <https://www.python.org/dev/peps/pep-0561/>`__ documents |
38 | | -several ways type information can be delivered for a library: inlined |
39 | | -type annotations, type stub files included in the package, a separate |
40 | | -companion type stub package, and type stubs in the typeshed repository. |
41 | | -Some of these options fall short on delivering the benefits above. We |
42 | | -therefore provide the following more specific guidance to library |
43 | | -authors. |
| 34 | +:pep:`561` documents several ways type information can be provided for a |
| 35 | +library: |
44 | 36 |
|
45 | | -.. note:: |
46 | | - All libraries should include inlined type annotations for the |
47 | | - functions, classes, methods, and constants that comprise the public |
48 | | - interface for the library. |
49 | | - |
50 | | -Inlined type annotations should be included directly within the source |
51 | | -code that ships with the package. Of the options listed in PEP 561, |
52 | | -inlined type annotations offer the most benefits. They typically require |
53 | | -the least effort to add and maintain, they are always consistent with |
54 | | -the implementation, and docstrings and default parameter values are |
55 | | -readily available, allowing language servers to enhance the development |
56 | | -experience. |
57 | | - |
58 | | -There are cases where inlined type annotations are not possible — most |
59 | | -notably when a library’s exposed functionality is implemented in a |
60 | | -language other than Python. |
| 37 | +- inline type annotations (preferred) |
| 38 | +- type stub files included in the package |
| 39 | +- a separate companion type stub package |
| 40 | +- type stubs in the typeshed repository |
| 41 | + |
| 42 | +Inline type annotations simply refers to the use of annotations within your |
| 43 | +``.py`` files. In contrast, with type stub files, type information lives in |
| 44 | +separate ``.pyi`` files; see :ref:`stubs` for more details. |
| 45 | + |
| 46 | +.. |
| 47 | + TODO: link to a guide for writing stubs above |
| 48 | +
|
| 49 | +We recommend using the inline type annotations approach, since it has the |
| 50 | +following benefits: |
| 51 | + |
| 52 | +- Typically requires the least effort to add and maintain |
| 53 | +- Users don't have to download additional packages |
| 54 | +- Always remains consistent with the implementation |
| 55 | +- Allows library authors to type check their own code |
| 56 | +- Allows language servers to show users relevant details about the |
| 57 | + implementation, such as docstrings and default parameter values |
| 58 | + |
| 59 | +However, there are cases where inlined type annotations are not possible — most |
| 60 | +notably when a library's functionality is implemented in a language |
| 61 | +other than Python. |
| 62 | + |
| 63 | +If you are not interested in providing type annotations for your library, you |
| 64 | +could suggest users to contribute type stubs to the |
| 65 | +`typeshed <https://github.com/python/typeshed>`__ project. |
| 66 | + |
| 67 | +Marking a package as providing type information |
| 68 | +----------------------------------------------- |
| 69 | + |
| 70 | +As specified in :pep:`561`, tools will not treat your package as providing type |
| 71 | +information unless it includes a special ``py.typed`` marker file. |
61 | 72 |
|
62 | 73 | .. note:: |
63 | | - Libraries that expose symbols implemented in languages other than |
64 | | - Python should include stub (``.pyi``) files that describe the types for |
65 | | - those symbols. These stubs should also contain docstrings and default |
66 | | - parameter values. |
| 74 | + Before marking a package as providing type information, it is best to ensure |
| 75 | + that the library's interface is fully annotated. See :ref:`type_completeness` |
| 76 | + for more details. |
| 77 | + |
| 78 | +Inline type annotations |
| 79 | +^^^^^^^^^^^^^^^^^^^^^^^ |
| 80 | + |
| 81 | +A typical directory structure would look like: |
| 82 | + |
| 83 | +.. code-block:: text |
| 84 | +
|
| 85 | + setup.py |
| 86 | + my_great_package/ |
| 87 | + __init__.py |
| 88 | + stuff.py |
| 89 | + py.typed |
| 90 | +
|
| 91 | +It's important to ensure that the ``py.typed`` marker file is included in the |
| 92 | +distributed package. If using ``setuptools``, this can be achieved like so: |
| 93 | + |
| 94 | +.. code-block:: python |
| 95 | +
|
| 96 | + from setuptools import setup |
| 97 | +
|
| 98 | + setup( |
| 99 | + name="my_great_distribution", |
| 100 | + version="0.1", |
| 101 | + package_data={"my_great_package": ["py.typed"]}, |
| 102 | + packages=["my_great_package"], |
| 103 | + ) |
| 104 | +
|
| 105 | +
|
| 106 | +Type stub files included in the package |
| 107 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 108 | + |
| 109 | +It's possible to include a mix of type stub files (``.pyi``) and inline type |
| 110 | +annotations (``.py``). One use case for including type stub files in your |
| 111 | +package is to provide types for extension modules in your library. A typical |
| 112 | +directory structure would look like: |
| 113 | + |
| 114 | +.. code-block:: text |
| 115 | +
|
| 116 | + setup.py |
| 117 | + my_great_package/ |
| 118 | + __init__.py |
| 119 | + stuff.py |
| 120 | + stuff.pyi |
| 121 | + py.typed |
| 122 | +
|
| 123 | +If using ``setuptools``, we can ensure the ``.pyi`` and ``py.typed`` files are |
| 124 | +included like so: |
| 125 | + |
| 126 | +.. code-block:: python |
| 127 | +
|
| 128 | + from setuptools import setup |
| 129 | +
|
| 130 | + setup( |
| 131 | + name="my_great_distribution", |
| 132 | + version="0.1", |
| 133 | + package_data={"my_great_package": ["py.typed", "stuff.pyi"]}, |
| 134 | + packages=["my_great_package"], |
| 135 | + ) |
67 | 136 |
|
68 | | -In many existing type stubs (such as those found in typeshed), default |
69 | | -parameter values are replaced with with ``...`` and all docstrings are |
70 | | -removed. We recommend that default values and docstrings remain within |
71 | | -the type stub file so language servers can display this information to |
72 | | -developers. |
| 137 | +The presence of ``.pyi`` files does not affect the Python interpreter at runtime |
| 138 | +in any way. However, static type checkers will only look at the ``.pyi`` file and |
| 139 | +ignore the corresponding ``.py`` file. |
73 | 140 |
|
74 | | -Library Interface |
75 | | -================= |
| 141 | +Companion type stub package |
| 142 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
76 | 143 |
|
77 | | -`PEP 561 <https://www.python.org/dev/peps/pep-0561/>`__ indicates that a |
78 | | -``py.typed`` marker file must be included in the package if the author |
79 | | -wishes to support type checking of their code. |
| 144 | +These are often referred to as "stub-only" packages. The name of the stub package |
| 145 | +should be the name of the runtime package suffixed with ``-stubs``. The ``py.typed`` |
| 146 | +marker file is not necessary for stub-only packages. This approach can be useful |
| 147 | +to develop type stubs independently from your library. |
| 148 | + |
| 149 | +For example: |
| 150 | + |
| 151 | +.. code-block:: text |
| 152 | +
|
| 153 | + setup.py |
| 154 | + my_great_package-stubs/ |
| 155 | + __init__.pyi |
| 156 | + stuff.pyi |
| 157 | +
|
| 158 | +
|
| 159 | +.. code-block:: python |
| 160 | +
|
| 161 | + from setuptools import setup |
| 162 | +
|
| 163 | + setup( |
| 164 | + name="my_great_package-stubs", |
| 165 | + version="0.1", |
| 166 | + package_data={"my_great_package-stubs": ["__init__.pyi", "stuff.pyi"]}, |
| 167 | + packages=["my_great_package-stubs"] |
| 168 | + ) |
| 169 | +
|
| 170 | +
|
| 171 | +Users are then able to install the stubs-only package separately to provide |
| 172 | +types for the original library. |
| 173 | + |
| 174 | +Inclusion in sdist |
| 175 | +^^^^^^^^^^^^^^^^^^ |
| 176 | + |
| 177 | +Note that to ensure inclusion of ``.pyi`` and ``py.typed`` files in an sdist |
| 178 | +(.tar.gz archive), you may also need to modify the inclusion rules in your |
| 179 | +``MANIFEST.in`` (see the |
| 180 | +`packaging guide <https://packaging.python.org/en/latest/guides/using-manifest-in/>`__ |
| 181 | +for more details on ``MANIFEST.in``). For example: |
| 182 | + |
| 183 | +.. code-block:: text |
| 184 | +
|
| 185 | + global-include *.pyi |
| 186 | + global-include py.typed |
| 187 | +
|
| 188 | +.. _type_completeness: |
| 189 | + |
| 190 | +How much of my library needs types? |
| 191 | +=================================== |
| 192 | + |
| 193 | +A "py.typed" library should aim to be type complete so that type |
| 194 | +checking and inspection can work to their full extent. Here we say that a |
| 195 | +library is “type complete” if all of the symbols |
| 196 | +that comprise its interface have type annotations that refer to types |
| 197 | +that are fully known. Private symbols are exempt. |
| 198 | + |
| 199 | +Library interface (public and private symbols) |
| 200 | +---------------------------------------------- |
80 | 201 |
|
81 | 202 | If a ``py.typed`` module is present, a type checker will treat all modules |
82 | 203 | within that package (i.e. all files that end in ``.py`` or ``.pyi``) as |
@@ -119,13 +240,7 @@ determine the value of ``__all__``. |
119 | 240 | - ``__all__.remove('a')`` |
120 | 241 |
|
121 | 242 | Type Completeness |
122 | | -================= |
123 | | - |
124 | | -A “py.typed” library should aim to be type complete so that type |
125 | | -checking and inspection can work to their full extent. Here we say that a |
126 | | -library is “type complete” if all of the symbols |
127 | | -that comprise its interface have type annotations that refer to types |
128 | | -that are fully known. Private symbols are exempt. |
| 243 | +----------------- |
129 | 244 |
|
130 | 245 | The following are best practice recommendations for how to define “type complete”: |
131 | 246 |
|
@@ -282,6 +397,9 @@ Examples of known and unknown types |
282 | 397 | class DictSubclass(dict): |
283 | 398 | pass |
284 | 399 |
|
| 400 | +.. |
| 401 | + TODO: consider moving best practices to their own page? |
| 402 | +
|
285 | 403 | Best Practices for Inlined Types |
286 | 404 | ================================ |
287 | 405 |
|
|
0 commit comments