Skip to content

Commit c1cbfa3

Browse files
committed
baseline cache
1 parent f24781b commit c1cbfa3

File tree

7 files changed

+151
-66
lines changed

7 files changed

+151
-66
lines changed

.mypy/baseline.json

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
"code": "no-any-explicit",
8484
"column": 0,
8585
"message": "Explicit \"Any\" is not allowed",
86-
"offset": 14,
86+
"offset": 15,
8787
"src": "def cache_meta_from_dict(meta: dict[str, Any], data_json: str) -> CacheMeta:",
8888
"target": "mypy.build.cache_meta_from_dict"
8989
},
@@ -463,6 +463,22 @@
463463
"src": "meta.get(\"plugin_data\", None),",
464464
"target": "mypy.build.cache_meta_from_dict"
465465
},
466+
{
467+
"code": "no-any-expr",
468+
"column": 8,
469+
"message": "Expression type contains \"Any\" (has type \"dict[str, Any]\")",
470+
"offset": 1,
471+
"src": "meta.get(\"baseline_hash\", None),",
472+
"target": "mypy.build.cache_meta_from_dict"
473+
},
474+
{
475+
"code": "no-any-expr",
476+
"column": 8,
477+
"message": "Expression has type \"Any\"",
478+
"offset": 0,
479+
"src": "meta.get(\"baseline_hash\", None),",
480+
"target": "mypy.build.cache_meta_from_dict"
481+
},
466482
{
467483
"code": "no-any-expr",
468484
"column": 26,
@@ -1343,11 +1359,19 @@
13431359
"src": "if m.plugin_data != plugin_data:",
13441360
"target": "mypy.build.find_cache_meta"
13451361
},
1362+
{
1363+
"code": "no-any-expr",
1364+
"column": 24,
1365+
"message": "Expression type contains \"Any\" (has type \"CacheMeta\")",
1366+
"offset": 17,
1367+
"src": "if baseline_hash != m.baseline_hash:",
1368+
"target": "mypy.build.find_cache_meta"
1369+
},
13461370
{
13471371
"code": "no-any-expr",
13481372
"column": 11,
13491373
"message": "Expression type contains \"Any\" (has type \"CacheMeta\")",
1350-
"offset": 5,
1374+
"offset": 7,
13511375
"src": "return m",
13521376
"target": "mypy.build.find_cache_meta"
13531377
},
@@ -1595,15 +1619,15 @@
15951619
"code": "no-any-expr",
15961620
"column": 18,
15971621
"message": "Expression has type \"Any\"",
1598-
"offset": 55,
1622+
"offset": 56,
15991623
"src": "plugin_data = manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=False))",
16001624
"target": "mypy.build.write_cache"
16011625
},
16021626
{
16031627
"code": "no-any-expr",
16041628
"column": 11,
1605-
"message": "Expression type contains \"Any\" (has type \"dict[str, str | int | list[str] | Mapping[str, object] | list[int] | Any]\")",
1606-
"offset": 51,
1629+
"message": "Expression type contains \"Any\" (has type \"dict[str, str | int | list[str] | Mapping[str, object] | list[int] | Any | None]\")",
1630+
"offset": 64,
16071631
"src": "meta = {",
16081632
"target": "mypy.build.write_cache"
16091633
},
@@ -1626,8 +1650,8 @@
16261650
{
16271651
"code": "no-any-expr",
16281652
"column": 26,
1629-
"message": "Expression type contains \"Any\" (has type \"dict[str, str | int | list[str] | Mapping[str, object] | list[int] | Any]\")",
1630-
"offset": 4,
1653+
"message": "Expression type contains \"Any\" (has type \"dict[str, str | int | list[str] | Mapping[str, object] | list[int] | Any | None]\")",
1654+
"offset": 5,
16311655
"src": "meta_str = json_dumps(meta, manager.options.debug_cache)",
16321656
"target": "mypy.build.write_cache"
16331657
},
@@ -1650,7 +1674,7 @@
16501674
{
16511675
"code": "no-any-expr",
16521676
"column": 48,
1653-
"message": "Expression type contains \"Any\" (has type \"dict[str, str | int | list[str] | Mapping[str, object] | list[int] | Any]\")",
1677+
"message": "Expression type contains \"Any\" (has type \"dict[str, str | int | list[str] | Mapping[str, object] | list[int] | Any | None]\")",
16541678
"offset": 0,
16551679
"src": "return interface_hash, cache_meta_from_dict(meta, data_json)",
16561680
"target": "mypy.build.write_cache"
@@ -1875,7 +1899,7 @@
18751899
"code": "no-any-expr",
18761900
"column": 15,
18771901
"message": "Expression type contains \"Any\" (has type \"CacheMeta | None\")",
1878-
"offset": 79,
1902+
"offset": 80,
18791903
"src": "if self.meta:",
18801904
"target": "mypy.build.State.generate_unused_ignore_notes"
18811905
},
@@ -2887,7 +2911,7 @@
28872911
"code": "explicit-override",
28882912
"column": 4,
28892913
"message": "Method \"visit_type_alias_expr\" is not using @override but is overriding a method in class \"mypy.visitor.ExpressionVisitor\"",
2890-
"offset": 46,
2914+
"offset": 47,
28912915
"src": "def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type:",
28922916
"target": "mypy.checkexpr.ExpressionChecker.visit_type_alias_expr"
28932917
},
@@ -6811,7 +6835,7 @@
68116835
"code": "truthy-bool",
68126836
"column": 11,
68136837
"message": "Member \"options\" has type \"Options\" which does not implement __bool__ or __len__ so it could always be true in boolean context",
6814-
"offset": 518,
6838+
"offset": 516,
68156839
"src": "if self.options:",
68166840
"target": "mypy.errors.Errors.is_error_code_enabled"
68176841
},
@@ -6835,7 +6859,7 @@
68356859
"code": "redundant-expr",
68366860
"column": 19,
68376861
"message": "Condition is always false",
6838-
"offset": 371,
6862+
"offset": 375,
68396863
"src": "if e.type is None:",
68406864
"target": "mypy.errors.Errors.render_messages"
68416865
},
@@ -10735,7 +10759,7 @@
1073510759
"code": "redundant-expr",
1073610760
"column": 18,
1073710761
"message": "Condition is always true",
10738-
"offset": 105,
10762+
"offset": 104,
1073910763
"src": "while True:",
1074010764
"target": "mypy.ipc.IPCBase.read"
1074110765
}
@@ -11341,7 +11365,7 @@
1134111365
"code": "helpful-string",
1134211366
"column": 12,
1134311367
"message": "The string for \"None\" isn't helpful in a user-facing or semantic string",
11344-
"offset": 235,
11368+
"offset": 239,
1134511369
"src": "\"Warning: unused section(s) in {}: {}\".format(",
1134611370
"target": "mypy.main.run_build"
1134711371
},
@@ -36453,7 +36477,7 @@
3645336477
"code": "no-any-expr",
3645436478
"column": 18,
3645536479
"message": "Expression type contains \"Any\" (has type \"list[(str, Any)]\")",
36456-
"offset": 365,
36480+
"offset": 364,
3645736481
"src": "members = inspect.getmembers(",
3645836482
"target": "mypy.util.get_class_descriptors"
3645936483
},

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## [Unreleased]
44
### Added
55
- `collections.User*` should have `__repr__`
6+
### Fixed
7+
- cache modules that only have baseline errors
68

79
## [2.8.1]
810
### Fixes

mypy/build.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ class CacheMeta(NamedTuple):
344344
version_id: str # mypy version for cache invalidation
345345
ignore_all: bool # if errors were ignored
346346
plugin_data: Any # config data from plugins
347+
baseline_hash: str | None # hash representing the baselined errors of this module
347348

348349

349350
# NOTE: dependencies + suppressed == all reachable imports;
@@ -382,6 +383,7 @@ def cache_meta_from_dict(meta: dict[str, Any], data_json: str) -> CacheMeta:
382383
meta.get("version_id", sentinel),
383384
meta.get("ignore_all", True),
384385
meta.get("plugin_data", None),
386+
meta.get("baseline_hash", None),
385387
)
386388

387389

@@ -1105,7 +1107,7 @@ def save_baseline(manager: BuildManager):
11051107
manager.stdout.write(f"Baseline successfully updated at {file}\n")
11061108

11071109

1108-
def load_baseline(options: Options, errors: Errors, stdout: TextIO) -> None:
1110+
def load_baseline(options: Options, errors: Errors, stdout: TextIO):
11091111
if not options.baseline_file:
11101112
return
11111113

@@ -1485,6 +1487,25 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No
14851487
manager.log(f"Metadata abandoned for {id}: plugin configuration differs")
14861488
return None
14871489

1490+
baseline_hash = None
1491+
baseline_errors = manager.errors.baseline.get(path)
1492+
if baseline_errors:
1493+
errors = [
1494+
{
1495+
"code": error["code"],
1496+
"column": error["column"],
1497+
"line": error["line"],
1498+
"message": error["message"],
1499+
}
1500+
for error in baseline_errors
1501+
]
1502+
baseline_hash = hash_digest(json.dumps(errors).encode())
1503+
if baseline_hash != m.baseline_hash:
1504+
manager.log(f"Metadata abandoned for {id}: baseline is different")
1505+
return None
1506+
if baseline_errors:
1507+
manager.errors.all_errors[path] = "fresh"
1508+
14881509
manager.add_stats(fresh_metas=1)
14891510
return m
14901511

@@ -1651,6 +1672,7 @@ def write_cache(
16511672
source_hash: str,
16521673
ignore_all: bool,
16531674
manager: BuildManager,
1675+
baseline_errors: list[ErrorInfo] | None,
16541676
) -> tuple[str, CacheMeta | None]:
16551677
"""Write cache files for a module.
16561678
@@ -1736,6 +1758,19 @@ def write_cache(
17361758
manager.log(f"Error in os.stat({data_json!r}), skipping cache write")
17371759
return interface_hash, None
17381760

1761+
baseline_hash = None
1762+
if baseline_errors:
1763+
errors = [
1764+
{
1765+
"code": error.code and error.code.code,
1766+
"column": error.column,
1767+
"line": error.line,
1768+
"message": error.message,
1769+
}
1770+
for error in baseline_errors
1771+
]
1772+
baseline_hash = hash_digest(json.dumps(errors).encode())
1773+
17391774
mtime = 0 if bazel else int(st.st_mtime)
17401775
size = st.st_size
17411776
# Note that the options we store in the cache are the options as
@@ -1761,6 +1796,7 @@ def write_cache(
17611796
"version_id": manager.version_id,
17621797
"ignore_all": ignore_all,
17631798
"plugin_data": plugin_data,
1799+
"baseline_hash": baseline_hash,
17641800
}
17651801

17661802
# Write meta cache file
@@ -2609,7 +2645,7 @@ def valid_references(self) -> set[str]:
26092645

26102646
return valid_refs
26112647

2612-
def write_cache(self) -> None:
2648+
def write_cache(self, *, baseline_errors: list[ErrorInfo] | None) -> None:
26132649
assert self.tree is not None, "Internal error: method must be called on parsed file only"
26142650
# We don't support writing cache files in fine-grained incremental mode.
26152651
if (
@@ -2648,6 +2684,7 @@ def write_cache(self) -> None:
26482684
self.source_hash,
26492685
self.ignore_all,
26502686
self.manager,
2687+
baseline_errors,
26512688
)
26522689
if new_interface_hash == self.interface_hash:
26532690
self.manager.log(f"Cached module {self.id} has same interface")
@@ -3599,12 +3636,17 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No
35993636
for id in stale:
36003637
graph[id].transitive_error = True
36013638
for id in stale:
3602-
if graph[id].xpath not in manager.errors.ignored_files:
3603-
errors = manager.errors.file_messages(
3604-
graph[id].xpath, formatter=manager.error_formatter
3605-
)
3606-
manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False)
3607-
graph[id].write_cache()
3639+
file = graph[id].xpath
3640+
if file not in manager.errors.ignored_files:
3641+
errors = manager.errors.file_messages(file, formatter=manager.error_formatter)
3642+
manager.flush_errors(manager.errors.simplify_path(file), errors, False)
3643+
all_errors = manager.errors.all_errors.get(file)
3644+
if all_errors == "fresh":
3645+
raise ValueError("huh?")
3646+
if file in manager.errors.ignored_files:
3647+
all_errors = None
3648+
# here, the `all_errors` will only be cached if not `transitive_error`, so `all_errors` is baselined errors
3649+
graph[id].write_cache(baseline_errors=all_errors)
36083650
graph[id].mark_as_rechecked()
36093651

36103652

0 commit comments

Comments
 (0)