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

Respect isort's skip_glob setting #751

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ __pycache__/
/.vscode/
/build
/venv
/.venv
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ release =
[flake8]
# Line length according to Black rules
max-line-length = 88
# Skip some directories
exclude =
.*
build
# Ignore rules which conflict with Black
ignore =
# C408 Unnecessary dict call - rewrite as a literal.
Expand Down
1 change: 1 addition & 0 deletions src/darker/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def _modify_and_reformat_single_file( # noqa: PLR0913
rev2_isorted = apply_isort(
rev2_content,
relative_path_in_rev2,
root,
exclude.isort,
edited_linenums_differ,
formatter.get_config_path(),
Expand Down
44 changes: 25 additions & 19 deletions src/darker/import_sorting.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Helpers for invoking ``isort`` and acting on its output"""

import logging
from contextlib import suppress
from pathlib import Path
from typing import Any, Collection, List, Optional, TypedDict

Expand Down Expand Up @@ -51,9 +52,10 @@ class IsortArgs(TypedDict, total=False):
settings_path: str


def apply_isort( # pylint: disable=too-many-arguments
def apply_isort( # pylint: disable=too-many-arguments # noqa: PLR0913
content: TextDocument,
src: Path,
relative_path: Path,
root: Path,
exclude: Collection[str],
edited_linenums_differ: EditedLinenumsDiffer,
config: Optional[str] = None,
Expand All @@ -62,27 +64,28 @@ def apply_isort( # pylint: disable=too-many-arguments
"""Run isort on the given Python source file content

:param content: The contents of the Python source code file to sort imports in
:param src: The relative path to the file. This must be the actual path in the
repository, which may differ from the path given on the command line in
case of VSCode temporary files.
:param relative_path: The path to the file relative to ``root``. Note that this may
differ from the path given on the command line in case of
VSCode temporary files.
:param root: The root directory based on which ``src`` is resolved.
:param exclude: The file path patterns to exclude from import sorting
:param edited_linenums_differ: Helper for finding out which lines were edited
:param config: Path to configuration file
:param line_length: Maximum line length to use
:return: Original Python source file contents with imports sorted

"""
if glob_any(src, exclude):
if glob_any(relative_path, exclude):
return content
edited_linenums = edited_linenums_differ.revision_vs_lines(
src,
relative_path,
content,
context_lines=0,
) # pylint: disable=duplicate-code
if not edited_linenums:
return content
isort_args = _build_isort_args(src, config, line_length)
rev2_isorted = _call_isort_code(content, isort_args)
isort_args = _build_isort_args(root / relative_path, config, line_length)
rev2_isorted = _call_isort_code(content, root / relative_path, isort_args)
# Get the chunks in the diff between the edited and import-sorted file
isort_chunks = diff_chunks(content, rev2_isorted)
if not isort_chunks:
Expand All @@ -98,15 +101,16 @@ def apply_isort( # pylint: disable=too-many-arguments


def _build_isort_args(
src: Path,
path_from_cwd: Path,
config: Optional[str] = None,
line_length: Optional[int] = None,
) -> IsortArgs:
"""Build ``isort.code()`` keyword arguments

:param src: The relative path to the file. This must be the actual path in the
repository, which may differ from the path given on the command line in
case of VSCode temporary files.
:param path_from_cwd: The path to the file. This must be either an absolute path or
a relative path from the current working directory. Note that
this may differ from the path given on the command line in
case of VSCode temporary files.
:param config: Path to configuration file
:param line_length: Maximum line length to use

Expand All @@ -115,16 +119,20 @@ def _build_isort_args(
if config:
isort_args["settings_file"] = config
else:
isort_args["settings_path"] = str(find_project_root((str(src),)))
isort_args["settings_path"] = str(find_project_root((str(path_from_cwd),)))
if line_length:
isort_args["line_length"] = line_length
return isort_args


def _call_isort_code(content: TextDocument, isort_args: IsortArgs) -> TextDocument:
def _call_isort_code(
content: TextDocument, path_from_cwd: Path, isort_args: IsortArgs
) -> TextDocument:
"""Call ``isort.code()`` and return the result as a `TextDocument` object

:param content: The contents of the Python source code file to sort imports in
:param path_from_cwd: The path to the file with the given content, either relative
to the current working directory or an absolute path.
:param isort_args: Keyword arguments for ``isort.code()``

"""
Expand All @@ -133,10 +141,8 @@ def _call_isort_code(content: TextDocument, isort_args: IsortArgs) -> TextDocume
"isort.code(code=..., %s)",
", ".join(f"{k}={v!r}" for k, v in isort_args.items()),
)
try:
code = isort_code(code=code, **isort_args)
except isort.exceptions.FileSkipComment:
pass
with suppress(isort.exceptions.FileSkipped):
code = isort_code(code=code, file_path=path_from_cwd, **isort_args)
return TextDocument.from_str(
code,
encoding=content.encoding,
Expand Down
24 changes: 18 additions & 6 deletions src/darker/tests/test_import_sorting.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@
content_ = TextDocument.from_lines(content, encoding=encoding, newline=newline)

result = darker.import_sorting.apply_isort(
content_, src, exclude=[], edited_linenums_differ=edited_linenums_differ
content_,
src,
git_repo.root,

Check failure on line 76 in src/darker/tests/test_import_sorting.py

View workflow job for this annotation

GitHub Actions / flake8

undefined name 'git_repo'

Check failure on line 76 in src/darker/tests/test_import_sorting.py

View workflow job for this annotation

GitHub Actions / Mypy

src/darker/tests/test_import_sorting.py#L76

Name "git_repo" is not defined [name-defined]

Check failure on line 76 in src/darker/tests/test_import_sorting.py

View workflow job for this annotation

GitHub Actions / Pylint

src/darker/tests/test_import_sorting.py#L76

Undefined variable 'git_repo' (undefined-variable, E0602)
exclude=[],
edited_linenums_differ=edited_linenums_differ,
)

assert result.lines == expect
Expand Down Expand Up @@ -106,7 +110,11 @@
content_ = TextDocument.from_lines(content, encoding=encoding, newline=newline)

result = darker.import_sorting.apply_isort(
content_, src, exclude=exclude, edited_linenums_differ=edited_linenums_differ
content_,
src,
git_repo.root,

Check failure on line 115 in src/darker/tests/test_import_sorting.py

View workflow job for this annotation

GitHub Actions / flake8

undefined name 'git_repo'

Check failure on line 115 in src/darker/tests/test_import_sorting.py

View workflow job for this annotation

GitHub Actions / Mypy

src/darker/tests/test_import_sorting.py#L115

Name "git_repo" is not defined [name-defined]

Check failure on line 115 in src/darker/tests/test_import_sorting.py

View workflow job for this annotation

GitHub Actions / Pylint

src/darker/tests/test_import_sorting.py#L115

Undefined variable 'git_repo' (undefined-variable, E0602)
exclude=exclude,
edited_linenums_differ=edited_linenums_differ,
)

assert result.lines == expect
Expand Down Expand Up @@ -148,10 +156,10 @@
),
),
)
def test_isort_config(monkeypatch, tmpdir, line_length, settings_file, expect):
def test_isort_config(monkeypatch, tmp_path, line_length, settings_file, expect):
"""``apply_isort()`` parses ``pyproject.toml``correctly"""
monkeypatch.chdir(tmpdir)
(tmpdir / "pyproject.toml").write(
monkeypatch.chdir(tmp_path)
(tmp_path / "pyproject.toml").write_text(
dedent(
f"""\
[tool.isort]
Expand All @@ -161,11 +169,14 @@
)

content = "from module import ab, cd, ef, gh, ij, kl, mn, op, qr, st, uv, wx, yz"
config = str(tmpdir / settings_file) if settings_file else None
# isort doesn't read the file but skips formatting if the file doesn't exist:
(tmp_path / "test1.py").write_text(content)
config = str(tmp_path / settings_file) if settings_file else None

actual = darker.import_sorting.apply_isort(
TextDocument.from_str(content),
Path("test1.py"),
tmp_path,
set(),
EditedLinenumsDiffer(Path("."), RevisionRange("master", "HEAD")),
config,
Expand Down Expand Up @@ -206,6 +217,7 @@
actual = darker.import_sorting.apply_isort(
TextDocument.from_str(content),
Path("test1.py"),
Path(),
set(),
EditedLinenumsDiffer(Path("."), RevisionRange("master", "HEAD")),
)
Expand Down
26 changes: 26 additions & 0 deletions src/darker/tests/test_main_isort.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# pylint: disable=no-member,redefined-outer-name,unused-argument,use-dict-literal

from pathlib import Path
from textwrap import dedent
from types import SimpleNamespace
from unittest.mock import patch

Expand Down Expand Up @@ -90,6 +92,30 @@ def test_isort_option_with_isort_calls_sortimports(run_isort, isort_args):
"""Relevant config options are passed from command line to ``isort``."""
run_isort.isort_code.assert_called_once_with(
code="changed",
file_path=Path(run_isort.root / "test1.py"),
settings_path=str(run_isort.root),
**isort_args,
)


def test_isort_respects_skip_glob(tmp_path):
"""Test that Darker respects isort's skip_glob setting."""
# Create a pyproject.toml file with isort skip_glob configuration
configuration = dedent(
"""
[tool.isort]
skip_glob = ['*/conf/settings/*']
filter_files = true
"""
)
(tmp_path / "pyproject.toml").write_text(configuration)
# Create a file that should be skipped
settings_dir = tmp_path / "conf" / "settings"
settings_dir.mkdir(parents=True)
backoffice_py = settings_dir / "backoffice.py"
backoffice_py.write_text("import sys\nimport os\n")

# Run darker with --isort
darker.__main__.main(["--isort", str(settings_dir / "backoffice.py")])

assert backoffice_py.read_text() == "import sys\nimport os\n"
Loading