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 --exclude and --exclude-glob flags #2153

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
32 changes: 32 additions & 0 deletions docs/configuration/options.md
Original file line number Diff line number Diff line change
@@ -126,6 +126,7 @@ Files that isort should skip over. To even skip matching files that have been sp

- --sg
- --skip-glob
- --exclude

**Examples:**

@@ -156,6 +157,7 @@ Additional files that isort should skip over (extending --skip-glob). To even sk
**CLI Flags:**

- --extend-skip-glob
- --extend-exclude

**Examples:**

@@ -175,6 +177,36 @@ extend_skip_glob = ["my_*_module.py", "test/*"]
```

## Exclude Glob

Files that isort should skip over. Paths are being excluded as globs. To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files).

**Type:** List of Strings
**Default:** `frozenset()`
**Config default:** `[]`
**Python & Config File Name:** exclude-glob
**CLI Flags:**

- --exclude-glob

**Examples:**

### Example `.isort.cfg`

```
[settings]
exclude_glob=docs/**
```

### Example `pyproject.toml`

```
[tool.isort]
exclude_glob = ["docs/**"]
```

## Skip Gitignore

Treat project as a git repository and ignore files listed in .gitignore. To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files).
4 changes: 2 additions & 2 deletions isort/exceptions.py
Original file line number Diff line number Diff line change
@@ -70,8 +70,8 @@ class FileSkipSetting(FileSkipped):

def __init__(self, file_path: str, **kwargs: str):
super().__init__(
f"{file_path} was skipped as it's listed in 'skip' setting"
" or matches a glob in 'skip_glob' setting",
f"{file_path} was skipped as it's listed in 'skip' setting, "
"matches in 'exclude' setting or matches a glob in 'exclude-glob'",
file_path=file_path,
)

12 changes: 10 additions & 2 deletions isort/main.py
Original file line number Diff line number Diff line change
@@ -363,16 +363,24 @@ def _build_arg_parser() -> argparse.ArgumentParser:
target_group.add_argument(
"--sg",
"--skip-glob",
"--exclude",
help="Files that isort should skip over.",
dest="skip_glob",
action="append",
)
target_group.add_argument(
"--extend-skip-glob",
"--extend-exclude",
help="Additional files that isort should skip over (extending --skip-glob).",
dest="extend_skip_glob",
action="append",
)
target_group.add_argument(
"--exclude-glob",
help="Files that isort should skip over, In a glob notation.",
dest="exclude_glob",
action="append",
)
target_group.add_argument(
"--gitignore",
"--skip-gitignore",
@@ -1249,8 +1257,8 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] =
for was_skipped in skipped:
print(
f"{was_skipped} was skipped as it's listed in 'skip' setting, "
"matches a glob in 'skip_glob' setting, or is in a .gitignore file with "
"--skip-gitignore enabled."
"matches in 'exclude' setting or matches a glob in 'exclude-glob', "
"or is in a .gitignore file with --skip-gitignore enabled."
)
print(f"Skipped {num_skipped} files")

15 changes: 15 additions & 0 deletions isort/settings.py
Original file line number Diff line number Diff line change
@@ -147,6 +147,7 @@ class _Config:
extend_skip: FrozenSet[str] = frozenset()
skip_glob: FrozenSet[str] = frozenset()
extend_skip_glob: FrozenSet[str] = frozenset()
exclude_glob: FrozenSet[str] = frozenset()
skip_gitignore: bool = False
line_length: int = 79
wrap_length: int = 0
@@ -306,6 +307,7 @@ def __init__(
self._section_comments_end: Optional[Tuple[str, ...]] = None
self._skips: Optional[FrozenSet[str]] = None
self._skip_globs: Optional[FrozenSet[str]] = None
self._exclude_globs: Optional[FrozenSet[str]] = None
self._sorting_function: Optional[Callable[..., List[str]]] = None

if config:
@@ -317,6 +319,7 @@ def __init__(
config_vars.pop("_section_comments_end")
config_vars.pop("_skips")
config_vars.pop("_skip_globs")
config_vars.pop("_exclude_globs")
config_vars.pop("_sorting_function")
super().__init__(**config_vars)
return
@@ -619,6 +622,10 @@ def is_skipped(self, file_path: Path) -> bool:
if fnmatch.fnmatch(file_name, sglob) or fnmatch.fnmatch("/" + file_name, sglob):
return True

for exclude_glob in self.exclude_globs:
if file_path in Path(self.directory).glob(exclude_glob):
return True

if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)):
return True

@@ -703,6 +710,14 @@ def skip_globs(self) -> FrozenSet[str]:
self._skip_globs = self.skip_glob.union(self.extend_skip_glob)
return self._skip_globs

@property
def exclude_globs(self) -> FrozenSet[str]:
if self._exclude_globs is not None:
return self._exclude_globs

self._exclude_globs = self.exclude_glob
return self._exclude_globs

@property
def sorting_function(self) -> Callable[..., List[str]]:
if self._sorting_function is not None:
8 changes: 8 additions & 0 deletions scripts/build_config_option_docs.py
Original file line number Diff line number Diff line change
@@ -165,6 +165,14 @@ def __str__(self):
""",
pyproject_toml="""
extend_skip_glob = ["my_*_module.py", "test/*"]
""",
),
"exclude_glob": Example(
cfg="""
exclude_glob=docs/**
""",
pyproject_toml="""
exclude_glob = ["docs/**"]
""",
),
"known_third_party": Example(
4 changes: 4 additions & 0 deletions tests/integration/test_setting_combinations.py
Original file line number Diff line number Diff line change
@@ -181,6 +181,7 @@ def _raise(*a):
}
),
skip_glob=frozenset(),
exclude_glob=frozenset(),
skip_gitignore=True,
line_length=79,
wrap_length=0,
@@ -615,6 +616,7 @@ def _raise(*a):
}
),
"skip_glob": frozenset(),
"exclude_glob": frozenset(),
"skip_gitignore": False,
"line_length": 79,
"wrap_length": 0,
@@ -1046,6 +1048,7 @@ def test_isort_is_idempotent(config: isort.Config, disregard_skip: bool) -> None
}
),
skip_glob=frozenset(),
exclude_glob=frozenset(),
skip_gitignore=True,
line_length=79,
wrap_length=0,
@@ -1480,6 +1483,7 @@ def test_isort_is_idempotent(config: isort.Config, disregard_skip: bool) -> None
}
),
"skip_glob": frozenset(),
"exclude_glob": frozenset(),
"skip_gitignore": False,
"line_length": 79,
"wrap_length": 0,
47 changes: 47 additions & 0 deletions tests/unit/test_isort.py
Original file line number Diff line number Diff line change
@@ -3883,6 +3883,53 @@ def test_skip_glob(tmpdir, skip_glob_assert: Tuple[List[str], int, Set[str]]) ->
assert file_names == file_names_expected


@pytest.mark.parametrize(
"exclude_glob,skipped_count,file_names_expected",
(
(
[],
0,
{
os.sep.join(("code", "file.py")),
os.sep.join(("code", "file1.py")),
os.sep.join(("code", "code_sub", "file2.py")),
os.sep.join(("code", "code_sub", "file3.py")),
},
),
(["code/**"], 1, set()),
(["code/**/*.py"], 4, set()),
(
["code/code_sub/*.py"],
2,
{
os.sep.join(("code", "file.py")),
os.sep.join(("code", "file1.py")),
},
),
),
)
def test_exclude_glob(
tmpdir, exclude_glob: List[str], skipped_count: int, file_names_expected: Set[str]
) -> None:
base_dir = tmpdir.mkdir("build")
code_dir = base_dir.mkdir("code")
code_dir.join("file.py").write("import os")
code_dir.join("file1.py").write("import os")
code_subdir = code_dir.mkdir("code_sub")
code_subdir.join("file2.py").write("import os")
code_subdir.join("file3.py").write("import os")

config = Config(exclude_glob=exclude_glob, directory=str(base_dir))
skipped: List[str] = []
broken: List[str] = []
file_names = {
os.path.relpath(f, str(base_dir))
for f in files.find([str(base_dir)], config, skipped, broken)
}
assert len(skipped) == skipped_count
assert file_names == file_names_expected


def test_broken(tmpdir) -> None:
base_dir = tmpdir.mkdir("broken")