Allow setting custom cache directory on all platforms (#2739)

Fixes #2506

``XDG_CACHE_HOME`` does not work on Windows. To allow for users to set a custom cache directory on all systems I added a new environment variable ``BLACK_CACHE_DIR`` to set the cache directory. The default remains the same so users will only notice a change if that environment variable is set.

The specific use case I have for this is I need to run black on in different processes at the same time. There is a race condition with the cache pickle file that made this rather difficult. A custom cache directory will remove the race condition.

I created ``get_cache_dir`` function in order to test the logic. This is only used to set the ``CACHE_DIR`` constant.
This commit is contained in:
Perry Vargas 2022-01-21 22:00:33 -08:00 committed by GitHub
parent d24bc4364c
commit 10677baa40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 6 deletions

View File

@ -20,6 +20,8 @@
- Tuple unpacking on `return` and `yield` constructs now implies 3.8+ (#2700) - Tuple unpacking on `return` and `yield` constructs now implies 3.8+ (#2700)
- Unparenthesized tuples on annotated assignments (e.g - Unparenthesized tuples on annotated assignments (e.g
`values: Tuple[int, ...] = 1, 2, 3`) now implies 3.8+ (#2708) `values: Tuple[int, ...] = 1, 2, 3`) now implies 3.8+ (#2708)
- Allow setting custom cache directory on all platforms with environment variable
`BLACK_CACHE_DIR` (#2739).
- Text coloring added in the final statistics (#2712) - Text coloring added in the final statistics (#2712)
- For stubs, one blank line between class attributes and methods is now kept if there's - For stubs, one blank line between class attributes and methods is now kept if there's
at least one pre-existing blank line (#2736) at least one pre-existing blank line (#2736)

View File

@ -96,6 +96,8 @@ Caching
.. autofunction:: black.cache.filter_cached .. autofunction:: black.cache.filter_cached
.. autofunction:: black.cache.get_cache_dir
.. autofunction:: black.cache.get_cache_file .. autofunction:: black.cache.get_cache_file
.. autofunction:: black.cache.get_cache_info .. autofunction:: black.cache.get_cache_info

View File

@ -22,10 +22,12 @@ run. The file is non-portable. The standard location on common operating systems
`file-mode` is an int flag that determines whether the file was formatted as 3.6+ only, `file-mode` is an int flag that determines whether the file was formatted as 3.6+ only,
as .pyi, and whether string normalization was omitted. as .pyi, and whether string normalization was omitted.
To override the location of these files on macOS or Linux, set the environment variable To override the location of these files on all systems, set the environment variable
`XDG_CACHE_HOME` to your preferred location. For example, if you want to put the cache `BLACK_CACHE_DIR` to the preferred location. Alternatively on macOS and Linux, set
in the directory you're running _Black_ from, set `XDG_CACHE_HOME=.cache`. _Black_ will `XDG_CACHE_HOME` to you your preferred location. For example, if you want to put the
then write the above files to `.cache/black/<version>/`. cache in the directory you're running _Black_ from, set `BLACK_CACHE_DIR=.cache/black`.
_Black_ will then write the above files to `.cache/black`. Note that `BLACK_CACHE_DIR`
will take precedence over `XDG_CACHE_HOME` if both are set.
## .gitignore ## .gitignore

View File

@ -20,7 +20,23 @@
Cache = Dict[str, CacheInfo] Cache = Dict[str, CacheInfo]
CACHE_DIR = Path(user_cache_dir("black", version=__version__)) def get_cache_dir() -> Path:
"""Get the cache directory used by black.
Users can customize this directory on all systems using `BLACK_CACHE_DIR`
environment variable. By default, the cache directory is the user cache directory
under the black application.
This result is immediately set to a constant `black.cache.CACHE_DIR` as to avoid
repeated calls.
"""
# NOTE: Function mostly exists as a clean way to test getting the cache directory.
default_cache_dir = user_cache_dir("black", version=__version__)
cache_dir = Path(os.environ.get("BLACK_CACHE_DIR", default_cache_dir))
return cache_dir
CACHE_DIR = get_cache_dir()
def read_cache(mode: Mode) -> Cache: def read_cache(mode: Mode) -> Cache:

View File

@ -40,7 +40,7 @@
import black.files import black.files
from black import Feature, TargetVersion from black import Feature, TargetVersion
from black import re_compile_maybe_verbose as compile_pattern from black import re_compile_maybe_verbose as compile_pattern
from black.cache import get_cache_file from black.cache import get_cache_dir, get_cache_file
from black.debug import DebugVisitor from black.debug import DebugVisitor
from black.output import color_diff, diff from black.output import color_diff, diff
from black.report import Report from black.report import Report
@ -1601,6 +1601,33 @@ def test_equivalency_ast_parse_failure_includes_error(self) -> None:
class TestCaching: class TestCaching:
def test_get_cache_dir(
self,
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Create multiple cache directories
workspace1 = tmp_path / "ws1"
workspace1.mkdir()
workspace2 = tmp_path / "ws2"
workspace2.mkdir()
# Force user_cache_dir to use the temporary directory for easier assertions
patch_user_cache_dir = patch(
target="black.cache.user_cache_dir",
autospec=True,
return_value=str(workspace1),
)
# If BLACK_CACHE_DIR is not set, use user_cache_dir
monkeypatch.delenv("BLACK_CACHE_DIR", raising=False)
with patch_user_cache_dir:
assert get_cache_dir() == workspace1
# If it is set, use the path provided in the env var.
monkeypatch.setenv("BLACK_CACHE_DIR", str(workspace2))
assert get_cache_dir() == workspace2
def test_cache_broken_file(self) -> None: def test_cache_broken_file(self) -> None:
mode = DEFAULT_MODE mode = DEFAULT_MODE
with cache_dir() as workspace: with cache_dir() as workspace: