Skip to content

Commit

Permalink
Last strict optional fixes (#4070)
Browse files Browse the repository at this point in the history
This makes mypy completely --strict-optional clean.

Fixes #1955.
  • Loading branch information
ilevkivskyi authored and JukkaL committed Oct 24, 2017
1 parent 272d0c4 commit 936ceac
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 112 deletions.
12 changes: 5 additions & 7 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1814,9 +1814,9 @@ def check_multi_assignment_from_iterable(self, lvalues: List[Lvalue], rvalue_typ
def check_lvalue(self, lvalue: Lvalue) -> Tuple[Optional[Type],
Optional[IndexExpr],
Optional[Var]]:
lvalue_type = None # type: Optional[Type]
index_lvalue = None # type: Optional[IndexExpr]
inferred = None # type: Optional[Var]
lvalue_type = None
index_lvalue = None
inferred = None

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

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

def push_type_map(self, type_map: Optional[Dict[Expression, Type]]) -> None:
def push_type_map(self, type_map: 'TypeMap') -> None:
if type_map is None:
self.binder.unreachable()
else:
Expand Down
8 changes: 4 additions & 4 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,8 +721,7 @@ def infer_arg_types_in_context2(
Returns the inferred types of *actual arguments*.
"""
dummy = None # type: Any
res = [dummy] * len(args) # type: List[Type]
res = [None] * len(args) # type: List[Optional[Type]]

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

def infer_function_type_arguments_using_context(
self, callable: CallableType, error_context: Context) -> CallableType:
Expand Down Expand Up @@ -2648,7 +2648,7 @@ def is_async_def(t: Type) -> bool:
def map_actuals_to_formals(caller_kinds: List[int],
caller_names: Optional[Sequence[Optional[str]]],
callee_kinds: List[int],
callee_names: List[Optional[str]],
callee_names: Sequence[Optional[str]],
caller_arg_type: Callable[[int],
Type]) -> List[List[int]]:
"""Calculate mapping between actual (caller) args and formals.
Expand Down
8 changes: 4 additions & 4 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from abc import abstractmethod
from collections import OrderedDict
from typing import (
Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional, Callable,
Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional, Callable, Sequence,
)

import mypy.strconv
Expand Down Expand Up @@ -1786,11 +1786,11 @@ class NewTypeExpr(Expression):
"""NewType expression NewType(...)."""
name = None # type: str
# The base type (the second argument to NewType)
old_type = None # type: mypy.types.Type
old_type = None # type: Optional[mypy.types.Type]
# The synthesized class representing the new type (inherits old_type)
info = None # type: Optional[TypeInfo]

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

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


def check_arg_names(names: List[Optional[str]], nodes: List[T], fail: Callable[[str, T], None],
def check_arg_names(names: Sequence[Optional[str]], nodes: List[T], fail: Callable[[str, T], None],
description: str = 'function definition') -> None:
seen_names = set() # type: Set[Optional[str]]
for name, node in zip(names, nodes):
Expand Down
165 changes: 94 additions & 71 deletions mypy/semanal.py

Large diffs are not rendered by default.

18 changes: 13 additions & 5 deletions mypy/semanal_pass3.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def visit_decorator(self, dec: Decorator) -> None:
for expr in dec.decorators:
preserve_type = False
if isinstance(expr, RefExpr) and isinstance(expr.node, FuncDef):
if is_identity_signature(expr.node.type):
if expr.node.type and is_identity_signature(expr.node.type):
preserve_type = True
if not preserve_type:
decorator_preserves_type = False
Expand Down Expand Up @@ -246,31 +246,38 @@ def perform_transform(self, node: Union[Node, SymbolTableNode],
transform: Callable[[Type], Type]) -> None:
"""Apply transform to all types associated with node."""
if isinstance(node, ForStmt):
node.index_type = transform(node.index_type)
if node.index_type:
node.index_type = transform(node.index_type)
self.transform_types_in_lvalue(node.index, transform)
if isinstance(node, WithStmt):
node.target_type = transform(node.target_type)
if node.target_type:
node.target_type = transform(node.target_type)
for n in node.target:
if isinstance(n, NameExpr) and isinstance(n.node, Var) and n.node.type:
n.node.type = transform(n.node.type)
if isinstance(node, (FuncDef, CastExpr, AssignmentStmt, TypeAliasExpr, Var)):
assert node.type, "Scheduled patch for non-existent type"
node.type = transform(node.type)
if isinstance(node, NewTypeExpr):
assert node.old_type, "Scheduled patch for non-existent type"
node.old_type = transform(node.old_type)
if isinstance(node, TypeVarExpr):
if node.upper_bound:
node.upper_bound = transform(node.upper_bound)
if node.values:
node.values = [transform(v) for v in node.values]
if isinstance(node, TypedDictExpr):
assert node.info.typeddict_type, "Scheduled patch for non-existent type"
node.info.typeddict_type = cast(TypedDictType,
transform(node.info.typeddict_type))
if isinstance(node, NamedTupleExpr):
assert node.info.tuple_type, "Scheduled patch for non-existent type"
node.info.tuple_type = cast(TupleType,
transform(node.info.tuple_type))
if isinstance(node, TypeApplication):
node.types = [transform(t) for t in node.types]
if isinstance(node, SymbolTableNode):
assert node.type_override, "Scheduled patch for non-existent type"
node.type_override = transform(node.type_override)
if isinstance(node, TypeInfo):
for tvar in node.defn.type_vars:
Expand All @@ -296,7 +303,8 @@ def transform_types_in_lvalue(self, lvalue: Lvalue,
if isinstance(lvalue, RefExpr):
if isinstance(lvalue.node, Var):
var = lvalue.node
var.type = transform(var.type)
if var.type:
var.type = transform(var.type)
elif isinstance(lvalue, TupleExpr):
for item in lvalue.items:
self.transform_types_in_lvalue(item, transform)
Expand Down Expand Up @@ -355,7 +363,7 @@ def fail(self, msg: str, ctx: Context, *, blocker: bool = False) -> None:
def fail_blocker(self, msg: str, ctx: Context) -> None:
self.fail(msg, ctx, blocker=True)

def builtin_type(self, name: str, args: List[Type] = None) -> Instance:
def builtin_type(self, name: str, args: Optional[List[Type]] = None) -> Instance:
names = self.modules['builtins']
sym = names.names[name]
node = sym.node
Expand Down
20 changes: 13 additions & 7 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@


def analyze_type_alias(node: Expression,
lookup_func: Callable[[str, Context], SymbolTableNode],
lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
lookup_fqn_func: Callable[[str], SymbolTableNode],
tvar_scope: TypeVarScope,
fail_func: Callable[[str, Context], None],
Expand Down Expand Up @@ -144,7 +144,7 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], AnalyzerPluginInterface):
global_scope = True # type: bool

def __init__(self,
lookup_func: Callable[[str, Context], SymbolTableNode],
lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
lookup_fqn_func: Callable[[str], SymbolTableNode],
tvar_scope: Optional[TypeVarScope],
fail_func: Callable[[str, Context], None],
Expand Down Expand Up @@ -555,7 +555,9 @@ def bind_function_type_variables(self,
return [] # We are in third pass, nothing new here
if fun_type.variables:
for var in fun_type.variables:
var_expr = self.lookup(var.name, var).node
var_node = self.lookup(var.name, var)
assert var_node, "Binding for function type variable not found within function"
var_expr = var_node.node
assert isinstance(var_expr, TypeVarExpr)
self.tvar_scope.bind(var.name, var_expr)
return fun_type.variables
Expand All @@ -575,8 +577,12 @@ def bind_function_type_variables(self,
return defs

def is_defined_type_var(self, tvar: str, context: Context) -> bool:
return (self.tvar_scope is not None and
self.tvar_scope.get_binding(self.lookup(tvar, context)) is not None)
if self.tvar_scope is None:
return False
tvar_node = self.lookup(tvar, context)
if not tvar_node:
return False
return self.tvar_scope.get_binding(tvar_node) is not None

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

def __init__(self,
lookup_func: Callable[[str, Context], SymbolTableNode],
lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
lookup_fqn_func: Callable[[str], SymbolTableNode],
fail_func: Callable[[str, Context], None],
note_func: Callable[[str, Context], None],
Expand Down Expand Up @@ -878,7 +884,7 @@ def flatten_tvars(ll: Iterable[List[T]]) -> List[T]:
class TypeVariableQuery(TypeQuery[TypeVarList]):

def __init__(self,
lookup: Callable[[str, Context], SymbolTableNode],
lookup: Callable[[str, Context], Optional[SymbolTableNode]],
scope: 'TypeVarScope',
*,
include_callables: bool = True,
Expand Down
6 changes: 3 additions & 3 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from collections import OrderedDict
from typing import (
Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Optional, Union, Iterable, NamedTuple,
Callable
Callable, Sequence
)

import mypy.nodes
Expand Down Expand Up @@ -646,7 +646,7 @@ class CallableType(FunctionLike):
def __init__(self,
arg_types: List[Type],
arg_kinds: List[int],
arg_names: List[Optional[str]],
arg_names: Sequence[Optional[str]],
ret_type: Type,
fallback: Instance,
name: Optional[str] = None,
Expand All @@ -667,7 +667,7 @@ def __init__(self,
assert not any(tp is None for tp in arg_types), "No annotation must be Any, not None"
self.arg_types = arg_types
self.arg_kinds = arg_kinds
self.arg_names = arg_names
self.arg_names = list(arg_names)
self.min_args = arg_kinds.count(ARG_POS)
self.is_var_arg = ARG_STAR in arg_kinds
self.is_kw_arg = ARG_STAR2 in arg_kinds
Expand Down
10 changes: 0 additions & 10 deletions mypy_self_check.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@ warn_redundant_casts = True
warn_unused_ignores = True
warn_unused_configs = True

# historical exception
[mypy-mypy.semanal]
strict_optional = False

[mypy-mypy.semanal_pass1]
strict_optional = False

[mypy-mypy.semanal_pass3]
strict_optional = False

# needs py2 compatibility
[mypy-mypy.test.testextensions]
disallow_untyped_defs = False
5 changes: 4 additions & 1 deletion test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -2904,10 +2904,13 @@ class B(object, A): # E: Cannot determine consistent method resolution order (MR
__iter__ = readlines

[case testDynamicMetaclass]

class C(metaclass=int()): # E: Dynamic metaclass not supported for 'C'
pass

[case testDynamicMetaclassCrash]
class C(metaclass=int().x): # E: Dynamic metaclass not supported for 'C'
pass

[case testVariableSubclass]
class A:
a = 1 # type: int
Expand Down

0 comments on commit 936ceac

Please sign in to comment.