-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
[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
[clean-strict-optional] Clean-up the type checking files #3957
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually looks great, but I have a bit of a preference for avoiding variable annotations if mypy can figure them out, and I think I found a whole bunch of them. There's one that looks suspicious though, I fear there's some kind of bug in mypy.
mypy/checker.py
Outdated
@@ -610,7 +610,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: str) -> None: | |||
# function. In the first case, set up another reference with the | |||
# precise type. | |||
if isinstance(item, FuncDef): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just begging to be combined with the following if
-- I think the whole thing can be collapsed into
if isinstance(item, FuncDef):
fdef = item
# Check if __init__ has an invalid, non-None return type.
<and the rest of that block>
mypy/checker.py
Outdated
self.check_getattr_method(typ, defn, name) | ||
elif name == '__setattr__': | ||
self.check_setattr_method(typ, defn) | ||
if name: # Special method names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Insert a blank line before this block, and another one before the next block at the same level.
mypy/checker.py
Outdated
@@ -1454,7 +1456,7 @@ def check_compatibility_super(self, lvalue: NameExpr, lvalue_type: Optional[Type | |||
# lvalue had a type defined; this is handled by other | |||
# parts, and all we have to worry about in that case is | |||
# that lvalue is compatible with the base class. | |||
compare_node = None # type: Node | |||
compare_node = None # type: Optional[Node] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This annotation seems unnecessary (partial types do their thing here).
mypy/checkexpr.py
Outdated
@@ -217,6 +218,7 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: | |||
elif typ.node.is_newtype: | |||
self.msg.fail(messages.CANNOT_ISINSTANCE_NEWTYPE, e) | |||
self.try_infer_partial_type(e) | |||
type_context = None # type: Optional[CallableType] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And again...
mypy/checkexpr.py
Outdated
@@ -201,11 +201,12 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: | |||
and len(e.args) == 2): | |||
for typ in mypy.checker.flatten(e.args[1]): | |||
if isinstance(typ, NameExpr): | |||
node = None # type: Optional[SymbolTableNode] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, partial types will do their thing here.
mypy/checkexpr.py
Outdated
stride = None # type:int | ||
begin = None # type: Optional[int] | ||
end = None # type: Optional[int] | ||
stride = None # type: Optional[int] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These three # type
annotations seem unnecessary too...
mypy/checkexpr.py
Outdated
@@ -1943,6 +1960,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: | |||
vtdef = TypeVarDef('VT', -2, [], self.object_type()) | |||
kt = TypeVarType(ktdef) | |||
vt = TypeVarType(vtdef) | |||
rv = None # type: Optional[Type] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again.
@@ -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()])) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
@@ -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]] |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
mypy_self_check.ini
Outdated
[mypy-mypy.checkexpr] | ||
strict_optional = False | ||
|
||
; historical exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer #
as comment character in .ini files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LG now. We need to file and investigate (in that order!) the alleged per-file strict-optional bug.
Also, the AppVeyor build failed building some xml thing -- maybe it's a flake and a retry will pass?
(I've restarted the AppVeyor build, we'll see how it fares.)
|
No, still the same failure. :-( |
The issue is there is a new lxml release and there are not Windows wheels out yet, as IIRC they are provided by a third party. For now we should pin to lxml 3.8. I'll make a PR. |
OK, all tests pass now. |
This makes another major piece of mypy
--strict-optional
clean: the type checking files.After this PR there will be only single exception left
semanal.py
.