Skip to content

Commit

Permalink
Fix daemon crash caused by deleted submodule (#16370)
Browse files Browse the repository at this point in the history
If a submodule has been deleted while using a fine-grained cache, the
daemon could crash during fixup, since there could be a symbol table
entry in a parent package that would appear to refer to itself. Handle
the case by adding a placeholder symbol table entry instead. Eventually
the parent package will be reprocessed and the symbol table will be
completed.
  • Loading branch information
JukkaL authored Oct 30, 2023
1 parent ad0e183 commit 5624f40
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 2 deletions.
19 changes: 17 additions & 2 deletions mypy/fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,23 @@ def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None:
cross_ref, self.modules, raise_on_missing=not self.allow_missing
)
if stnode is not None:
assert stnode.node is not None, (table_fullname + "." + key, cross_ref)
value.node = stnode.node
if stnode is value:
# The node seems to refer to itself, which can mean that
# the target is a deleted submodule of the current module,
# and thus lookup falls back to the symbol table of the parent
# package. Here's how this may happen:
#
# pkg/__init__.py:
# from pkg import sub
#
# Now if pkg.sub is deleted, the pkg.sub symbol table entry
# appears to refer to itself. Replace the entry with a
# placeholder to avoid a crash. We can't delete the entry,
# as it would stop dependency propagation.
value.node = Var(key + "@deleted")
else:
assert stnode.node is not None, (table_fullname + "." + key, cross_ref)
value.node = stnode.node
elif not self.allow_missing:
assert False, f"Could not find cross-ref {cross_ref}"
else:
Expand Down
2 changes: 2 additions & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3824,6 +3824,8 @@ def __str__(self) -> str:
# Include declared type of variables and functions.
if self.type is not None:
s += f" : {self.type}"
if self.cross_ref:
s += f" cross_ref:{self.cross_ref}"
return s

def serialize(self, prefix: str, name: str) -> JsonDict:
Expand Down
19 changes: 19 additions & 0 deletions test-data/unit/fine-grained.test
Original file line number Diff line number Diff line change
Expand Up @@ -10486,3 +10486,22 @@ reveal_type(s)
==
==
b.py:2: note: Revealed type is "builtins.str"

[case testRenameSubModule]
import a

[file a.py]
import pkg.sub

[file pkg/__init__.py]
[file pkg/sub/__init__.py]
from pkg.sub import mod
[file pkg/sub/mod.py]

[file pkg/sub/__init__.py.2]
from pkg.sub import modb
[delete pkg/sub/mod.py.2]
[file pkg/sub/modb.py.2]

[out]
==

0 comments on commit 5624f40

Please sign in to comment.