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 --extend-exclude parameter #2005

Merged
merged 10 commits into from Mar 1, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 11 additions & 19 deletions README.md
Expand Up @@ -135,11 +135,17 @@ Options:
hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_bu
ild|buck-out|build|dist)/]

--extend-exclude TEXT Like --exclude, but adds additional files
thejcannon marked this conversation as resolved.
Show resolved Hide resolved
and directories on top of the excluded
ones (useful if you simply want to add to
the default).

--force-exclude TEXT Like --exclude, but files and directories
matching this regex will be excluded even
when they are passed explicitly as
arguments.


--stdin-filename TEXT The name of the file when passing it through
stdin. Useful to make sure Black will
respect --force-exclude option on some
Expand All @@ -151,7 +157,7 @@ Options:

-v, --verbose Also emit messages to stderr about files
that were not changed or were ignored due to
--exclude=.
exclusion patterns.

--version Show the version and exit.
--config FILE Read configuration from FILE path.
Expand Down Expand Up @@ -263,7 +269,7 @@ above. What seems like a bug might be intended behaviour.

_Black_ is able to read project-specific default values for its command line options
from a `pyproject.toml` file. This is especially useful for specifying custom
`--include` and `--exclude` patterns for your project.
`--include` and `--exclude`/`--extend-exclude` patterns for your project.

**Pro-tip**: If you're asking yourself "Do I need to configure anything?" the answer is
"No". _Black_ is all about sensible defaults.
Expand Down Expand Up @@ -313,25 +319,10 @@ expressions by Black. Use `[ ]` to denote a significant space character.
line-length = 88
target-version = ['py37']
include = '\.pyi?$'
exclude = '''
extend-exclude = '''
# A regex preceded with ^/ will apply only to files and directories
# in the root of the project.
^/(
(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
| foo.py # also separately exclude a file named foo.py in
# the root of the project
)
^/foo.py # exclude a file named foo.py in the root of the project (in addition to the defaults)
'''
```

Expand Down Expand Up @@ -616,6 +607,7 @@ Multiple contributions by:
- [Joseph Larson](mailto:larson.joseph@gmail.com)
- [Josh Bode](mailto:joshbode@fastmail.com)
- [Josh Holland](mailto:anowlcalledjosh@gmail.com)
- [Joshua Cannon](mailto:joshdcannon@gmail.com)
- [José Padilla](mailto:jpadilla@webapplicate.com)
- [Juan Luis Cano Rodríguez](mailto:hello@juanlu.space)
- [kaiix](mailto:kvn.hou@gmail.com)
Expand Down
2 changes: 2 additions & 0 deletions docs/change_log.md
Expand Up @@ -28,6 +28,8 @@

- use lowercase hex strings (#1692)

- added `--extend-exclude` argument (#1571)

#### _Packaging_

- Self-contained native _Black_ binaries are now provided for releases via GitHub
Expand Down
7 changes: 6 additions & 1 deletion docs/installation_and_usage.md
Expand Up @@ -95,6 +95,11 @@ Options:
when they are passed explicitly as
arguments.

--extend-exclude TEXT Like --exclude, but adds additional files
and directories on top of the excluded
ones. (useful if you simply want to add to
the default)

--stdin-filename TEXT The name of the file when passing it through
stdin. Useful to make sure Black will
respect --force-exclude option on some
Expand All @@ -106,7 +111,7 @@ Options:

-v, --verbose Also emit messages to stderr about files
that were not changed or were ignored due to
--exclude=.
exclusion patterns.

--version Show the version and exit.
--config FILE Read configuration from FILE path.
Expand Down
22 changes: 4 additions & 18 deletions docs/pyproject_toml.md
Expand Up @@ -4,7 +4,8 @@

_Black_ is able to read project-specific default values for its command line options
from a `pyproject.toml` file. This is especially useful for specifying custom
`--include` and `--exclude` patterns for your project.
`--include` and `--exclude`/`--force-exclude`/`--extend-exclude` patterns for your
project.

**Pro-tip**: If you're asking yourself "Do I need to configure anything?" the answer is
"No". _Black_ is all about sensible defaults.
Expand Down Expand Up @@ -54,25 +55,10 @@ expressions by Black. Use `[ ]` to denote a significant space character.
line-length = 88
target-version = ['py37']
include = '\.pyi?$'
exclude = '''
extend-exclude = '''
# A regex preceded with ^/ will apply only to files and directories
# in the root of the project.
^/(
(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
| foo.py # also separately exclude a file named foo.py in
# the root of the project
)
^/foo.py # exclude a file named foo.py in the root of the project (in addition to the defaults)
'''
```

Expand Down
13 changes: 1 addition & 12 deletions pyproject.toml
Expand Up @@ -9,19 +9,8 @@
line-length = 88
target-version = ['py36', 'py37', 'py38']
include = '\.pyi?$'
exclude = '''
extend-exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist

# The following are specific to Black, you probably don't want those.
| blib2to3
| tests/data
Expand Down
78 changes: 52 additions & 26 deletions src/black/__init__.py
Expand Up @@ -461,6 +461,14 @@ def target_version_option_callback(
),
show_default=True,
)
@click.option(
"--extend-exclude",
type=str,
help=(
"Like --exclude, but adds additional files and directories on top of the"
" excluded ones. (Useful if you simply want to add to the default)"
),
)
@click.option(
"--force-exclude",
type=str,
Expand Down Expand Up @@ -493,7 +501,7 @@ def target_version_option_callback(
is_flag=True,
help=(
"Also emit messages to stderr about files that were not changed or were ignored"
" due to --exclude=."
" due to exclusion patterns."
),
)
@click.version_option(version=__version__)
Expand Down Expand Up @@ -537,6 +545,7 @@ def main(
verbose: bool,
include: str,
exclude: str,
extend_exclude: Optional[str],
force_exclude: Optional[str],
stdin_filename: Optional[str],
src: Tuple[str, ...],
Expand Down Expand Up @@ -570,6 +579,7 @@ def main(
verbose=verbose,
include=include,
exclude=exclude,
extend_exclude=extend_exclude,
force_exclude=force_exclude,
report=report,
stdin_filename=stdin_filename,
Expand Down Expand Up @@ -602,6 +612,18 @@ def main(
ctx.exit(report.return_code)


def test_regex(
ctx: click.Context,
regex_name: str,
regex: Optional[str],
) -> Optional[Pattern]:
thejcannon marked this conversation as resolved.
Show resolved Hide resolved
try:
return re_compile_maybe_verbose(regex) if regex is not None else None
except re.error:
err(f"Invalid regular expression for {regex_name} given: {regex!r}")
ctx.exit(2)
thejcannon marked this conversation as resolved.
Show resolved Hide resolved


def get_sources(
*,
ctx: click.Context,
Expand All @@ -610,28 +632,18 @@ def get_sources(
verbose: bool,
include: str,
exclude: str,
extend_exclude: Optional[str],
force_exclude: Optional[str],
report: "Report",
stdin_filename: Optional[str],
) -> Set[Path]:
"""Compute the set of files to be formatted."""
try:
include_regex = re_compile_maybe_verbose(include)
except re.error:
err(f"Invalid regular expression for include given: {include!r}")
ctx.exit(2)
try:
exclude_regex = re_compile_maybe_verbose(exclude)
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 force_exclude given: {force_exclude!r}")
ctx.exit(2)

include_regex = test_regex(ctx, "include", include)
exclude_regex = test_regex(ctx, "exclude", exclude)
assert exclude_regex is not None
extend_exclude_regex = test_regex(ctx, "extend_exclude", extend_exclude)
force_exclude_regex = test_regex(ctx, "force_exclude", force_exclude)

root = find_project_root(src)
sources: Set[Path] = set()
Expand Down Expand Up @@ -672,6 +684,7 @@ def get_sources(
root,
include_regex,
exclude_regex,
extend_exclude_regex,
force_exclude_regex,
report,
gitignore,
Expand Down Expand Up @@ -6110,17 +6123,27 @@ def normalize_path_maybe_ignore(
return normalized_path


def path_is_excluded(
normalized_path: str,
pattern: Optional[Pattern[str]],
) -> bool:
match = pattern.search(normalized_path) if pattern else None
return bool(match and match.group(0))


def gen_python_files(
paths: Iterable[Path],
root: Path,
include: Optional[Pattern[str]],
exclude: Pattern[str],
extend_exclude: Optional[Pattern[str]],
force_exclude: Optional[Pattern[str]],
report: "Report",
gitignore: PathSpec,
) -> Iterator[Path]:
"""Generate all files under `path` whose paths are not excluded by the
`exclude_regex` or `force_exclude` regexes, but are included by the `include` regex.
`exclude_regex`, `extend_exclude`, or `force_exclude` regexes,
but are included by the `include` regex.

Symbolic links pointing outside of the `root` directory are ignored.

Expand All @@ -6137,20 +6160,22 @@ def gen_python_files(
report.path_ignored(child, "matches the .gitignore file content")
continue

# Then ignore with `--exclude` and `--force-exclude` options.
# Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options.
normalized_path = "/" + normalized_path
if child.is_dir():
normalized_path += "/"

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

force_exclude_match = (
force_exclude.search(normalized_path) if force_exclude else None
)
if force_exclude_match and force_exclude_match.group(0):
if path_is_excluded(normalized_path, extend_exclude):
report.path_ignored(
child, "matches the --extend-exclude regular expression"
)
continue

if path_is_excluded(normalized_path, force_exclude):
report.path_ignored(child, "matches the --force-exclude regular expression")
continue

Expand All @@ -6160,6 +6185,7 @@ def gen_python_files(
root,
include,
exclude,
extend_exclude,
force_exclude,
report,
gitignore,
Expand Down