Skip to content

Commit

Permalink
Refactor fine-grained graph processing and optimize deletes in initia…
Browse files Browse the repository at this point in the history
…l load
  • Loading branch information
msullivan committed Mar 8, 2018
1 parent b328f74 commit 116d8e5
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 15 deletions.
40 changes: 28 additions & 12 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1795,13 +1795,15 @@ def load_tree(self) -> None:

def fix_cross_refs(self) -> None:
assert self.tree is not None, "Internal error: method must be called on parsed file only"
# We need to set quick_and_dirty when doing a fine grained
# cache load because we need to gracefully handle missing modules.
fixup_module_pass_one(self.tree, self.manager.modules,
self.manager.options.quick_and_dirty)
self.manager.options.quick_and_dirty or
self.manager.only_load_from_cache)

def calculate_mros(self) -> None:
assert self.tree is not None, "Internal error: method must be called on parsed file only"
fixup_module_pass_two(self.tree, self.manager.modules,
self.manager.options.quick_and_dirty)
fixup_module_pass_two(self.tree, self.manager.modules)

def patch_dependency_parents(self) -> None:
"""
Expand Down Expand Up @@ -2128,7 +2130,10 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
if manager.options.dump_graph:
dump_graph(graph)
return graph
process_graph(graph, manager)
if manager.only_load_from_cache:
halfass_process_graph(graph, manager)
else:
process_graph(graph, manager)
updated = preserve_cache(graph)
set_updated = set(updated)
manager.saved_cache.clear()
Expand Down Expand Up @@ -2437,14 +2442,6 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
manager.log("Processing SCC of size %d (%s) as %s" % (size, scc_str, fresh_msg))
process_stale_scc(graph, scc, manager)

# If we are running in fine-grained incremental mode with caching,
# we always process fresh SCCs so that we have all of the symbol
# tables and fine-grained dependencies available.
if manager.options.use_fine_grained_cache:
for prev_scc in fresh_scc_queue:
process_fresh_scc(graph, prev_scc, manager)
fresh_scc_queue = []

sccs_left = len(fresh_scc_queue)
nodes_left = sum(len(scc) for scc in fresh_scc_queue)
manager.add_stats(sccs_left=sccs_left, nodes_left=nodes_left)
Expand All @@ -2456,6 +2453,25 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
manager.log("No fresh SCCs left in queue")


def halfass_process_graph(graph: Graph, manager: BuildManager) -> None:
"""Finish loading everything for use in the fine-grained incremental cache"""

# If we are running in fine-grained incremental mode with caching,
# we process all SCCs as fresh SCCs so that we have all of the symbol
# tables and fine-grained dependencies available.
# We fail the loading of any SCC that we can't load a meta for, so we
# don't have anything *but* fresh SCCs.
sccs = sorted_components(graph)
manager.log("Found %d SCCs; largest has %d nodes" %
(len(sccs), max(len(scc) for scc in sccs)))

for ascc in sccs:
# Order the SCC's nodes using a heuristic.
# Note that ascc is a set, and scc is a list.
scc = order_ascc(graph, ascc)
process_fresh_scc(graph, scc, manager)


def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_ALL) -> List[str]:
"""Come up with the ideal processing order within an SCC.
Expand Down
5 changes: 5 additions & 0 deletions mypy/dmypy_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ def initialize_fine_grained(self, sources: List[mypy.build.BuildSource]) -> Dict

# Run an update
changed = self.find_changed(sources)
for state in self.fine_grained_manager.graph.values():
if not state.is_fresh():
assert state.path is not None
changed.append((state.id, state.path))

if changed:
messages = self.fine_grained_manager.update(changed)
self.fscache.flush()
Expand Down
3 changes: 1 addition & 2 deletions mypy/fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ def fixup_module_pass_one(tree: MypyFile, modules: Dict[str, MypyFile],
node_fixer.visit_symbol_table(tree.names)


def fixup_module_pass_two(tree: MypyFile, modules: Dict[str, MypyFile],
quick_and_dirty: bool) -> None:
def fixup_module_pass_two(tree: MypyFile, modules: Dict[str, MypyFile]) -> None:
compute_all_mros(tree.names, modules)


Expand Down
4 changes: 3 additions & 1 deletion mypy/server/astdiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,9 @@ def snapshot_symbol_table(name_prefix: str, table: SymbolTable) -> Dict[str, Sna
common = (fullname, symbol.kind, symbol.module_public)
if symbol.kind == MODULE_REF:
# This is a cross-reference to another module.
assert isinstance(node, MypyFile)
# If the reference is busted because the other module is missing,
# the node will be a "stale_info" TypeInfo produced by fixup,
# but that doesn't really matter to us here.
result[name] = ('Moduleref', common)
elif symbol.kind == TVAR:
assert isinstance(node, TypeVarExpr)
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/fine-grained-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,22 @@ c.f(1)
==
a.py:2: error: Too many arguments for "f"

[case testRenameAndDeleteModule]
import a
[file a.py]
from b1 import f
f()
[file b1.py]
def f() -> None: pass
[file b2.py.2]
def f() -> None: pass
[delete b1.py.2]
[file a.py.2]
from b2 import f
f()
[out]
==

[case testDeleteFileWithinPackage]
import a
[file a.py]
Expand Down

0 comments on commit 116d8e5

Please sign in to comment.