Skip to content

Commit 24ad85f

Browse files
authored
Merge branch 'master' into refine-unreachable-checks-against-with
2 parents b667bd1 + 85420a0 commit 24ad85f

Some content is hidden

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

57 files changed

+1510
-449
lines changed

docs/source/getting_started.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ Arguments with default values can be annotated like so:
158158
# 'args' has type 'Tuple[int, ...]' (a tuple of ints)
159159
# 'kwargs' has type 'Dict[str, float]' (a dict of strs to floats)
160160
for arg in args:
161-
print(name)
161+
print(arg)
162162
for key, value in kwargs:
163163
print(key, value)
164164

misc/proper_plugin.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from mypy.plugin import Plugin, FunctionContext
2+
from mypy.types import Type, Instance, CallableType, UnionType, get_proper_type
3+
4+
import os.path
5+
from typing_extensions import Type as typing_Type
6+
from typing import Optional, Callable
7+
8+
FILE_WHITELIST = [
9+
'checker.py',
10+
'checkexpr.py',
11+
'checkmember.py',
12+
'messages.py',
13+
'semanal.py',
14+
'typeanal.py'
15+
]
16+
17+
18+
class ProperTypePlugin(Plugin):
19+
"""
20+
A plugin to ensure that every type is expanded before doing any special-casing.
21+
22+
This solves the problem that we have hundreds of call sites like:
23+
24+
if isinstance(typ, UnionType):
25+
... # special-case union
26+
27+
But after introducing a new type TypeAliasType (and removing immediate expansion)
28+
all these became dangerous because typ may be e.g. an alias to union.
29+
"""
30+
def get_function_hook(self, fullname: str
31+
) -> Optional[Callable[[FunctionContext], Type]]:
32+
if fullname == 'builtins.isinstance':
33+
return isinstance_proper_hook
34+
return None
35+
36+
37+
def isinstance_proper_hook(ctx: FunctionContext) -> Type:
38+
if os.path.split(ctx.api.path)[-1] in FILE_WHITELIST:
39+
return ctx.default_return_type
40+
for arg in ctx.arg_types[0]:
41+
if is_improper_type(arg):
42+
right = get_proper_type(ctx.arg_types[1][0])
43+
if isinstance(right, CallableType) and right.is_type_obj():
44+
if right.type_object().fullname() in ('mypy.types.Type',
45+
'mypy.types.ProperType',
46+
'mypy.types.TypeAliasType'):
47+
# Special case: things like assert isinstance(typ, ProperType) are always OK.
48+
return ctx.default_return_type
49+
if right.type_object().fullname() in ('mypy.types.UnboundType',
50+
'mypy.types.TypeVarType'):
51+
# Special case: these are not valid targets for a type alias and thus safe.
52+
return ctx.default_return_type
53+
ctx.api.fail('Never apply isinstance() to unexpanded types;'
54+
' use mypy.types.get_proper_type() first', ctx.context)
55+
return ctx.default_return_type
56+
57+
58+
def is_improper_type(typ: Type) -> bool:
59+
"""Is this a type that is not a subtype of ProperType?"""
60+
typ = get_proper_type(typ)
61+
if isinstance(typ, Instance):
62+
info = typ.type
63+
return info.has_base('mypy.types.Type') and not info.has_base('mypy.types.ProperType')
64+
if isinstance(typ, UnionType):
65+
return any(is_improper_type(t) for t in typ.items)
66+
return False
67+
68+
69+
def plugin(version: str) -> typing_Type[ProperTypePlugin]:
70+
return ProperTypePlugin

mypy/applytype.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import mypy.subtypes
44
import mypy.sametypes
55
from mypy.expandtype import expand_type
6-
from mypy.types import Type, TypeVarId, TypeVarType, CallableType, AnyType, PartialType
6+
from mypy.types import (
7+
Type, TypeVarId, TypeVarType, CallableType, AnyType, PartialType, get_proper_types
8+
)
79
from mypy.messages import MessageBuilder
810
from mypy.nodes import Context
911

@@ -25,10 +27,10 @@ def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optiona
2527
assert len(tvars) == len(orig_types)
2628
# Check that inferred type variable values are compatible with allowed
2729
# values and bounds. Also, promote subtype values to allowed values.
28-
types = list(orig_types)
30+
types = get_proper_types(orig_types)
2931
for i, type in enumerate(types):
3032
assert not isinstance(type, PartialType), "Internal error: must never apply partial type"
31-
values = callable.variables[i].values
33+
values = get_proper_types(callable.variables[i].values)
3234
if type is None:
3335
continue
3436
if values:

mypy/argmap.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from typing import List, Optional, Sequence, Callable, Set
44

5-
from mypy.types import Type, Instance, TupleType, AnyType, TypeOfAny, TypedDictType
5+
from mypy.types import (
6+
Type, Instance, TupleType, AnyType, TypeOfAny, TypedDictType, get_proper_type
7+
)
68
from mypy import nodes
79

810

@@ -34,7 +36,7 @@ def map_actuals_to_formals(actual_kinds: List[int],
3436
formal_to_actual[fi].append(ai)
3537
elif actual_kind == nodes.ARG_STAR:
3638
# We need to know the actual type to map varargs.
37-
actualt = actual_arg_type(ai)
39+
actualt = get_proper_type(actual_arg_type(ai))
3840
if isinstance(actualt, TupleType):
3941
# A tuple actual maps to a fixed number of formals.
4042
for _ in range(len(actualt.items)):
@@ -65,7 +67,7 @@ def map_actuals_to_formals(actual_kinds: List[int],
6567
formal_to_actual[formal_kinds.index(nodes.ARG_STAR2)].append(ai)
6668
else:
6769
assert actual_kind == nodes.ARG_STAR2
68-
actualt = actual_arg_type(ai)
70+
actualt = get_proper_type(actual_arg_type(ai))
6971
if isinstance(actualt, TypedDictType):
7072
for name, value in actualt.items.items():
7173
if name in formal_names:
@@ -153,6 +155,7 @@ def expand_actual_type(self,
153155
This is supposed to be called for each formal, in order. Call multiple times per
154156
formal if multiple actuals map to a formal.
155157
"""
158+
actual_type = get_proper_type(actual_type)
156159
if actual_kind == nodes.ARG_STAR:
157160
if isinstance(actual_type, Instance):
158161
if actual_type.type.fullname() == 'builtins.list':

mypy/binder.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from typing import Dict, List, Set, Iterator, Union, Optional, Tuple, cast
55
from typing_extensions import DefaultDict
66

7-
from mypy.types import Type, AnyType, PartialType, UnionType, TypeOfAny, NoneType
7+
from mypy.types import (
8+
Type, AnyType, PartialType, UnionType, TypeOfAny, NoneType, get_proper_type
9+
)
810
from mypy.subtypes import is_subtype
911
from mypy.join import join_simple
1012
from mypy.sametypes import is_same_type
@@ -191,7 +193,7 @@ def update_from_options(self, frames: List[Frame]) -> bool:
191193

192194
type = resulting_values[0]
193195
assert type is not None
194-
declaration_type = self.declarations.get(key)
196+
declaration_type = get_proper_type(self.declarations.get(key))
195197
if isinstance(declaration_type, AnyType):
196198
# At this point resulting values can't contain None, see continue above
197199
if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]):
@@ -246,6 +248,9 @@ def assign_type(self, expr: Expression,
246248
type: Type,
247249
declared_type: Optional[Type],
248250
restrict_any: bool = False) -> None:
251+
type = get_proper_type(type)
252+
declared_type = get_proper_type(declared_type)
253+
249254
if self.type_assignments is not None:
250255
# We are in a multiassign from union, defer the actual binding,
251256
# just collect the types.
@@ -270,7 +275,7 @@ def assign_type(self, expr: Expression,
270275
# times?
271276
return
272277

273-
enclosing_type = self.most_recent_enclosing_type(expr, type)
278+
enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type))
274279
if isinstance(enclosing_type, AnyType) and not restrict_any:
275280
# If x is Any and y is int, after x = y we do not infer that x is int.
276281
# This could be changed.
@@ -287,7 +292,8 @@ def assign_type(self, expr: Expression,
287292
elif (isinstance(type, AnyType)
288293
and isinstance(declared_type, UnionType)
289294
and any(isinstance(item, NoneType) for item in declared_type.items)
290-
and isinstance(self.most_recent_enclosing_type(expr, NoneType()), NoneType)):
295+
and isinstance(get_proper_type(self.most_recent_enclosing_type(expr, NoneType())),
296+
NoneType)):
291297
# Replace any Nones in the union type with Any
292298
new_items = [type if isinstance(item, NoneType) else item
293299
for item in declared_type.items]
@@ -320,6 +326,7 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None:
320326
self._cleanse_key(dep)
321327

322328
def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Optional[Type]:
329+
type = get_proper_type(type)
323330
if isinstance(type, AnyType):
324331
return get_declaration(expr)
325332
key = literal_hash(expr)
@@ -412,7 +419,7 @@ def top_frame_context(self) -> Iterator[Frame]:
412419

413420
def get_declaration(expr: BindableExpression) -> Optional[Type]:
414421
if isinstance(expr, RefExpr) and isinstance(expr.node, Var):
415-
type = expr.node.type
422+
type = get_proper_type(expr.node.type)
416423
if not isinstance(type, PartialType):
417424
return type
418425
return None

mypy/build.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
from mypy.renaming import VariableRenameVisitor
5858
from mypy.config_parser import parse_mypy_comments
5959
from mypy.freetree import free_tree
60+
from mypy import errorcodes as codes
6061

6162

6263
# Switch to True to produce debug output related to fine-grained incremental
@@ -2422,14 +2423,16 @@ def module_not_found(manager: BuildManager, line: int, caller_state: State,
24222423
or (manager.options.python_version[0] >= 3
24232424
and moduleinfo.is_py3_std_lib_module(target))):
24242425
errors.report(
2425-
line, 0, "No library stub file for standard library module '{}'".format(target))
2426+
line, 0, "No library stub file for standard library module '{}'".format(target),
2427+
code=codes.IMPORT)
24262428
errors.report(line, 0, stub_msg, severity='note', only_once=True)
24272429
elif moduleinfo.is_third_party_module(target):
2428-
errors.report(line, 0, "No library stub file for module '{}'".format(target))
2430+
errors.report(line, 0, "No library stub file for module '{}'".format(target),
2431+
code=codes.IMPORT)
24292432
errors.report(line, 0, stub_msg, severity='note', only_once=True)
24302433
else:
24312434
note = "See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports"
2432-
errors.report(line, 0, "Cannot find module named '{}'".format(target))
2435+
errors.report(line, 0, "Cannot find module named '{}'".format(target), code=codes.IMPORT)
24332436
errors.report(line, 0, note, severity='note', only_once=True)
24342437
errors.set_import_context(save_import_context)
24352438

0 commit comments

Comments
 (0)