-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Write cache for modules with errors #19820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,6 @@ | |
| from pathlib import Path | ||
|
|
||
| from mypy import build | ||
| from mypy.build import Graph | ||
| from mypy.errors import CompileError | ||
| from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths | ||
| from mypy.test.config import test_data_prefix, test_temp_dir | ||
|
|
@@ -164,11 +163,13 @@ def run_case_once( | |
| sys.path.insert(0, plugin_dir) | ||
|
|
||
| res = None | ||
| blocker = False | ||
| try: | ||
| res = build.build(sources=sources, options=options, alt_lib_path=test_temp_dir) | ||
| a = res.errors | ||
| except CompileError as e: | ||
| a = e.messages | ||
| blocker = True | ||
| finally: | ||
| assert sys.path[0] == plugin_dir | ||
| del sys.path[0] | ||
|
|
@@ -199,7 +200,7 @@ def run_case_once( | |
|
|
||
| if res: | ||
| if options.cache_dir != os.devnull: | ||
| self.verify_cache(module_data, res.errors, res.manager, res.graph) | ||
| self.verify_cache(module_data, res.manager, blocker) | ||
|
|
||
| name = "targets" | ||
| if incremental_step: | ||
|
|
@@ -229,42 +230,23 @@ def run_case_once( | |
| check_test_output_files(testcase, incremental_step, strip_prefix="tmp/") | ||
|
|
||
| def verify_cache( | ||
| self, | ||
| module_data: list[tuple[str, str, str]], | ||
| a: list[str], | ||
| manager: build.BuildManager, | ||
| graph: Graph, | ||
| self, module_data: list[tuple[str, str, str]], manager: build.BuildManager, blocker: bool | ||
| ) -> None: | ||
| # There should be valid cache metadata for each module except | ||
| # for those that had an error in themselves or one of their | ||
| # dependencies. | ||
| error_paths = self.find_error_message_paths(a) | ||
| busted_paths = {m.path for id, m in manager.modules.items() if graph[id].transitive_error} | ||
| modules = self.find_module_files(manager) | ||
| modules.update({module_name: path for module_name, path, text in module_data}) | ||
| missing_paths = self.find_missing_cache_files(modules, manager) | ||
| # We would like to assert error_paths.issubset(busted_paths) | ||
| # but this runs into trouble because while some 'notes' are | ||
| # really errors that cause an error to be marked, many are | ||
| # just notes attached to other errors. | ||
| assert error_paths or not busted_paths, "Some modules reported error despite no errors" | ||
| if not missing_paths == busted_paths: | ||
| raise AssertionError(f"cache data discrepancy {missing_paths} != {busted_paths}") | ||
| if not blocker: | ||
| # There should be valid cache metadata for each module except | ||
| # in case of a blocking error in themselves or one of their | ||
| # dependencies. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this comment up-to-date? Don't we know have cache metadata even if there are non-blocker errors? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this should now read "... except for those that had a blocker error in themselves or ..." |
||
| modules = self.find_module_files(manager) | ||
| modules.update({module_name: path for module_name, path, text in module_data}) | ||
| missing_paths = self.find_missing_cache_files(modules, manager) | ||
| if missing_paths: | ||
| raise AssertionError(f"cache data missing for {missing_paths}") | ||
| assert os.path.isfile(os.path.join(manager.options.cache_dir, ".gitignore")) | ||
| cachedir_tag = os.path.join(manager.options.cache_dir, "CACHEDIR.TAG") | ||
| assert os.path.isfile(cachedir_tag) | ||
| with open(cachedir_tag) as f: | ||
| assert f.read().startswith("Signature: 8a477f597d28d172789f06886806bc55") | ||
|
|
||
| def find_error_message_paths(self, a: list[str]) -> set[str]: | ||
| hits = set() | ||
| for line in a: | ||
| m = re.match(r"([^\s:]+):(\d+:)?(\d+:)? (error|warning|note):", line) | ||
| if m: | ||
| p = m.group(1) | ||
| hits.add(p) | ||
| return hits | ||
|
|
||
| def find_module_files(self, manager: build.BuildManager) -> dict[str, str]: | ||
| return {id: module.path for id, module in manager.modules.items()} | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there are blockers? Would we then need to delete the cache file? I wonder if we have a test for this, i.e. blocker is introduced, and the following incremental run still has the same blocker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocker are always exceptions in
_build()caught bybuild(), so we will not even get to the point where we write cache. If there was an existing one, it is fine. It will be either discarded on the next run as well (because it was discarded on this run, as we were checking the file for some reason), or actually used if a user does an "undo" in the file. I think there is never a reason to delete an existing cache file.However, surprisingly I can't find an existing test for this.