Skip to content

Commit 620da98

Browse files
committed
Merge branch 'typed-dict-in-type-narrowing' of https://github.com/ikonst/mypy into typed-dict-in-type-narrowing
2 parents 520df60 + 2807feb commit 620da98

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1485
-782
lines changed

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
toxenv: py
6565
tox_extra_args: "-n 2"
6666
- name: Test suite with py311-ubuntu, mypyc-compiled
67-
python: '3.11-dev'
67+
python: '3.11'
6868
arch: x64
6969
os: ubuntu-latest
7070
toxenv: py

.pre-commit-config.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/psf/black
3-
rev: 22.6.0 # must match test-requirements.txt
3+
rev: 22.10.0 # must match test-requirements.txt
44
hooks:
55
- id: black
66
- repo: https://github.com/pycqa/isort
@@ -12,5 +12,5 @@ repos:
1212
hooks:
1313
- id: flake8
1414
additional_dependencies:
15-
- flake8-bugbear==22.8.23 # must match test-requirements.txt
15+
- flake8-bugbear==22.9.23 # must match test-requirements.txt
1616
- flake8-noqa==1.2.9 # must match test-requirements.txt

docs/source/cheat_sheet_py3.rst

+76-76
Original file line numberDiff line numberDiff line change
@@ -34,39 +34,45 @@ Useful built-in types
3434

3535
.. code-block:: python
3636
37-
from typing import List, Set, Dict, Tuple, Optional
38-
3937
# For most types, just use the name of the type
4038
x: int = 1
4139
x: float = 1.0
4240
x: bool = True
4341
x: str = "test"
4442
x: bytes = b"test"
4543
46-
# For collections, the type of the collection item is in brackets
47-
# (Python 3.9+)
44+
# For collections on Python 3.9+, the type of the collection item is in brackets
4845
x: list[int] = [1]
4946
x: set[int] = {6, 7}
5047
51-
# In Python 3.8 and earlier, the name of the collection type is
52-
# capitalized, and the type is imported from the 'typing' module
53-
x: List[int] = [1]
54-
x: Set[int] = {6, 7}
55-
5648
# For mappings, we need the types of both keys and values
5749
x: dict[str, float] = {"field": 2.0} # Python 3.9+
58-
x: Dict[str, float] = {"field": 2.0}
5950
6051
# For tuples of fixed size, we specify the types of all the elements
6152
x: tuple[int, str, float] = (3, "yes", 7.5) # Python 3.9+
62-
x: Tuple[int, str, float] = (3, "yes", 7.5)
6353
6454
# For tuples of variable size, we use one type and ellipsis
6555
x: tuple[int, ...] = (1, 2, 3) # Python 3.9+
56+
57+
# On Python 3.8 and earlier, the name of the collection type is
58+
# capitalized, and the type is imported from the 'typing' module
59+
from typing import List, Set, Dict, Tuple
60+
x: List[int] = [1]
61+
x: Set[int] = {6, 7}
62+
x: Dict[str, float] = {"field": 2.0}
63+
x: Tuple[int, str, float] = (3, "yes", 7.5)
6664
x: Tuple[int, ...] = (1, 2, 3)
6765
68-
# Use Optional[] for values that could be None
69-
x: Optional[str] = some_function()
66+
from typing import Union, Optional
67+
68+
# On Python 3.10+, use the | operator when something could be one of a few types
69+
x: list[int | str] = [3, 5, "test", "fun"] # Python 3.10+
70+
# On earlier versions, use Union
71+
x: list[Union[int, str]] = [3, 5, "test", "fun"]
72+
73+
# Use Optional[X] for a value that could be None
74+
# Optional[X] is the same as X | None or Union[X, None]
75+
x: Optional[str] = "something" if some_condition() else None
7076
# Mypy understands a value can't be None in an if-statement
7177
if x is not None:
7278
print(x.upper())
@@ -89,9 +95,10 @@ Functions
8995
def plus(num1: int, num2: int) -> int:
9096
return num1 + num2
9197
92-
# Add default value for an argument after the type annotation
93-
def f(num1: int, my_float: float = 3.5) -> float:
94-
return num1 + my_float
98+
# If a function does not return a value, use None as the return type
99+
# Default value for an argument goes after the type annotation
100+
def show(value: str, excitement: int = 10) -> None:
101+
print(value + "!" * excitement)
95102
96103
# This is how you annotate a callable (function) value
97104
x: Callable[[int, float], float] = f
@@ -124,13 +131,61 @@ Functions
124131
quux(3, 5) # error: Too many positional arguments for "quux"
125132
quux(x=3, y=5) # error: Unexpected keyword argument "x" for "quux"
126133
127-
# This makes each positional arg and each keyword arg a "str"
134+
# This says each positional arg and each keyword arg is a "str"
128135
def call(self, *args: str, **kwargs: str) -> str:
129136
reveal_type(args) # Revealed type is "tuple[str, ...]"
130137
reveal_type(kwargs) # Revealed type is "dict[str, str]"
131138
request = make_request(*args, **kwargs)
132139
return self.do_api_query(request)
133140
141+
Classes
142+
*******
143+
144+
.. code-block:: python
145+
146+
class MyClass:
147+
# You can optionally declare instance variables in the class body
148+
attr: int
149+
# This is an instance variable with a default value
150+
charge_percent: int = 100
151+
152+
# The "__init__" method doesn't return anything, so it gets return
153+
# type "None" just like any other method that doesn't return anything
154+
def __init__(self) -> None:
155+
...
156+
157+
# For instance methods, omit type for "self"
158+
def my_method(self, num: int, str1: str) -> str:
159+
return num * str1
160+
161+
# User-defined classes are valid as types in annotations
162+
x: MyClass = MyClass()
163+
164+
# You can also declare the type of an attribute in "__init__"
165+
class Box:
166+
def __init__(self) -> None:
167+
self.items: list[str] = []
168+
169+
# You can use the ClassVar annotation to declare a class variable
170+
class Car:
171+
seats: ClassVar[int] = 4
172+
passengers: ClassVar[list[str]]
173+
174+
# If you want dynamic attributes on your class, have it
175+
# override "__setattr__" or "__getattr__":
176+
# - "__getattr__" allows for dynamic access to names
177+
# - "__setattr__" allows for dynamic assignment to names
178+
class A:
179+
# This will allow assignment to any A.x, if x is the same type as "value"
180+
# (use "value: Any" to allow arbitrary types)
181+
def __setattr__(self, name: str, value: int) -> None: ...
182+
183+
# This will allow access to any A.x, if x is compatible with the return type
184+
def __getattr__(self, name: str) -> int: ...
185+
186+
a.foo = 42 # Works
187+
a.bar = 'Ex-parrot' # Fails type checking
188+
134189
When you're puzzled or when things are complicated
135190
**************************************************
136191

@@ -143,9 +198,6 @@ When you're puzzled or when things are complicated
143198
# message with the type; remove it again before running the code.
144199
reveal_type(1) # Revealed type is "builtins.int"
145200
146-
# Use Union when something could be one of a few types
147-
x: list[Union[int, str]] = [3, 5, "test", "fun"]
148-
149201
# If you initialize a variable with an empty container or "None"
150202
# you may have to help mypy a bit by providing an explicit type annotation
151203
x: list[str] = []
@@ -154,6 +206,8 @@ When you're puzzled or when things are complicated
154206
# Use Any if you don't know the type of something or it's too
155207
# dynamic to write a type for
156208
x: Any = mystery_function()
209+
# Mypy will let you do anything with x!
210+
x.whatever() * x["you"] + x("want") - any(x) and all(x) is super # no errors
157211
158212
# Use a "type: ignore" comment to suppress errors on a given line,
159213
# when your code confuses mypy or runs into an outright bug in mypy.
@@ -216,56 +270,6 @@ that are common in idiomatic Python are standardized.
216270
217271
You can even make your own duck types using :ref:`protocol-types`.
218272

219-
Classes
220-
*******
221-
222-
.. code-block:: python
223-
224-
class MyClass:
225-
# You can optionally declare instance variables in the class body
226-
attr: int
227-
# This is an instance variable with a default value
228-
charge_percent: int = 100
229-
230-
# The "__init__" method doesn't return anything, so it gets return
231-
# type "None" just like any other method that doesn't return anything
232-
def __init__(self) -> None:
233-
...
234-
235-
# For instance methods, omit type for "self"
236-
def my_method(self, num: int, str1: str) -> str:
237-
return num * str1
238-
239-
# User-defined classes are valid as types in annotations
240-
x: MyClass = MyClass()
241-
242-
# You can use the ClassVar annotation to declare a class variable
243-
class Car:
244-
seats: ClassVar[int] = 4
245-
passengers: ClassVar[list[str]]
246-
247-
# You can also declare the type of an attribute in "__init__"
248-
class Box:
249-
def __init__(self) -> None:
250-
self.items: list[str] = []
251-
252-
# If you want dynamic attributes on your class, have it override "__setattr__"
253-
# or "__getattr__" in a stub or in your source code.
254-
#
255-
# "__setattr__" allows for dynamic assignment to names
256-
# "__getattr__" allows for dynamic access to names
257-
class A:
258-
# This will allow assignment to any A.x, if x is the same type as "value"
259-
# (use "value: Any" to allow arbitrary types)
260-
def __setattr__(self, name: str, value: int) -> None: ...
261-
262-
# This will allow access to any A.x, if x is compatible with the return type
263-
def __getattr__(self, name: str) -> int: ...
264-
265-
a.foo = 42 # Works
266-
a.bar = 'Ex-parrot' # Fails type checking
267-
268-
269273
Coroutines and asyncio
270274
**********************
271275

@@ -290,11 +294,7 @@ Miscellaneous
290294
.. code-block:: python
291295
292296
import sys
293-
import re
294-
from typing import Match, IO
295-
296-
# "typing.Match" describes regex matches from the re module
297-
x: Match[str] = re.match(r'[0-9]+', "15")
297+
from typing import IO
298298
299299
# Use IO[] for functions that should accept or return any
300300
# object that comes from an open() call (IO[] does not
@@ -309,7 +309,7 @@ Miscellaneous
309309
310310
# Forward references are useful if you want to reference a class before
311311
# it is defined
312-
def f(foo: A) -> int: # This will fail
312+
def f(foo: A) -> int: # This will fail at runtime with 'A' is not defined
313313
...
314314
315315
class A:

docs/source/existing_code.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ fraction of production network requests. This clearly requires more
168168
care, as type collection could impact the reliability or the
169169
performance of your service.
170170

171+
.. _getting-to-strict:
172+
171173
Introduce stricter options
172174
--------------------------
173175

@@ -181,7 +183,7 @@ An excellent goal to aim for is to have your codebase pass when run against ``my
181183
This basically ensures that you will never have a type related error without an explicit
182184
circumvention somewhere (such as a ``# type: ignore`` comment).
183185

184-
The following config is equivalent to ``--strict``:
186+
The following config is equivalent to ``--strict`` (as of mypy 0.990):
185187

186188
.. code-block:: text
187189

docs/source/extending_mypy.rst

+3-45
Original file line numberDiff line numberDiff line change
@@ -155,23 +155,9 @@ When analyzing this code, mypy will call ``get_type_analyze_hook("lib.Vector")``
155155
so the plugin can return some valid type for each variable.
156156

157157
**get_function_hook()** is used to adjust the return type of a function call.
158-
This is a good choice if the return type of some function depends on *values*
159-
of some arguments that can't be expressed using literal types (for example
160-
a function may return an ``int`` for positive arguments and a ``float`` for
161-
negative arguments). This hook will be also called for instantiation of classes.
162-
For example:
163-
164-
.. code-block:: python
165-
166-
from contextlib import contextmanager
167-
from typing import TypeVar, Callable
168-
169-
T = TypeVar('T')
170-
171-
@contextmanager # built-in plugin can infer a precise type here
172-
def stopwatch(timer: Callable[[], T]) -> Iterator[T]:
173-
...
174-
yield timer()
158+
This hook will be also called for instantiation of classes.
159+
This is a good choice if the return type is too complex
160+
to be expressed by regular python typing.
175161

176162
**get_function_signature_hook** is used to adjust the signature of a function.
177163

@@ -251,31 +237,3 @@ mypy's cache for that module so that it can be rechecked. This hook
251237
should be used to report to mypy any relevant configuration data,
252238
so that mypy knows to recheck the module if the configuration changes.
253239
The hooks should return data encodable as JSON.
254-
255-
Notes about the semantic analyzer
256-
*********************************
257-
258-
Mypy 0.710 introduced a new semantic analyzer, and the old semantic
259-
analyzer was removed in mypy 0.730. Support for the new semantic analyzer
260-
required some changes to existing plugins. Here is a short summary of the
261-
most important changes:
262-
263-
* The order of processing AST nodes is different. Code outside
264-
functions is processed first, and functions and methods are
265-
processed afterwards.
266-
267-
* Each AST node can be processed multiple times to resolve forward
268-
references. The same plugin hook may be called multiple times, so
269-
they need to be idempotent.
270-
271-
* The ``anal_type()`` API method returns ``None`` if some part of
272-
the type is not available yet due to forward references, for example.
273-
274-
* When looking up symbols, you may encounter *placeholder nodes* that
275-
are used for names that haven't been fully processed yet. You'll
276-
generally want to request another semantic analysis iteration by
277-
*deferring* in that case.
278-
279-
See the docstring at the top of
280-
`mypy/plugin.py <https://github.com/python/mypy/blob/master/mypy/plugin.py>`_
281-
for more details.

0 commit comments

Comments
 (0)