Skip to content

Commit 5624f40

Browse files
authored
Fix daemon crash caused by deleted submodule (#16370)
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.
1 parent ad0e183 commit 5624f40

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

mypy/fixup.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,23 @@ def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None:
128128
cross_ref, self.modules, raise_on_missing=not self.allow_missing
129129
)
130130
if stnode is not None:
131-
assert stnode.node is not None, (table_fullname + "." + key, cross_ref)
132-
value.node = stnode.node
131+
if stnode is value:
132+
# The node seems to refer to itself, which can mean that
133+
# the target is a deleted submodule of the current module,
134+
# and thus lookup falls back to the symbol table of the parent
135+
# package. Here's how this may happen:
136+
#
137+
# pkg/__init__.py:
138+
# from pkg import sub
139+
#
140+
# Now if pkg.sub is deleted, the pkg.sub symbol table entry
141+
# appears to refer to itself. Replace the entry with a
142+
# placeholder to avoid a crash. We can't delete the entry,
143+
# as it would stop dependency propagation.
144+
value.node = Var(key + "@deleted")
145+
else:
146+
assert stnode.node is not None, (table_fullname + "." + key, cross_ref)
147+
value.node = stnode.node
133148
elif not self.allow_missing:
134149
assert False, f"Could not find cross-ref {cross_ref}"
135150
else:

mypy/nodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3824,6 +3824,8 @@ def __str__(self) -> str:
38243824
# Include declared type of variables and functions.
38253825
if self.type is not None:
38263826
s += f" : {self.type}"
3827+
if self.cross_ref:
3828+
s += f" cross_ref:{self.cross_ref}"
38273829
return s
38283830

38293831
def serialize(self, prefix: str, name: str) -> JsonDict:

test-data/unit/fine-grained.test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10486,3 +10486,22 @@ reveal_type(s)
1048610486
==
1048710487
==
1048810488
b.py:2: note: Revealed type is "builtins.str"
10489+
10490+
[case testRenameSubModule]
10491+
import a
10492+
10493+
[file a.py]
10494+
import pkg.sub
10495+
10496+
[file pkg/__init__.py]
10497+
[file pkg/sub/__init__.py]
10498+
from pkg.sub import mod
10499+
[file pkg/sub/mod.py]
10500+
10501+
[file pkg/sub/__init__.py.2]
10502+
from pkg.sub import modb
10503+
[delete pkg/sub/mod.py.2]
10504+
[file pkg/sub/modb.py.2]
10505+
10506+
[out]
10507+
==

0 commit comments

Comments
 (0)