Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compatibility with Black >= 22.1 #270

Merged
merged 5 commits into from
Feb 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
constraints: ['']
include:
- os: ubuntu-latest
python-version: '3.7'
constraints: '--constraint constraints-oldest.txt'

steps:
- uses: actions/checkout@v2
Expand All @@ -30,7 +35,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade 'pip>=20.3' # strict dependency resolution added in pip 20.3
pip install -e '.[isort,test]'
pip install -e '.[isort,test]' ${{ matrix.constraints }}
- name: Test with pytest
run: |
pytest
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Fixed
- ``regex`` module now always available for unit tests
- Compatibility with NixOS. Keep ``$PATH`` intact so Git can be called.
- Updated tests to pass on new Pygments versions
- Compatibility with Black 22.1


1.3.2_ - 2021-10-28
Expand Down
13 changes: 13 additions & 0 deletions constraints-oldest.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Constraints for pip to pin dependencies to oldest supported versions.
# This is used in a GitHub Workflow matrix job which ensures everything
# still works against oldest supported versions of both the Python
# interpreter and Python ependencies. Keep this up-to-date with minimum
# versions in `setup.cfg`.
black==21.8b0
mypy==0.910
pytest==6.1.0
pytest-flake8==1.0.6
pytest-isort==1.1.0
pytest-kwparametrize==0.0.3
regex==2021.4.4
types-toml==0.1.3
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ strict_equality = True
[mypy-darker.argparse_helpers]
disallow_any_explicit = False

[mypy-darker.black_compat]
disallow_any_explicit = False

[mypy-darker.command_line]
disallow_any_explicit = False

Expand Down
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ package_dir =
=src
packages = find:
install_requires =
# NOTE: remember to keep `constraints-oldest.txt` in sync with these
black>=21.5b1
toml
typing-extensions ; python_version < "3.8"
dataclasses ; python_version < "3.7"
# NOTE: remember to keep `.github/workflows/python-package.yml` in sync
# with the minimum required Python version
python_requires = >=3.6

[options.packages.find]
Expand All @@ -46,6 +49,7 @@ pygments.lexers =
isort =
isort>=5.0.1
test =
# NOTE: remember to keep `constraints-oldest.txt` in sync with these
black>=21.7b1 # to prevent Mypy error about `gen_python_files`, see issue #189
flake8<4
mypy>=0.910
Expand Down
21 changes: 21 additions & 0 deletions src/darker/black_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Functions for maintaining compatibility with multiple Black versions"""

from pathlib import Path
from typing import Any, Sequence, Tuple, cast

from black import find_project_root as black_find_project_root


def find_project_root(srcs: Sequence[str]) -> Path:
"""Hide changed return value type in Black behind this wrapper

:param srcs: Files and directories to find the common root for
:return: Project root path

"""
root = cast(Any, black_find_project_root(tuple(srcs or ["."])))
if isinstance(root, tuple):
# Black >= 22.1
return cast(Tuple[Path], root)[0]
# Black < 22
return cast(Path, root)
3 changes: 2 additions & 1 deletion src/darker/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from typing import Iterable, List, cast

import toml
from black import find_project_root

from darker.black_compat import find_project_root

if sys.version_info >= (3, 8):
from typing import TypedDict
Expand Down
3 changes: 1 addition & 2 deletions src/darker/import_sorting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
from pathlib import Path
from typing import Any, Optional

from black import find_project_root

from darker.black_compat import find_project_root
from darker.exceptions import IncompatiblePackageError, MissingPackageError
from darker.utils import TextDocument

Expand Down
12 changes: 9 additions & 3 deletions src/darker/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Dict, Optional

import pytest
from black import find_project_root
from black import find_project_root as black_find_project_root

from darker.git import _git_check_output_lines

Expand Down Expand Up @@ -97,5 +97,11 @@ def git_repo(tmp_path, monkeypatch):

@pytest.fixture
def find_project_root_cache_clear():
"""Clear LRU caching in :func:`black.find_project_root` before each test"""
find_project_root.cache_clear()
"""Clear LRU caching in :func:`black.find_project_root` before each test

NOTE: We use `darker.black_compat.find_project_root` to wrap Black's original
function since its signature has changed along the way. However, clearing the cache
needs to be done on the original of course.

"""
black_find_project_root.cache_clear()
25 changes: 22 additions & 3 deletions src/darker/tests/test_black_diff.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Unit tests for `darker.black_diff`"""

import re
from dataclasses import dataclass, field
from pathlib import Path
from unittest.mock import ANY, patch

Expand All @@ -17,6 +18,20 @@
from darker.utils import TextDocument


@dataclass
class RegexEquality:
"""Compare equality to either `re.Pattern` or `regex.Pattern`"""

pattern: str
flags: int = field(default=re.UNICODE)

def __eq__(self, other):
return (
other.pattern == self.pattern
and other.flags & 0x1FF == re.compile(self.pattern).flags | self.flags
)


@pytest.mark.kwparametrize(
dict(
config_path=None, config_lines=["line-length = 79"], expect={"line_length": 79}
Expand Down Expand Up @@ -46,15 +61,19 @@
dict(config_lines=[r"include = '\.pyi$'"], expect={}),
dict(
config_lines=[r"exclude = '\.pyx$'"],
expect={"exclude": re.compile("\\.pyx$")},
expect={"exclude": RegexEquality("\\.pyx$")},
),
dict(
config_lines=["extend-exclude = '''", r"^/setup\.py", r"|^/dummy\.py", "'''"],
expect={"extend_exclude": re.compile("(?x)^/setup\\.py\n|^/dummy\\.py\n")},
expect={
"extend_exclude": RegexEquality(
"(?x)^/setup\\.py\n|^/dummy\\.py\n", re.VERBOSE
)
},
),
dict(
config_lines=["force-exclude = '''", r"^/setup\.py", r"|\.pyc$", "'''"],
expect={"force_exclude": re.compile("(?x)^/setup\\.py\n|\\.pyc$\n")},
expect={"force_exclude": RegexEquality("(?x)^/setup\\.py\n|\\.pyc$\n")},
),
config_path=None,
)
Expand Down
13 changes: 10 additions & 3 deletions src/darker/tests/test_import_sorting.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Tests for :mod:`darker.import_sorting`"""

# pylint: disable=unused-argument

from importlib import reload
from pathlib import Path
from textwrap import dedent

import pytest
from black import find_project_root

import darker.import_sorting
from darker.tests.helpers import isort_present
Expand Down Expand Up @@ -76,9 +77,15 @@ def test_apply_isort(encoding, newline):
),
),
)
def test_isort_config(monkeypatch, tmpdir, line_length, settings_file, expect):
def test_isort_config(
monkeypatch,
tmpdir,
find_project_root_cache_clear,
line_length,
settings_file,
expect,
):
"""``apply_isort()`` parses ``pyproject.toml``correctly"""
find_project_root.cache_clear()
monkeypatch.chdir(tmpdir)
(tmpdir / "pyproject.toml").write(
dedent(
Expand Down
9 changes: 6 additions & 3 deletions src/darker/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from unittest.mock import call, patch

import pytest
from black import find_project_root

import darker.__main__
import darker.import_sorting
Expand All @@ -38,9 +37,13 @@ def test_isort_option_without_isort(git_repo, caplog):


@pytest.fixture
def run_isort(git_repo, monkeypatch, caplog, request):
find_project_root.cache_clear()
def run_isort(git_repo, monkeypatch, caplog, request, find_project_root_cache_clear):
"""Fixture for running Darker with requested arguments and a patched `isort`

Provides an `run_isort.isort_code` mock object which allows checking whether and how
the `isort.code()` function was called.

"""
monkeypatch.chdir(git_repo.root)
paths = git_repo.add({"test1.py": "original"}, commit="Initial commit")
paths["test1.py"].write_bytes(b"changed")
Expand Down