Skip to content

Commit 63ba7d9

Browse files
committed
Fix some fine-grained module error message bugs
1 parent 4fb532e commit 63ba7d9

File tree

4 files changed

+139
-14
lines changed

4 files changed

+139
-14
lines changed

mypy/build.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,34 @@ def write_cache(self) -> None:
19091909
self.mark_interface_stale()
19101910
self.interface_hash = new_interface_hash
19111911

1912+
def verify_dependencies(self) -> None:
1913+
"""Report errors for import targets in module that don't exist."""
1914+
# Strip out indirect dependencies. See comment in build.load_graph().
1915+
manager = self.manager
1916+
dependencies = [dep for dep in self.dependencies
1917+
if self.priorities.get(dep) != PRI_INDIRECT]
1918+
assert self.ancestors is not None
1919+
for dep in dependencies + self.suppressed + self.ancestors:
1920+
if dep not in manager.modules and not manager.options.ignore_missing_imports:
1921+
line = self.dep_line_map.get(dep, 1)
1922+
try:
1923+
if dep in self.ancestors:
1924+
state, ancestor = None, self # type: (Optional[State], Optional[State])
1925+
else:
1926+
state, ancestor = self, None
1927+
# Called just for its side effects of producing diagnostics.
1928+
find_module_and_diagnose(
1929+
manager, self.options, dep,
1930+
caller_state=state, caller_line=line,
1931+
ancestor_for=ancestor)
1932+
except Exception:
1933+
# Swallow up any exception while generating a diagnostic.
1934+
# This can actually include CompileErrors that get generated in
1935+
# fine-grained mode when an __init__.py is deleted, if a module
1936+
# that was in that package has targets reprocessed before
1937+
# it is renamed.
1938+
pass
1939+
19121940
def dependency_priorities(self) -> List[int]:
19131941
return [self.priorities.get(dep, PRI_HIGH) for dep in self.dependencies]
19141942

mypy/server/update.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -594,18 +594,6 @@ def get_all_changed_modules(root_module: str,
594594
return changed_modules
595595

596596

597-
def verify_dependencies(state: State, manager: BuildManager) -> None:
598-
"""Report errors for import targets in module that don't exist."""
599-
# Strip out indirect dependencies. See comment in build.load_graph().
600-
dependencies = [dep for dep in state.dependencies if state.priorities.get(dep) != PRI_INDIRECT]
601-
for dep in dependencies + state.suppressed: # TODO: ancestors?
602-
if dep not in manager.modules and not state.options.ignore_missing_imports:
603-
assert state.tree
604-
line = state.dep_line_map.get(dep, 1)
605-
assert state.path
606-
module_not_found(manager, line, state, dep)
607-
608-
609597
def collect_dependencies(new_modules: Mapping[str, Optional[MypyFile]],
610598
deps: Dict[str, Set[str]],
611599
graph: Dict[str, State]) -> None:
@@ -879,7 +867,7 @@ def key(node: DeferredNode) -> int:
879867
update_deps(module_id, nodes, graph, deps, options)
880868

881869
# Report missing imports.
882-
verify_dependencies(graph[module_id], manager)
870+
graph[module_id].verify_dependencies()
883871

884872
return new_triggered
885873

mypy/test/testfinegrained.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def parse_sources(self, program_text: str,
222222
m = re.search('# cmd: mypy ([a-zA-Z0-9_./ ]+)$', program_text, flags=re.MULTILINE)
223223
regex = '# cmd{}: mypy ([a-zA-Z0-9_./ ]+)$'.format(incremental_step)
224224
alt_m = re.search(regex, program_text, flags=re.MULTILINE)
225-
if alt_m is not None and incremental_step > 1:
225+
if alt_m is not None:
226226
# Optionally return a different command if in a later step
227227
# of incremental mode, otherwise default to reusing the
228228
# original cmd.

test-data/unit/fine-grained-modules.test

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,3 +1632,112 @@ class Foo:
16321632
[out]
16331633
==
16341634
a.py:3: error: Argument 1 to "foo" of "Foo" has incompatible type "int"; expected "str"
1635+
1636+
[case testSkipButDontIgnore1]
1637+
# cmd: mypy a.py c.py
1638+
# flags: --follow-imports=skip
1639+
[file a.py]
1640+
import b
1641+
from c import x
1642+
[file b.py]
1643+
[file c.py]
1644+
x = 1
1645+
[file c.py.2]
1646+
x = '2'
1647+
[out]
1648+
==
1649+
1650+
[case testSkipButDontIgnore2]
1651+
# cmd: mypy a.py c.py
1652+
# flags: --follow-imports=skip
1653+
[file a.py]
1654+
from c import x
1655+
import b
1656+
[file b.py]
1657+
[file c.py]
1658+
x = 1
1659+
[file c.py.2]
1660+
x = '2'
1661+
[file c.py.3]
1662+
x = 2
1663+
[delete b.py.3]
1664+
[out]
1665+
==
1666+
==
1667+
a.py:2: error: Cannot find module named 'b'
1668+
a.py:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)
1669+
1670+
[case testErrorButDontIgnore1]
1671+
# cmd: mypy a.py c.py
1672+
# flags: --follow-imports=error
1673+
[file a.py]
1674+
from c import x
1675+
import b
1676+
[file b.py]
1677+
[file c.py]
1678+
x = 1
1679+
[file c.py.2]
1680+
x = '2'
1681+
[out]
1682+
a.py:2: note: Import of 'b' ignored
1683+
a.py:2: note: (Using --follow-imports=error, module not passed on command line)
1684+
==
1685+
a.py:2: note: Import of 'b' ignored
1686+
a.py:2: note: (Using --follow-imports=error, module not passed on command line)
1687+
1688+
[case testErrorButDontIgnore2]
1689+
# cmd1: mypy a.py c.py b.py
1690+
# cmd2: mypy a.py c.py
1691+
# flags: --follow-imports=error
1692+
[file a.py]
1693+
from c import x
1694+
import b
1695+
[file b.py]
1696+
[file c.py]
1697+
x = 1
1698+
[file c.py.2]
1699+
x = '2'
1700+
[out]
1701+
==
1702+
a.py:2: note: Import of 'b' ignored
1703+
a.py:2: note: (Using --follow-imports=error, module not passed on command line)
1704+
1705+
-- This test fails because p.b doesn't seem to trigger p properly...
1706+
[case testErrorButDontIgnore3-skip]
1707+
# cmd1: mypy a.py c.py p/b.py p/__init__.py
1708+
# cmd2: mypy a.py c.py p/b.py
1709+
# flags: --follow-imports=error --verbose
1710+
[file a.py]
1711+
from c import x
1712+
from p.b import y
1713+
[file p/b.py]
1714+
y = 12
1715+
[file p/__init__.py]
1716+
[file c.py]
1717+
x = 1
1718+
[file c.py.2]
1719+
x = '2'
1720+
[out]
1721+
==
1722+
p/b.py: note: Ancestor package 'p' ignored
1723+
p/b.py: note: (Using --follow-imports=error, submodule passed on command line)
1724+
1725+
[case testErrorButDontIgnore4]
1726+
# cmd: mypy a.py z.py p/b.py p/__init__.py
1727+
# cmd2: mypy a.py p/b.py
1728+
# flags: --follow-imports=error
1729+
[file a.py]
1730+
from p.b import y
1731+
[file p/b.py]
1732+
from z import x
1733+
y = 12
1734+
[file p/__init__.py]
1735+
[file z.py]
1736+
x = 1
1737+
[delete z.py.2]
1738+
[out]
1739+
==
1740+
p/b.py: note: Ancestor package 'p' ignored
1741+
p/b.py: note: (Using --follow-imports=error, submodule passed on command line)
1742+
p/b.py:1: error: Cannot find module named 'z'
1743+
p/b.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)

0 commit comments

Comments
 (0)