Skip to content

Commit dd2c9a6

Browse files
committed
Improve some dynamic typing docs (#14576)
Linking #13681
1 parent a6dce84 commit dd2c9a6

File tree

4 files changed

+119
-22
lines changed

4 files changed

+119
-22
lines changed

docs/source/dynamic_typing.rst

+65-15
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,39 @@
44
Dynamically typed code
55
======================
66

7-
As mentioned earlier, bodies of functions that don't have any explicit
8-
types in their function annotation are dynamically typed (operations
9-
are checked at runtime). Code outside functions is statically typed by
10-
default, and types of variables are inferred. This does usually the
11-
right thing, but you can also make any variable dynamically typed by
12-
defining it explicitly with the type ``Any``:
7+
In :ref:`getting-started-dynamic-vs-static`, we discussed how bodies of functions
8+
that don't have any explicit type annotations in their function are "dynamically typed"
9+
and that mypy will not check them. In this section, we'll talk a little bit more
10+
about what that means and how you can enable dynamic typing on a more fine grained basis.
11+
12+
In cases where your code is too magical for mypy to understand, you can make a
13+
variable or parameter dynamically typed by explicitly giving it the type
14+
``Any``. Mypy will let you do basically anything with a value of type ``Any``,
15+
including assigning a value of type ``Any`` to a variable of any type (or vice
16+
versa).
1317

1418
.. code-block:: python
1519
1620
from typing import Any
1721
18-
s = 1 # Statically typed (type int)
19-
d: Any = 1 # Dynamically typed (type Any)
20-
s = 'x' # Type check error
21-
d = 'x' # OK
22+
num = 1 # Statically typed (inferred to be int)
23+
num = 'x' # error: Incompatible types in assignment (expression has type "str", variable has type "int")
24+
25+
dyn: Any = 1 # Dynamically typed (type Any)
26+
dyn = 'x' # OK
27+
28+
num = dyn # No error, mypy will let you assign a value of type Any to any variable
29+
num += 1 # Oops, mypy still thinks num is an int
30+
31+
You can think of ``Any`` as a way to locally disable type checking.
32+
See :ref:`silencing-type-errors` for other ways you can shut up
33+
the type checker.
2234

2335
Operations on Any values
2436
------------------------
2537

26-
You can do anything using a value with type ``Any``, and type checker
27-
does not complain:
38+
You can do anything using a value with type ``Any``, and the type checker
39+
will not complain:
2840

2941
.. code-block:: python
3042
@@ -37,20 +49,53 @@ does not complain:
3749
open(x).read()
3850
return x
3951
40-
Values derived from an ``Any`` value also often have the type ``Any``
52+
Values derived from an ``Any`` value also usually have the type ``Any``
4153
implicitly, as mypy can't infer a more precise result type. For
4254
example, if you get the attribute of an ``Any`` value or call a
4355
``Any`` value the result is ``Any``:
4456

4557
.. code-block:: python
4658
4759
def f(x: Any) -> None:
48-
y = x.foo() # y has type Any
49-
y.bar() # Okay as well!
60+
y = x.foo()
61+
reveal_type(y) # Revealed type is "Any"
62+
z = y.bar("mypy will let you do anything to y")
63+
reveal_type(z) # Revealed type is "Any"
5064
5165
``Any`` types may propagate through your program, making type checking
5266
less effective, unless you are careful.
5367

68+
Function parameters without annotations are also implicitly ``Any``:
69+
70+
.. code-block:: python
71+
72+
def f(x) -> None:
73+
reveal_type(x) # Revealed type is "Any"
74+
x.can.do["anything", x]("wants", 2)
75+
76+
You can make mypy warn you about untyped function parameters using the
77+
:option:`--disallow-untyped-defs <mypy --disallow-untyped-defs>` flag.
78+
79+
Generic types missing type parameters will have those parameters implicitly
80+
treated as ``Any``:
81+
82+
.. code-block:: python
83+
84+
from typing import List
85+
86+
def f(x: List) -> None:
87+
reveal_type(x) # Revealed type is "builtins.list[Any]"
88+
reveal_type(x[0]) # Revealed type is "Any"
89+
x[0].anything_goes() # OK
90+
91+
You can make mypy warn you about untyped function parameters using the
92+
:option:`--disallow-any-generics <mypy --disallow-any-generics>` flag.
93+
94+
Finally, another major source of ``Any`` types leaking into your program is from
95+
third party libraries that mypy does not know about. This is particularly the case
96+
when using the :option:`--ignore-missing-imports <mypy --ignore-missing-imports>`
97+
flag. See :ref:`fix-missing-imports` for more information about this.
98+
5499
Any vs. object
55100
--------------
56101

@@ -80,6 +125,11 @@ operations:
80125
n: int = 1
81126
n = o # Error!
82127
128+
129+
If you're not sure whether you need to use :py:class:`object` or ``Any``, use
130+
:py:class:`object` -- only switch to using ``Any`` if you get a type checker
131+
complaint.
132+
83133
You can use different :ref:`type narrowing <type-narrowing>`
84134
techniques to narrow :py:class:`object` to a more specific
85135
type (subtype) such as ``int``. Type narrowing is not needed with

docs/source/getting_started.rst

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ easy to adopt mypy incrementally.
4444
In order to get useful diagnostics from mypy, you must add *type annotations*
4545
to your code. See the section below for details.
4646

47+
.. _getting-started-dynamic-vs-static:
48+
4749
Dynamic vs static typing
4850
************************
4951

docs/source/running_mypy.rst

+12-5
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ attribute of the module will automatically succeed:
228228
# But this type checks, and x will have type 'Any'
229229
x = does_not_exist.foobar()
230230
231+
This can result in mypy failing to warn you about errors in your code. Since
232+
operations on ``Any`` result in ``Any``, these dynamic types can propagate
233+
through your code, making type checking less effective. See
234+
:ref:`dynamic-typing` for more information.
235+
231236
The next sections describe what each of these errors means and recommended next steps; scroll to
232237
the section that matches your error.
233238

@@ -245,7 +250,7 @@ unless they either have declared themselves to be
245250
themselves on `typeshed <https://github.com/python/typeshed>`_, the repository
246251
of types for the standard library and some 3rd party libraries.
247252

248-
If you are getting this error, try:
253+
If you are getting this error, try to obtain type hints for the library you're using:
249254

250255
1. Upgrading the version of the library you're using, in case a newer version
251256
has started to include type hints.
@@ -264,7 +269,7 @@ If you are getting this error, try:
264269
adding the location to the ``MYPYPATH`` environment variable.
265270

266271
These stub files do not need to be complete! A good strategy is to use
267-
stubgen, a program that comes bundled with mypy, to generate a first
272+
:ref:`stubgen <stubgen>`, a program that comes bundled with mypy, to generate a first
268273
rough draft of the stubs. You can then iterate on just the parts of the
269274
library you need.
270275

@@ -273,9 +278,11 @@ If you are getting this error, try:
273278
:ref:`PEP 561 compliant packages <installed-packages>`.
274279

275280
If you are unable to find any existing type hints nor have time to write your
276-
own, you can instead *suppress* the errors. All this will do is make mypy stop
277-
reporting an error on the line containing the import: the imported module
278-
will continue to be of type ``Any``.
281+
own, you can instead *suppress* the errors.
282+
283+
All this will do is make mypy stop reporting an error on the line containing the
284+
import: the imported module will continue to be of type ``Any``, and mypy may
285+
not catch errors in its use.
279286

280287
1. To suppress a *single* missing import error, add a ``# type: ignore`` at the end of the
281288
line containing the import.

docs/source/type_inference_and_annotations.rst

+40-2
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ Working around the issue is easy by adding a type annotation:
185185
a: list[int] = [] # OK
186186
foo(a)
187187
188+
.. _silencing-type-errors:
189+
188190
Silencing type errors
189191
*********************
190192

@@ -228,6 +230,8 @@ short explanation of the bug. To do that, use this format:
228230
# Starting app on http://localhost:8000
229231
app.run(8000) # type: ignore # `run()` in v2.0 accepts an `int`, as a port
230232
233+
Type ignore error codes
234+
-----------------------
231235

232236
By default, mypy displays an error code for each error:
233237

@@ -240,7 +244,21 @@ It is possible to add a specific error-code in your ignore comment (e.g.
240244
``# type: ignore[attr-defined]``) to clarify what's being silenced. You can
241245
find more information about error codes :ref:`here <silence-error-codes>`.
242246

243-
Similarly, you can also ignore all mypy errors in a file, by adding a
247+
Other ways to silence errors
248+
----------------------------
249+
250+
You can get mypy to silence errors about a specific variable by dynamically
251+
typing it with ``Any``. See :ref:`dynamic-typing` for more information.
252+
253+
.. code-block:: python
254+
255+
from typing import Any
256+
257+
def f(x: Any, y: str) -> None:
258+
x = 'hello'
259+
x += 1 # OK
260+
261+
You can ignore all mypy errors in a file by adding a
244262
``# mypy: ignore-errors`` at the top of the file:
245263

246264
.. code-block:: python
@@ -250,8 +268,28 @@ Similarly, you can also ignore all mypy errors in a file, by adding a
250268
import unittest
251269
...
252270
271+
You can also specify per-module configuration options in your :ref:`config-file`.
272+
For example:
273+
274+
.. code-block:: ini
275+
276+
# Don't report errors in the 'package_to_fix_later' package
277+
[mypy-package_to_fix_later.*]
278+
ignore_errors = True
279+
280+
# Disable specific error codes in the 'tests' package
281+
# Also don't require type annotations
282+
[mypy-tests.*]
283+
disable_error_code = var-annotated, has-type
284+
allow_untyped_defs = True
285+
286+
# Silence import errors from the 'library_missing_types' package
287+
[mypy-library_missing_types.*]
288+
ignore_missing_imports = True
289+
253290
Finally, adding a ``@typing.no_type_check`` decorator to a class, method or
254-
function has the effect of ignoring that class, method or function.
291+
function causes mypy to avoid type checking that class, method or function
292+
and to treat it as not having any type annotations.
255293

256294
.. code-block:: python
257295

0 commit comments

Comments
 (0)