Skip to content

Commit

Permalink
Tighten FakeInfo and fix crashes in --quick mode (#3304)
Browse files Browse the repository at this point in the history
The idea was proposed by Jukka in #3285 (comment)

This could (hopefully) fix #3281 for real.
Also fixes #3278
  • Loading branch information
ilevkivskyi authored and gvanrossum committed May 11, 2017
1 parent 784c72d commit d2638b1
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 6 deletions.
26 changes: 23 additions & 3 deletions mypy/fixup.py
Expand Up @@ -5,7 +5,7 @@
from mypy.nodes import (
MypyFile, SymbolNode, SymbolTable, SymbolTableNode,
TypeInfo, FuncDef, OverloadedFuncDef, Decorator, Var,
TypeVarExpr, ClassDef,
TypeVarExpr, ClassDef, Block,
LDEF, MDEF, GDEF
)
from mypy.types import (
Expand Down Expand Up @@ -90,6 +90,11 @@ def visit_symbol_table(self, symtab: SymbolTable) -> None:
value.type_override = stnode.type_override
elif not self.quick_and_dirty:
assert stnode is not None, "Could not find cross-ref %s" % (cross_ref,)
else:
# We have a missing crossref in quick mode, need to put something
value.node = stale_info()
if value.type_override is not None:
value.type_override.accept(self.type_fixer)
else:
if isinstance(value.node, TypeInfo):
# TypeInfo has no accept(). TODO: Add it?
Expand Down Expand Up @@ -162,6 +167,10 @@ def visit_instance(self, inst: Instance) -> None:
for base in inst.type.bases:
if base.type is NOT_READY:
base.accept(self)
else:
# Looks like a missing TypeInfo in quick mode, put something there
assert self.quick_and_dirty, "Should never get here in normal mode"
inst.type = stale_info()
for a in inst.args:
a.accept(self)

Expand Down Expand Up @@ -267,12 +276,23 @@ def lookup_qualified_stnode(modules: Dict[str, MypyFile], name: str,
return None
key = rest.pop()
if key not in names:
if not quick_and_dirty:
assert key in names, "Cannot find %s for %s" % (key, name)
return None
elif not quick_and_dirty:
assert key in names, "Cannot find %s for %s" % (key, name)
stnode = names[key]
if not rest:
return stnode
node = stnode.node
assert isinstance(node, TypeInfo)
names = node.names


def stale_info() -> TypeInfo:
suggestion = "<stale cache: consider running mypy without --quick>"
dummy_def = ClassDef(suggestion, Block([]))
dummy_def.fullname = suggestion

info = TypeInfo(SymbolTable(), dummy_def, "<stale>")
info.mro = [info]
info.bases = []
return info
5 changes: 4 additions & 1 deletion mypy/nodes.py
Expand Up @@ -2229,7 +2229,10 @@ class FakeInfo(TypeInfo):
# pass cleanly.
# 2. If NOT_READY value is accidentally used somewhere, it will be obvious where the value
# is from, whereas a 'None' value could come from anywhere.
def __getattr__(self, attr: str) -> None:
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass

def __getattribute__(self, attr: str) -> None:
raise AssertionError('De-serialization failure: TypeInfo not fixed')


Expand Down
6 changes: 4 additions & 2 deletions mypy/types.py
Expand Up @@ -401,7 +401,7 @@ class Instance(Type):

def __init__(self, typ: mypy.nodes.TypeInfo, args: List[Type],
line: int = -1, column: int = -1, erased: bool = False) -> None:
assert(typ is None or typ.fullname() not in ["builtins.Any", "typing.Any"])
assert(typ is NOT_READY or typ.fullname() not in ["builtins.Any", "typing.Any"])
self.type = typ
self.args = args
self.erased = erased
Expand Down Expand Up @@ -1796,8 +1796,10 @@ def union_items(typ: Type) -> List[Type]:
return [typ]


names = globals().copy()
names.pop('NOT_READY', None)
deserialize_map = {
key: obj.deserialize # type: ignore
for key, obj in globals().items()
for key, obj in names.items()
if isinstance(obj, type) and issubclass(obj, Type) and obj is not Type
}

0 comments on commit d2638b1

Please sign in to comment.