Skip to content

[clean-strict-optional] Clean-up the type checking files #3957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions mypy/applytype.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Dict
from typing import List, Dict, Sequence, Optional

import mypy.subtypes
from mypy.sametypes import is_same_type
Expand All @@ -8,7 +8,7 @@
from mypy.nodes import Context


def apply_generic_arguments(callable: CallableType, types: List[Type],
def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optional[Type]],
msg: MessageBuilder, context: Context) -> CallableType:
"""Apply generic type arguments to a callable type.

Expand All @@ -18,10 +18,10 @@ def apply_generic_arguments(callable: CallableType, types: List[Type],
Note that each type can be None; in this case, it will not be applied.
"""
tvars = callable.variables
assert len(tvars) == len(types)
assert len(tvars) == len(orig_types)
# Check that inferred type variable values are compatible with allowed
# values and bounds. Also, promote subtype values to allowed values.
types = types[:]
types = list(orig_types)
for i, type in enumerate(types):
values = callable.variables[i].values
if values and type:
Expand All @@ -47,8 +47,9 @@ def apply_generic_arguments(callable: CallableType, types: List[Type],
# Create a map from type variable id to target type.
id_to_type = {} # type: Dict[TypeVarId, Type]
for i, tv in enumerate(tvars):
if types[i]:
id_to_type[tv.id] = types[i]
typ = types[i]
if typ:
id_to_type[tv.id] = typ

# Apply arguments to argument types.
arg_types = [expand_type(at, id_to_type) for at in callable.arg_types]
Expand Down
2 changes: 1 addition & 1 deletion mypy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def pop_frame(self, can_skip: bool, fall_through: int) -> Frame:

def assign_type(self, expr: Expression,
type: Type,
declared_type: Type,
declared_type: Optional[Type],
restrict_any: bool = False) -> None:
if not isinstance(expr, BindableTypes):
return None
Expand Down
94 changes: 50 additions & 44 deletions mypy/checker.py

Large diffs are not rendered by default.

123 changes: 72 additions & 51 deletions mypy/checkexpr.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ class B(A): pass

"""
if isinstance(method, Overloaded):
return cast(F, Overloaded([bind_self(c, method) for c in method.items()]))
return cast(F, Overloaded([bind_self(c, original_type) for c in method.items()]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, was that a bug?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I recall fixing it in at some point, but probably only on a local branch.

The reason it hadn't surface (AFAIK) is that it requires an overloaded method with generic self that's also has as bound a supertype of the class, without overlapping the other overload. This ish is a rare combination which is hard to get right anyway, and the checking for the "being a supertype" part is too strict in the case of overload, so I actually struggle to make an example where this is the only error.

from typing import TypeVar, overload

T = TypeVar('T')

class A:
    @overload
    def f(self: T) -> T: return self
    def f(self): return self

reveal_type(A().f())

result:

../tmp.py:6: error: Single overload definition, multiple required
../tmp.py:10: error: Revealed type is 'Overload(def [T] (self: T`9) -> T`9)'

Adding a valid overload is not obvious (to me).

assert isinstance(method, CallableType)
func = method
if not func.arg_types:
Expand Down
4 changes: 2 additions & 2 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Type inference constraints."""

from typing import Iterable, List, Optional
from typing import Iterable, List, Optional, Sequence

from mypy import experiments
from mypy.types import (
Expand Down Expand Up @@ -42,7 +42,7 @@ def __repr__(self) -> str:


def infer_constraints_for_callable(
callee: CallableType, arg_types: List[Optional[Type]], arg_kinds: List[int],
callee: CallableType, arg_types: Sequence[Optional[Type]], arg_kinds: List[int],
formal_to_actual: List[List[int]]) -> List[Constraint]:
"""Infer type variable constraints for a callable and actual arguments.

Expand Down
6 changes: 4 additions & 2 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,8 @@ def visit_JoinedStr(self, n: ast3.JoinedStr) -> Expression:
join_method.set_line(empty_string)
result_expression = CallExpr(join_method,
[strs_to_join],
[ARG_POS])
[ARG_POS],
[None])
return result_expression

# FormattedValue(expr value)
Expand All @@ -902,7 +903,8 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression:
format_method.set_line(format_string)
result_expression = CallExpr(format_method,
[exp],
[ARG_POS])
[ARG_POS],
[None])
return result_expression

# Bytes(bytes s)
Expand Down
4 changes: 2 additions & 2 deletions mypy/infer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Utilities for type argument inference."""

from typing import List, Optional
from typing import List, Optional, Sequence

from mypy.constraints import infer_constraints, infer_constraints_for_callable
from mypy.types import Type, TypeVarId, CallableType
Expand All @@ -9,7 +9,7 @@


def infer_function_type_arguments(callee_type: CallableType,
arg_types: List[Optional[Type]],
arg_types: Sequence[Optional[Type]],
arg_kinds: List[int],
formal_to_actual: List[List[int]],
strict: bool = True) -> List[Optional[Type]]:
Expand Down
8 changes: 4 additions & 4 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def enable_errors(self) -> None:
def is_errors(self) -> bool:
return self.errors.is_errors()

def report(self, msg: str, context: Context, severity: str,
def report(self, msg: str, context: Optional[Context], severity: str,
file: Optional[str] = None, origin: Optional[Context] = None,
offset: int = 0) -> None:
"""Report an error or note (unless disabled)."""
Expand All @@ -175,7 +175,7 @@ def report(self, msg: str, context: Context, severity: str,
msg.strip(), severity=severity, file=file, offset=offset,
origin_line=origin.get_line() if origin else None)

def fail(self, msg: str, context: Context, file: Optional[str] = None,
def fail(self, msg: str, context: Optional[Context], file: Optional[str] = None,
origin: Optional[Context] = None) -> None:
"""Report an error message (unless disabled)."""
self.report(msg, context, 'error', file=file, origin=origin)
Expand Down Expand Up @@ -641,7 +641,7 @@ def invalid_index_type(self, index_type: Type, expected_type: Type, base_str: st
self.format(index_type), base_str, self.format(expected_type)), context)

def too_few_arguments(self, callee: CallableType, context: Context,
argument_names: List[str]) -> None:
argument_names: Optional[Sequence[Optional[str]]]) -> None:
if (argument_names is not None and not all(k is None for k in argument_names)
and len(argument_names) >= 1):
diff = [k for k in callee.arg_names if k not in argument_names]
Expand Down Expand Up @@ -695,7 +695,7 @@ def duplicate_argument_value(self, callee: CallableType, index: int,
format(capitalize(callable_name(callee)),
callee.arg_names[index]), context)

def does_not_return_value(self, callee_type: Type, context: Context) -> None:
def does_not_return_value(self, callee_type: Optional[Type], context: Context) -> None:
"""Report an error about use of an unusable type."""
name = None # type: Optional[str]
if isinstance(callee_type, FunctionLike):
Expand Down
2 changes: 1 addition & 1 deletion mypy/myunit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def __init__(self, name: str, suite: 'Optional[Suite]' = None,
self.name = name
self.suite = suite
self.old_cwd = None # type: Optional[str]
self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory]
self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory[str]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally off-topic: this would be a case where if you converted it to PEP 526 style (tmpdir: Optional[tempfile.TemporaryDirectory[str]] at the class level) it would fail at runtime, because the real tempfile module doesn't declare TemporaryDirectory as generic -- but Łukasz's PEP 563 would save the day...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a good case for PEP 563.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has surfaced in #5087 during self-check:

mypy/test/data.py:232: error: "TemporaryDirectory" expects no type arguments, but 1 given

How did that pass before?


def run(self) -> None:
if self.func:
Expand Down
6 changes: 3 additions & 3 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ def __init__(self,
callee: Expression,
args: List[Expression],
arg_kinds: List[int],
arg_names: Optional[List[Optional[str]]] = None,
arg_names: List[Optional[str]],
analyzed: Optional[Expression] = None) -> None:
if not arg_names:
arg_names = [None] * len(args)
Expand Down Expand Up @@ -1731,13 +1731,13 @@ class TypeAliasExpr(Expression):
type = None # type: mypy.types.Type
# Simple fallback type for aliases that are invalid in runtime expressions
# (for example Union, Tuple, Callable).
fallback = None # type: Optional[mypy.types.Type]
fallback = None # type: mypy.types.Type
# This type alias is subscripted in a runtime expression like Alias[int](42)
# (not in a type context like type annotation or base class).
in_runtime = False # type: bool

def __init__(self, type: 'mypy.types.Type', tvars: List[str],
fallback: 'Optional[mypy.types.Type]' = None, in_runtime: bool = False) -> None:
fallback: 'mypy.types.Type', in_runtime: bool = False) -> None:
self.type = type
self.fallback = fallback
self.in_runtime = in_runtime
Expand Down
3 changes: 2 additions & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,8 @@ def copy_modified(self, *, fallback: Optional[Instance] = None,
items = self.items
return TupleType(items, fallback, self.line, self.column)

def slice(self, begin: int, stride: int, end: int) -> 'TupleType':
def slice(self, begin: Optional[int], stride: Optional[int],
end: Optional[int]) -> 'TupleType':
return TupleType(self.items[begin:end:stride], self.fallback,
self.line, self.column, self.implicit)

Expand Down
17 changes: 1 addition & 16 deletions mypy_self_check.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,6 @@ disallow_any = generics, unimported
warn_redundant_casts = True
warn_unused_ignores = True

; historical exceptions
[mypy-mypy.binder]
disallow_any = unimported

[mypy-mypy.checker]
strict_optional = False

[mypy-mypy.checkexpr]
strict_optional = False

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

[mypy-mypy.myunit]
disallow_any = unimported

[mypy-mypy.nodes]
disallow_any = unimported