All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 """Caching of formatted files with feature-based invalidation."""
5 from pathlib import Path
7 from typing import Dict, Iterable, Set, Tuple
9 from platformdirs import user_cache_dir
11 from black.mode import Mode
13 from _black_version import version as __version__
19 CacheInfo = Tuple[Timestamp, FileSize]
20 Cache = Dict[str, CacheInfo]
23 CACHE_DIR = Path(user_cache_dir("black", version=__version__))
26 def read_cache(mode: Mode) -> Cache:
27 """Read the cache if it exists and is well formed.
29 If it is not well formed, the call to write_cache later should resolve the issue.
31 cache_file = get_cache_file(mode)
32 if not cache_file.exists():
35 with cache_file.open("rb") as fobj:
37 cache: Cache = pickle.load(fobj)
38 except (pickle.UnpicklingError, ValueError, IndexError):
44 def get_cache_file(mode: Mode) -> Path:
45 return CACHE_DIR / f"cache.{mode.get_cache_key()}.pickle"
48 def get_cache_info(path: Path) -> CacheInfo:
49 """Return the information used to check if a file is already formatted or not."""
51 return stat.st_mtime, stat.st_size
54 def filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]:
55 """Split an iterable of paths in `sources` into two sets.
57 The first contains paths of files that modified on disk or are not in the
58 cache. The other contains paths to non-modified files.
60 todo, done = set(), set()
62 res_src = src.resolve()
63 if cache.get(str(res_src)) != get_cache_info(res_src):
70 def write_cache(cache: Cache, sources: Iterable[Path], mode: Mode) -> None:
71 """Update the cache file."""
72 cache_file = get_cache_file(mode)
74 CACHE_DIR.mkdir(parents=True, exist_ok=True)
77 **{str(src.resolve()): get_cache_info(src) for src in sources},
79 with tempfile.NamedTemporaryFile(dir=str(cache_file.parent), delete=False) as f:
80 pickle.dump(new_cache, f, protocol=4)
81 os.replace(f.name, cache_file)