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 --force-exclude argument #1032

Merged
merged 15 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
73 changes: 52 additions & 21 deletions black.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Pattern,
Sequence,
Set,
Sized,
Tuple,
TypeVar,
Union,
Expand Down Expand Up @@ -360,6 +361,14 @@ def target_version_option_callback(
),
show_default=True,
)
@click.option(
"--force-exclude",
type=str,
help=(
"Like --exclude, but in these case files and directories will be excluded "
"even when they are passed explicitly as arguments"
),
)
@click.option(
"-q",
"--quiet",
Expand Down Expand Up @@ -412,6 +421,7 @@ def main(
verbose: bool,
include: str,
exclude: str,
force_exclude: Optional[str],
src: Tuple[str, ...],
config: Optional[str],
) -> None:
Expand Down Expand Up @@ -453,27 +463,48 @@ def main(
except re.error:
err(f"Invalid regular expression for exclude given: {exclude!r}")
ctx.exit(2)
try:
force_exclude_regex = (
re_compile_maybe_verbose(force_exclude) if force_exclude else None
)
except re.error:
err(f"Invalid regular expression for exclude given: {exclude!r}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should say force_exclude, not exclude.

ctx.exit(2)
report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose)
root = find_project_root(src)
sources: Set[Path] = set()
path_empty(src, quiet, verbose, ctx)
path_empty(src, "No Path provided. Nothing to do 😴", quiet, verbose, ctx)
for s in src:
p = Path(s)
if p.is_dir():
sources.update(
gen_python_files_in_dir(
p, root, include_regex, exclude_regex, report, get_gitignore(root)
gen_python_files(
p.iterdir(),
root,
include_regex,
exclude_regex,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should also obey the force_exclude_regex. It will be surprising if I force-exclude a file and it gets included anyway because it's in a directory I included.

report,
get_gitignore(root),
)
)
elif p.is_file() or s == "-":
# if a file was explicitly given, we don't care about its extension
elif s == "-":
sources.add(p)
elif p.is_file():
sources.update(
gen_python_files(
[p], root, None, force_exclude_regex, report, get_gitignore(root)
)
)
else:
err(f"invalid path: {s}")
if len(sources) == 0:
if verbose or not quiet:
out("No Python files are present to be formatted. Nothing to do 😴")
ctx.exit(0)

path_empty(
sources,
"No Python files are present to be formatted. Nothing to do 😴",
quiet,
verbose,
ctx,
)

if len(sources) == 1:
reformat_one(
Expand All @@ -495,14 +526,14 @@ def main(


def path_empty(
src: Tuple[str, ...], quiet: bool, verbose: bool, ctx: click.Context
src: Sized, msg: str, quiet: bool, verbose: bool, ctx: click.Context
) -> None:
"""
Exit if there is no `src` provided for formatting
"""
if not src:
if len(src) == 0:
if verbose or not quiet:
out("No Path provided. Nothing to do 😴")
out(msg)
ctx.exit(0)


Expand Down Expand Up @@ -3527,11 +3558,11 @@ def get_gitignore(root: Path) -> PathSpec:
return PathSpec.from_lines("gitwildmatch", lines)


def gen_python_files_in_dir(
path: Path,
def gen_python_files(
paths: Iterable[Path],
root: Path,
include: Pattern[str],
exclude: Pattern[str],
include: Optional[Pattern[str]],
exclude: Optional[Pattern[str]],
report: "Report",
gitignore: PathSpec,
) -> Iterator[Path]:
Expand All @@ -3543,7 +3574,7 @@ def gen_python_files_in_dir(
`report` is where output about exclusions goes.
"""
assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}"
for child in path.iterdir():
for child in paths:
# First ignore files matching .gitignore
if gitignore.match_file(child.as_posix()):
report.path_ignored(child, f"matches the .gitignore file content")
Expand All @@ -3568,18 +3599,18 @@ def gen_python_files_in_dir(
if child.is_dir():
normalized_path += "/"

exclude_match = exclude.search(normalized_path)
exclude_match = exclude.search(normalized_path) if exclude else None
if exclude_match and exclude_match.group(0):
report.path_ignored(child, f"matches the --exclude regular expression")
continue

if child.is_dir():
yield from gen_python_files_in_dir(
child, root, include, exclude, report, gitignore
yield from gen_python_files(
child.iterdir(), root, include, exclude, report, gitignore,
)

elif child.is_file():
include_match = include.search(normalized_path)
include_match = include.search(normalized_path) if include else True
if include_match:
yield child

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/reference_functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ File operations

.. autofunction:: black.find_project_root

.. autofunction:: black.gen_python_files_in_dir
.. autofunction:: black.gen_python_files

.. autofunction:: black.read_pyproject_toml

Expand Down
24 changes: 12 additions & 12 deletions tests/test_black.py
Original file line number Diff line number Diff line change
Expand Up @@ -1442,8 +1442,8 @@ def test_include_exclude(self) -> None:
]
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files_in_dir(
path, this_abs, include, exclude, report, gitignore
black.gen_python_files(
path.iterdir(), this_abs, include, exclude, report, gitignore
)
)
self.assertEqual(sorted(expected), sorted(sources))
Expand All @@ -1463,8 +1463,8 @@ def test_gitignore_exclude(self) -> None:
]
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files_in_dir(
path, this_abs, include, exclude, report, gitignore
black.gen_python_files(
path.iterdir(), this_abs, include, exclude, report, gitignore
)
)
self.assertEqual(sorted(expected), sorted(sources))
Expand All @@ -1488,8 +1488,8 @@ def test_empty_include(self) -> None:
]
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files_in_dir(
path,
black.gen_python_files(
path.iterdir(),
this_abs,
empty,
re.compile(black.DEFAULT_EXCLUDES),
Expand All @@ -1515,8 +1515,8 @@ def test_empty_exclude(self) -> None:
]
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files_in_dir(
path,
black.gen_python_files(
path.iterdir(),
this_abs,
re.compile(black.DEFAULT_INCLUDES),
empty,
Expand Down Expand Up @@ -1575,8 +1575,8 @@ def test_symlink_out_of_root_directory(self) -> None:
child.is_symlink.return_value = True
try:
list(
black.gen_python_files_in_dir(
path, root, include, exclude, report, gitignore
black.gen_python_files(
path.iterdir(), root, include, exclude, report, gitignore
)
)
except ValueError as ve:
Expand All @@ -1589,8 +1589,8 @@ def test_symlink_out_of_root_directory(self) -> None:
child.is_symlink.return_value = False
with self.assertRaises(ValueError):
list(
black.gen_python_files_in_dir(
path, root, include, exclude, report, gitignore
black.gen_python_files(
path.iterdir(), root, include, exclude, report, gitignore
)
)
path.iterdir.assert_called()
Expand Down