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."""
6 from pathlib import Path
7 from typing import Dict, Iterable, Set, Tuple
9 from platformdirs import user_cache_dir
11 from _black_version import version as __version__
12 from black.mode import Mode
17 CacheInfo = Tuple[Timestamp, FileSize]
18 Cache = Dict[str, CacheInfo]
21 def get_cache_dir() -> Path:
22 """Get the cache directory used by black.
24 Users can customize this directory on all systems using `BLACK_CACHE_DIR`
25 environment variable. By default, the cache directory is the user cache directory
26 under the black application.
28 This result is immediately set to a constant `black.cache.CACHE_DIR` as to avoid
31 # NOTE: Function mostly exists as a clean way to test getting the cache directory.
32 default_cache_dir = user_cache_dir("black", version=__version__)
33 cache_dir = Path(os.environ.get("BLACK_CACHE_DIR", default_cache_dir))
37 CACHE_DIR = get_cache_dir()
40 def read_cache(mode: Mode) -> Cache:
41 """Read the cache if it exists and is well formed.
43 If it is not well formed, the call to write_cache later should resolve the issue.
45 cache_file = get_cache_file(mode)
46 if not cache_file.exists():
49 with cache_file.open("rb") as fobj:
51 cache: Cache = pickle.load(fobj)
52 except (pickle.UnpicklingError, ValueError, IndexError):
58 def get_cache_file(mode: Mode) -> Path:
59 return CACHE_DIR / f"cache.{mode.get_cache_key()}.pickle"
62 def get_cache_info(path: Path) -> CacheInfo:
63 """Return the information used to check if a file is already formatted or not."""
65 return stat.st_mtime, stat.st_size
68 def filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]:
69 """Split an iterable of paths in `sources` into two sets.
71 The first contains paths of files that modified on disk or are not in the
72 cache. The other contains paths to non-modified files.
74 todo, done = set(), set()
76 res_src = src.resolve()
77 if cache.get(str(res_src)) != get_cache_info(res_src):
84 def write_cache(cache: Cache, sources: Iterable[Path], mode: Mode) -> None:
85 """Update the cache file."""
86 cache_file = get_cache_file(mode)
88 CACHE_DIR.mkdir(parents=True, exist_ok=True)
91 **{str(src.resolve()): get_cache_info(src) for src in sources},
93 with tempfile.NamedTemporaryFile(dir=str(cache_file.parent), delete=False) as f:
94 pickle.dump(new_cache, f, protocol=4)
95 os.replace(f.name, cache_file)