From 5830cd69dbc67c03c6bb635f93dbb59f58bf352b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Dec 2017 11:11:43 -0800 Subject: [PATCH 1/5] Add unit test --- test-data/unit/check-incremental.test | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index e867e08d37e22..baf2e036aa8fc 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1,7 +1,7 @@ -- Checks for incremental mode (see testcheck.py). --- Each test is run twice, once with a cold cache, once with a warm cache. +-- Each test is run at least twice, once with a cold cache, once with a warm cache. -- Before the tests are run again, in step N any *.py.N files are copied to --- *.py. +-- *.py. There are at least two runs; more as long as there are *.py.N files. -- -- You can add an empty section like `[delete mod.py.2]` to delete `mod.py` -- before the second run. @@ -3432,3 +3432,18 @@ extra = 1 [out1] [out2] + +[case testErrorsAffectDependentsOnly] +# cmd: mypy -m m.a m.b m.c +[file m/__init__.py] +[file m/a.py] +1 + '' # Deliberate error +[file m/b.py] +import m.a # Depends on module with error +[file m/c.py] +import m # No error here +[rechecked m.a, m.b] +[out1] +tmp/m/a.py:1: error: Unsupported operand types for + ("int" and "str") +[out2] +tmp/m/a.py:1: error: Unsupported operand types for + ("int" and "str") From 70611e0a32bd16a5a65e3aede9a86af88786633e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Dec 2017 10:55:12 -0800 Subject: [PATCH 2/5] Only delete cache errors in dependencies -- take two Fixes #4135 --- mypy/build.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index 677511e28318c..8f51ba42ecddb 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1474,6 +1474,9 @@ class State: # Whether to ignore all errors ignore_all = False + # Whether the module has an error or any of its dependencies have one. + transitive_error = False + # Type checker used for checking this file. Use type_checker() for # access and to construct this on demand. _type_checker = None # type: Optional[TypeChecker] @@ -1944,7 +1947,7 @@ def write_cache(self) -> None: if self.manager.options.quick_and_dirty: is_errors = self.manager.errors.is_errors_for_file(self.path) else: - is_errors = self.manager.errors.is_errors() + is_errors = self.transitive_error if is_errors: delete_cache(self.id, self.path, self.manager) self.meta = None @@ -2257,6 +2260,10 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: else: fresh_msg = "stale due to deps (%s)" % " ".join(sorted(stale_deps)) + if any(graph[dep].transitive_error for dep in deps if dep in graph): + for id in scc: + graph[id].transitive_error = True + scc_str = " ".join(scc) if fresh: if not maybe_reuse_in_memory_tree(graph, scc, manager): @@ -2464,6 +2471,9 @@ def process_stale_scc(graph: Graph, scc: List[str], manager: BuildManager) -> No for id in stale: if graph[id].type_check_second_pass(): more = True + if any(manager.errors.is_errors_for_file(graph[id].path) for id in stale): + for id in stale: + graph[id].transitive_error = True for id in stale: graph[id].finish_passes() graph[id].write_cache() From 257a9d14adacdec7a496a5b7e5f2697a6acd585c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Dec 2017 21:27:20 -0800 Subject: [PATCH 3/5] Fix test (not sure why this one test needs fixing) --- test-data/unit/check-incremental.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index baf2e036aa8fc..6df0d81a686b1 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1113,7 +1113,7 @@ val = "foo" [builtins fixtures/module_all.pyi] [rechecked main, c, c.submodule] -[stale] +[stale c] [out2] tmp/c/submodule.py:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/main.py:7: error: "C" has no attribute "foo" From d2bdf1bd0c8e732a96cde3a5480f59ae13b1732b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 8 Dec 2017 08:31:58 -0800 Subject: [PATCH 4/5] Update some comments --- mypy/build.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mypy/build.py b/mypy/build.py index 8f51ba42ecddb..d4688105309a4 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2260,6 +2260,8 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: else: fresh_msg = "stale due to deps (%s)" % " ".join(sorted(stale_deps)) + # Initialize transitive_error for all SCC members from union + # of transitive_error of dependencies. if any(graph[dep].transitive_error for dep in deps if dep in graph): for id in scc: graph[id].transitive_error = True @@ -2279,6 +2281,11 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: # single fresh SCC. This is intentional -- we don't need those modules # loaded if there are no more stale SCCs to be rechecked. # + # Also note we shouldn't have to worry about transitive_error here, + # since modules with transitive errors aren't written to the cache, + # and if any dependencies were changed, this SCC would be stale. + # (Also, in quick_and_dirty mode we don't care about transitive errors.) + # # TODO: see if it's possible to determine if we need to process only a # _subset_ of the past SCCs instead of having to process them all. for prev_scc in fresh_scc_queue: @@ -2423,6 +2430,7 @@ def can_reuse_in_memory_tree(graph: Graph, scc: List[str], manager: BuildManager # Check that all dependencies were loaded from memory. # If not, some dependency was reparsed but the interface hash # wasn't changed -- in that case we can't reuse the tree. + # TODO: Pass deps in from process_graph(), via maybe_reuse_in_memory_tree()? deps = set(dep for id in scc for dep in graph[id].dependencies if dep in graph) deps -= set(scc) # Subtract the SCC itself (else nothing will be safe) if all(graph[dep].is_from_saved_cache for dep in deps): From c52c00e32195ebf81f55f8599245a16849daeaf0 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 8 Dec 2017 09:02:48 -0800 Subject: [PATCH 5/5] Fix strict-optional error --- mypy/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index d4688105309a4..aa8830d3ae268 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2479,7 +2479,7 @@ def process_stale_scc(graph: Graph, scc: List[str], manager: BuildManager) -> No for id in stale: if graph[id].type_check_second_pass(): more = True - if any(manager.errors.is_errors_for_file(graph[id].path) for id in stale): + if any(manager.errors.is_errors_for_file(graph[id].xpath) for id in stale): for id in stale: graph[id].transitive_error = True for id in stale: