Skip to content

Commit 936ceac

Browse files
ilevkivskyiJukkaL
authored andcommitted
Last strict optional fixes (#4070)
This makes mypy completely --strict-optional clean. Fixes #1955.
1 parent 272d0c4 commit 936ceac

File tree

9 files changed

+140
-112
lines changed

9 files changed

+140
-112
lines changed

mypy/checker.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -1814,9 +1814,9 @@ def check_multi_assignment_from_iterable(self, lvalues: List[Lvalue], rvalue_typ
18141814
def check_lvalue(self, lvalue: Lvalue) -> Tuple[Optional[Type],
18151815
Optional[IndexExpr],
18161816
Optional[Var]]:
1817-
lvalue_type = None # type: Optional[Type]
1818-
index_lvalue = None # type: Optional[IndexExpr]
1819-
inferred = None # type: Optional[Var]
1817+
lvalue_type = None
1818+
index_lvalue = None
1819+
inferred = None
18201820

18211821
if self.is_definition(lvalue):
18221822
if isinstance(lvalue, NameExpr):
@@ -2781,12 +2781,10 @@ def iterable_item_type(self, instance: Instance) -> Type:
27812781
def function_type(self, func: FuncBase) -> FunctionLike:
27822782
return function_type(func, self.named_type('builtins.function'))
27832783

2784-
# TODO: These next two functions should refer to TypeMap below
2785-
def find_isinstance_check(self, n: Expression) -> Tuple[Optional[Dict[Expression, Type]],
2786-
Optional[Dict[Expression, Type]]]:
2784+
def find_isinstance_check(self, n: Expression) -> 'Tuple[TypeMap, TypeMap]':
27872785
return find_isinstance_check(n, self.type_map)
27882786

2789-
def push_type_map(self, type_map: Optional[Dict[Expression, Type]]) -> None:
2787+
def push_type_map(self, type_map: 'TypeMap') -> None:
27902788
if type_map is None:
27912789
self.binder.unreachable()
27922790
else:

mypy/checkexpr.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -721,8 +721,7 @@ def infer_arg_types_in_context2(
721721
722722
Returns the inferred types of *actual arguments*.
723723
"""
724-
dummy = None # type: Any
725-
res = [dummy] * len(args) # type: List[Type]
724+
res = [None] * len(args) # type: List[Optional[Type]]
726725

727726
for i, actuals in enumerate(formal_to_actual):
728727
for ai in actuals:
@@ -733,7 +732,8 @@ def infer_arg_types_in_context2(
733732
for i, t in enumerate(res):
734733
if not t:
735734
res[i] = self.accept(args[i])
736-
return res
735+
assert all(tp is not None for tp in res)
736+
return cast(List[Type], res)
737737

738738
def infer_function_type_arguments_using_context(
739739
self, callable: CallableType, error_context: Context) -> CallableType:
@@ -2648,7 +2648,7 @@ def is_async_def(t: Type) -> bool:
26482648
def map_actuals_to_formals(caller_kinds: List[int],
26492649
caller_names: Optional[Sequence[Optional[str]]],
26502650
callee_kinds: List[int],
2651-
callee_names: List[Optional[str]],
2651+
callee_names: Sequence[Optional[str]],
26522652
caller_arg_type: Callable[[int],
26532653
Type]) -> List[List[int]]:
26542654
"""Calculate mapping between actual (caller) args and formals.

mypy/nodes.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from abc import abstractmethod
55
from collections import OrderedDict
66
from typing import (
7-
Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional, Callable,
7+
Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional, Callable, Sequence,
88
)
99

1010
import mypy.strconv
@@ -1786,11 +1786,11 @@ class NewTypeExpr(Expression):
17861786
"""NewType expression NewType(...)."""
17871787
name = None # type: str
17881788
# The base type (the second argument to NewType)
1789-
old_type = None # type: mypy.types.Type
1789+
old_type = None # type: Optional[mypy.types.Type]
17901790
# The synthesized class representing the new type (inherits old_type)
17911791
info = None # type: Optional[TypeInfo]
17921792

1793-
def __init__(self, name: str, old_type: 'mypy.types.Type', line: int) -> None:
1793+
def __init__(self, name: str, old_type: 'Optional[mypy.types.Type]', line: int) -> None:
17941794
self.name = name
17951795
self.old_type = old_type
17961796

@@ -2552,7 +2552,7 @@ def check_arg_kinds(arg_kinds: List[int], nodes: List[T], fail: Callable[[str, T
25522552
is_kw_arg = True
25532553

25542554

2555-
def check_arg_names(names: List[Optional[str]], nodes: List[T], fail: Callable[[str, T], None],
2555+
def check_arg_names(names: Sequence[Optional[str]], nodes: List[T], fail: Callable[[str, T], None],
25562556
description: str = 'function definition') -> None:
25572557
seen_names = set() # type: Set[Optional[str]]
25582558
for name, node in zip(names, nodes):

mypy/semanal.py

+94-71
Large diffs are not rendered by default.

mypy/semanal_pass3.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def visit_decorator(self, dec: Decorator) -> None:
152152
for expr in dec.decorators:
153153
preserve_type = False
154154
if isinstance(expr, RefExpr) and isinstance(expr.node, FuncDef):
155-
if is_identity_signature(expr.node.type):
155+
if expr.node.type and is_identity_signature(expr.node.type):
156156
preserve_type = True
157157
if not preserve_type:
158158
decorator_preserves_type = False
@@ -246,31 +246,38 @@ def perform_transform(self, node: Union[Node, SymbolTableNode],
246246
transform: Callable[[Type], Type]) -> None:
247247
"""Apply transform to all types associated with node."""
248248
if isinstance(node, ForStmt):
249-
node.index_type = transform(node.index_type)
249+
if node.index_type:
250+
node.index_type = transform(node.index_type)
250251
self.transform_types_in_lvalue(node.index, transform)
251252
if isinstance(node, WithStmt):
252-
node.target_type = transform(node.target_type)
253+
if node.target_type:
254+
node.target_type = transform(node.target_type)
253255
for n in node.target:
254256
if isinstance(n, NameExpr) and isinstance(n.node, Var) and n.node.type:
255257
n.node.type = transform(n.node.type)
256258
if isinstance(node, (FuncDef, CastExpr, AssignmentStmt, TypeAliasExpr, Var)):
259+
assert node.type, "Scheduled patch for non-existent type"
257260
node.type = transform(node.type)
258261
if isinstance(node, NewTypeExpr):
262+
assert node.old_type, "Scheduled patch for non-existent type"
259263
node.old_type = transform(node.old_type)
260264
if isinstance(node, TypeVarExpr):
261265
if node.upper_bound:
262266
node.upper_bound = transform(node.upper_bound)
263267
if node.values:
264268
node.values = [transform(v) for v in node.values]
265269
if isinstance(node, TypedDictExpr):
270+
assert node.info.typeddict_type, "Scheduled patch for non-existent type"
266271
node.info.typeddict_type = cast(TypedDictType,
267272
transform(node.info.typeddict_type))
268273
if isinstance(node, NamedTupleExpr):
274+
assert node.info.tuple_type, "Scheduled patch for non-existent type"
269275
node.info.tuple_type = cast(TupleType,
270276
transform(node.info.tuple_type))
271277
if isinstance(node, TypeApplication):
272278
node.types = [transform(t) for t in node.types]
273279
if isinstance(node, SymbolTableNode):
280+
assert node.type_override, "Scheduled patch for non-existent type"
274281
node.type_override = transform(node.type_override)
275282
if isinstance(node, TypeInfo):
276283
for tvar in node.defn.type_vars:
@@ -296,7 +303,8 @@ def transform_types_in_lvalue(self, lvalue: Lvalue,
296303
if isinstance(lvalue, RefExpr):
297304
if isinstance(lvalue.node, Var):
298305
var = lvalue.node
299-
var.type = transform(var.type)
306+
if var.type:
307+
var.type = transform(var.type)
300308
elif isinstance(lvalue, TupleExpr):
301309
for item in lvalue.items:
302310
self.transform_types_in_lvalue(item, transform)
@@ -355,7 +363,7 @@ def fail(self, msg: str, ctx: Context, *, blocker: bool = False) -> None:
355363
def fail_blocker(self, msg: str, ctx: Context) -> None:
356364
self.fail(msg, ctx, blocker=True)
357365

358-
def builtin_type(self, name: str, args: List[Type] = None) -> Instance:
366+
def builtin_type(self, name: str, args: Optional[List[Type]] = None) -> Instance:
359367
names = self.modules['builtins']
360368
sym = names.names[name]
361369
node = sym.node

mypy/typeanal.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353

5454

5555
def analyze_type_alias(node: Expression,
56-
lookup_func: Callable[[str, Context], SymbolTableNode],
56+
lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
5757
lookup_fqn_func: Callable[[str], SymbolTableNode],
5858
tvar_scope: TypeVarScope,
5959
fail_func: Callable[[str, Context], None],
@@ -144,7 +144,7 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], AnalyzerPluginInterface):
144144
global_scope = True # type: bool
145145

146146
def __init__(self,
147-
lookup_func: Callable[[str, Context], SymbolTableNode],
147+
lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
148148
lookup_fqn_func: Callable[[str], SymbolTableNode],
149149
tvar_scope: Optional[TypeVarScope],
150150
fail_func: Callable[[str, Context], None],
@@ -555,7 +555,9 @@ def bind_function_type_variables(self,
555555
return [] # We are in third pass, nothing new here
556556
if fun_type.variables:
557557
for var in fun_type.variables:
558-
var_expr = self.lookup(var.name, var).node
558+
var_node = self.lookup(var.name, var)
559+
assert var_node, "Binding for function type variable not found within function"
560+
var_expr = var_node.node
559561
assert isinstance(var_expr, TypeVarExpr)
560562
self.tvar_scope.bind(var.name, var_expr)
561563
return fun_type.variables
@@ -575,8 +577,12 @@ def bind_function_type_variables(self,
575577
return defs
576578

577579
def is_defined_type_var(self, tvar: str, context: Context) -> bool:
578-
return (self.tvar_scope is not None and
579-
self.tvar_scope.get_binding(self.lookup(tvar, context)) is not None)
580+
if self.tvar_scope is None:
581+
return False
582+
tvar_node = self.lookup(tvar, context)
583+
if not tvar_node:
584+
return False
585+
return self.tvar_scope.get_binding(tvar_node) is not None
580586

581587
def anal_array(self, a: List[Type], nested: bool = True) -> List[Type]:
582588
res = [] # type: List[Type]
@@ -638,7 +644,7 @@ class TypeAnalyserPass3(TypeVisitor[None]):
638644
"""
639645

640646
def __init__(self,
641-
lookup_func: Callable[[str, Context], SymbolTableNode],
647+
lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
642648
lookup_fqn_func: Callable[[str], SymbolTableNode],
643649
fail_func: Callable[[str, Context], None],
644650
note_func: Callable[[str, Context], None],
@@ -878,7 +884,7 @@ def flatten_tvars(ll: Iterable[List[T]]) -> List[T]:
878884
class TypeVariableQuery(TypeQuery[TypeVarList]):
879885

880886
def __init__(self,
881-
lookup: Callable[[str, Context], SymbolTableNode],
887+
lookup: Callable[[str, Context], Optional[SymbolTableNode]],
882888
scope: 'TypeVarScope',
883889
*,
884890
include_callables: bool = True,

mypy/types.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from collections import OrderedDict
66
from typing import (
77
Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Optional, Union, Iterable, NamedTuple,
8-
Callable
8+
Callable, Sequence
99
)
1010

1111
import mypy.nodes
@@ -646,7 +646,7 @@ class CallableType(FunctionLike):
646646
def __init__(self,
647647
arg_types: List[Type],
648648
arg_kinds: List[int],
649-
arg_names: List[Optional[str]],
649+
arg_names: Sequence[Optional[str]],
650650
ret_type: Type,
651651
fallback: Instance,
652652
name: Optional[str] = None,
@@ -667,7 +667,7 @@ def __init__(self,
667667
assert not any(tp is None for tp in arg_types), "No annotation must be Any, not None"
668668
self.arg_types = arg_types
669669
self.arg_kinds = arg_kinds
670-
self.arg_names = arg_names
670+
self.arg_names = list(arg_names)
671671
self.min_args = arg_kinds.count(ARG_POS)
672672
self.is_var_arg = ARG_STAR in arg_kinds
673673
self.is_kw_arg = ARG_STAR2 in arg_kinds

mypy_self_check.ini

-10
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,6 @@ warn_redundant_casts = True
99
warn_unused_ignores = True
1010
warn_unused_configs = True
1111

12-
# historical exception
13-
[mypy-mypy.semanal]
14-
strict_optional = False
15-
16-
[mypy-mypy.semanal_pass1]
17-
strict_optional = False
18-
19-
[mypy-mypy.semanal_pass3]
20-
strict_optional = False
21-
2212
# needs py2 compatibility
2313
[mypy-mypy.test.testextensions]
2414
disallow_untyped_defs = False

test-data/unit/check-classes.test

+4-1
Original file line numberDiff line numberDiff line change
@@ -2904,10 +2904,13 @@ class B(object, A): # E: Cannot determine consistent method resolution order (MR
29042904
__iter__ = readlines
29052905

29062906
[case testDynamicMetaclass]
2907-
29082907
class C(metaclass=int()): # E: Dynamic metaclass not supported for 'C'
29092908
pass
29102909

2910+
[case testDynamicMetaclassCrash]
2911+
class C(metaclass=int().x): # E: Dynamic metaclass not supported for 'C'
2912+
pass
2913+
29112914
[case testVariableSubclass]
29122915
class A:
29132916
a = 1 # type: int

0 commit comments

Comments
 (0)