Skip to content

Commit 2db1dd0

Browse files
emmatypingJukkaL
authored andcommitted
Restore dict based typesafe caching (#4898)
This also restores `isfile_case` caching.
1 parent e95f7b5 commit 2db1dd0

File tree

2 files changed

+44
-23
lines changed

2 files changed

+44
-23
lines changed

mypy/build.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -825,18 +825,26 @@ class FindModuleCache:
825825

826826
def __init__(self, fscache: Optional[FileSystemMetaCache] = None) -> None:
827827
self.fscache = fscache or FileSystemMetaCache()
828-
self.find_lib_path_dirs = functools.lru_cache(maxsize=None)(self._find_lib_path_dirs)
829-
self.find_module = functools.lru_cache(maxsize=None)(self._find_module)
828+
# Cache find_lib_path_dirs: (dir_chain, lib_path)
829+
self.dirs = {} # type: Dict[Tuple[str, Tuple[str, ...]], List[str]]
830+
# Cache find_module: (id, lib_path, python_version) -> result.
831+
self.results = {} # type: Dict[Tuple[str, Tuple[str, ...], Optional[str]], Optional[str]]
830832

831833
def clear(self) -> None:
832-
self.find_module.cache_clear()
833-
self.find_lib_path_dirs.cache_clear()
834+
self.results.clear()
835+
self.dirs.clear()
834836

835-
def _find_lib_path_dirs(self, dir_chain: str, lib_path: Tuple[str, ...]) -> List[str]:
837+
def find_lib_path_dirs(self, dir_chain: str, lib_path: Tuple[str, ...]) -> List[str]:
836838
# Cache some repeated work within distinct find_module calls: finding which
837839
# elements of lib_path have even the subdirectory they'd need for the module
838840
# to exist. This is shared among different module ids when they differ only
839841
# in the last component.
842+
key = (dir_chain, lib_path)
843+
if key not in self.dirs:
844+
self.dirs[key] = self._find_lib_path_dirs(dir_chain, lib_path)
845+
return self.dirs[key]
846+
847+
def _find_lib_path_dirs(self, dir_chain: str, lib_path: Tuple[str, ...]) -> List[str]:
840848
dirs = []
841849
for pathitem in lib_path:
842850
# e.g., '/usr/lib/python3.4/foo/bar'
@@ -845,9 +853,16 @@ def _find_lib_path_dirs(self, dir_chain: str, lib_path: Tuple[str, ...]) -> List
845853
dirs.append(dir)
846854
return dirs
847855

856+
def find_module(self, id: str, lib_path: Tuple[str, ...],
857+
python_executable: Optional[str]) -> Optional[str]:
858+
"""Return the path of the module source file, or None if not found."""
859+
key = (id, lib_path, python_executable)
860+
if key not in self.results:
861+
self.results[key] = self._find_module(id, lib_path, python_executable)
862+
return self.results[key]
863+
848864
def _find_module(self, id: str, lib_path: Tuple[str, ...],
849865
python_executable: Optional[str]) -> Optional[str]:
850-
"""Return the path of the module source file, or None if not found."""
851866
fscache = self.fscache
852867

853868
# If we're looking for a module like 'foo.bar.baz', it's likely that most of the
@@ -2167,14 +2182,12 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
21672182
graph = load_graph(sources, manager)
21682183

21692184
t1 = time.time()
2170-
fm_cache_size = manager.find_module_cache.find_module.cache_info().currsize
2171-
fm_dir_cache_size = manager.find_module_cache.find_lib_path_dirs.cache_info().currsize
21722185
manager.add_stats(graph_size=len(graph),
21732186
stubs_found=sum(g.path is not None and g.path.endswith('.pyi')
21742187
for g in graph.values()),
21752188
graph_load_time=(t1 - t0),
2176-
fm_cache_size=fm_cache_size,
2177-
fm_dir_cache_size=fm_dir_cache_size,
2189+
fm_cache_size=len(manager.find_module_cache.results),
2190+
fm_dir_cache_size=len(manager.find_module_cache.dirs),
21782191
)
21792192
if not graph:
21802193
print("Nothing to do?!")

mypy/fscache.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,36 +37,41 @@
3737

3838
class FileSystemMetaCache:
3939
def __init__(self) -> None:
40-
self.stat = functools.lru_cache(maxsize=None)(self._stat)
41-
self.listdir = functools.lru_cache(maxsize=None)(self._listdir)
42-
# lru_cache doesn't handle exceptions, so we need special caches for them.
43-
self.stat_error_cache = {} # type: Dict[str, Exception]
44-
self.listdir_error_cache = {} # type: Dict[str, Exception]
40+
self.flush()
4541

4642
def flush(self) -> None:
4743
"""Start another transaction and empty all caches."""
48-
self.stat.cache_clear()
49-
self.listdir.cache_clear()
50-
self.stat_error_cache.clear()
51-
self.listdir_error_cache.clear()
44+
self.stat_cache = {} # type: Dict[str, os.stat_result]
45+
self.stat_error_cache = {} # type: Dict[str, Exception]
46+
self.listdir_cache = {} # type: Dict[str, List[str]]
47+
self.listdir_error_cache = {} # type: Dict[str, Exception]
48+
self.isfile_case_cache = {} # type: Dict[str, bool]
5249

53-
def _stat(self, path: str) -> os.stat_result:
50+
def stat(self, path: str) -> os.stat_result:
51+
if path in self.stat_cache:
52+
return self.stat_cache[path]
5453
if path in self.stat_error_cache:
5554
raise self.stat_error_cache[path]
5655
try:
57-
return os.stat(path)
56+
st = os.stat(path)
5857
except Exception as err:
5958
self.stat_error_cache[path] = err
6059
raise
60+
self.stat_cache[path] = st
61+
return st
6162

62-
def _listdir(self, path: str) -> List[str]:
63+
def listdir(self, path: str) -> List[str]:
64+
if path in self.listdir_cache:
65+
return self.listdir_cache[path]
6366
if path in self.listdir_error_cache:
6467
raise self.listdir_error_cache[path]
6568
try:
66-
return os.listdir(path)
69+
results = os.listdir(path)
6770
except Exception as err:
6871
self.listdir_error_cache[path] = err
6972
raise err
73+
self.listdir_cache[path] = results
74+
return results
7075

7176
def isfile(self, path: str) -> bool:
7277
try:
@@ -84,6 +89,8 @@ def isfile_case(self, path: str) -> bool:
8489
TODO: We should maybe check the case for some directory components also,
8590
to avoid permitting wrongly-cased *packages*.
8691
"""
92+
if path in self.isfile_case_cache:
93+
return self.isfile_case_cache[path]
8794
head, tail = os.path.split(path)
8895
if not tail:
8996
res = False
@@ -93,6 +100,7 @@ def isfile_case(self, path: str) -> bool:
93100
res = tail in names and self.isfile(path)
94101
except OSError:
95102
res = False
103+
self.isfile_case_cache[path] = res
96104
return res
97105

98106
def isdir(self, path: str) -> bool:

0 commit comments

Comments
 (0)