diff --git a/CHANGES.rst b/CHANGES.rst index 3f34bf3b7..d13a6c8f9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,13 +11,14 @@ Added - Black's ``target-version =`` configuration file option and ``-t`` / ``--target-version`` command line option - Fixed ----- - Compatibility of highlighting unit tests with Pygments 2.14.0. - In the CI test workflow, don't use environment variables to add a Black version constraint to the ``pip`` command. This fixes the Windows builds. - Pass Git errors to stderr correctly both in raw and encoded subprocess output mode. +- Add a work-around for cleaning up temporary directories. Needed for Python 3.7 on + Windows. 1.6.1_ - 2022-12-28 diff --git a/src/darker/tests/conftest.py b/src/darker/tests/conftest.py index fc0c54ab9..be830b3ae 100644 --- a/src/darker/tests/conftest.py +++ b/src/darker/tests/conftest.py @@ -10,6 +10,7 @@ from black import find_project_root as black_find_project_root from darker.git import _git_check_output_lines, git_get_version +from darker.utils import fix_py37_win_tempdir_permissions class GitRepoFixture: @@ -114,7 +115,10 @@ def git_repo(tmp_path, monkeypatch): # variables for any Git commands run by the fixture, let's explicitly remove # `GIT_DIR` in case a test should call Git directly: monkeypatch.delenv("GIT_DIR", raising=False) - return repository + + yield repository + + fix_py37_win_tempdir_permissions(repository.root) @pytest.fixture diff --git a/src/darker/tests/test_linting.py b/src/darker/tests/test_linting.py index d88a9631a..573ae6039 100644 --- a/src/darker/tests/test_linting.py +++ b/src/darker/tests/test_linting.py @@ -3,7 +3,6 @@ """Unit tests for :mod:`darker.linting`""" import os -import sys from pathlib import Path from textwrap import dedent from unittest.mock import call, patch @@ -13,9 +12,10 @@ from darker import linting from darker.git import WORKTREE, RevisionRange from darker.tests.helpers import raises_if_exception +from darker.utils import WINDOWS -SKIP_ON_WINDOWS = [pytest.mark.skip] if sys.platform.startswith("win") else [] -SKIP_ON_UNIX = [] if sys.platform.startswith("win") else [pytest.mark.skip] +SKIP_ON_WINDOWS = [pytest.mark.skip] if WINDOWS else [] +SKIP_ON_UNIX = [] if WINDOWS else [pytest.mark.skip] @pytest.mark.kwparametrize( diff --git a/src/darker/utils.py b/src/darker/utils.py index d7ad3b017..812b3a302 100644 --- a/src/darker/utils.py +++ b/src/darker/utils.py @@ -2,17 +2,20 @@ import io import logging +import os +import sys import tokenize from datetime import datetime from itertools import chain from pathlib import Path -from typing import Collection, Iterable, List, Tuple +from typing import Collection, Iterable, List, Tuple, Union logger = logging.getLogger(__name__) TextLines = Tuple[str, ...] +WINDOWS = sys.platform.startswith("win") GIT_DATEFORMAT = "%Y-%m-%d %H:%M:%S.%f +0000" @@ -237,3 +240,27 @@ def glob_any(path: Path, patterns: Collection[str]) -> bool: """ return any(path.glob(pattern) for pattern in patterns) + + +def fix_py37_win_tempdir_permissions(dirpath: Union[str, Path]) -> None: + """Work around a `tempfile` clean-up issue on Windows with Python 3.7 + + Call this before exiting a ``with TemporaryDirectory():`` block or in teardown for + a Pytest fixture which creates a temporary directory. + + See discussion in https://github.com/akaihola/darker/pull/393 + Solution borrowed from https://github.com/python/cpython/pull/10320 + + :param dirpath: The root path of the temporary directory + + """ + if not WINDOWS or sys.version_info >= (3, 8): + return + for root, dirs, files in os.walk(dirpath): + for name in dirs + files: + path = os.path.join(root, name) + try: + os.chflags(path, 0) # type: ignore[attr-defined] + except AttributeError: + pass + os.chmod(path, 0o700)