Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
- Ignore empty cache files like other malformed cache files instead of raising an
`EOFError` (#5192)
- Reject non-string `include` and `force-exclude` values in `pyproject.toml` (#5193)
- Validate `BLACK_NUM_WORKERS` values and report invalid values as usage errors instead
of crashing (#5211)

### Packaging

Expand Down
19 changes: 17 additions & 2 deletions src/black/concurrency.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from pathlib import Path
from typing import Any

import click
from mypy_extensions import mypyc_attr

from black import WriteBack, format_file_in_place
Expand Down Expand Up @@ -89,8 +90,22 @@ def reformat_many(
"""Reformat multiple files using a ProcessPoolExecutor."""

if workers is None:
workers = int(os.environ.get("BLACK_NUM_WORKERS", 0))
workers = workers or os.cpu_count() or 1
workers_value = os.environ.get("BLACK_NUM_WORKERS")
if workers_value is not None:
try:
workers = int(workers_value)
except ValueError:
raise click.BadParameter(
f"{workers_value!r} is not a valid integer",
param_hint="BLACK_NUM_WORKERS",
) from None
if workers < 1:
raise click.BadParameter(
f"{workers_value!r} is not in the range x>=1",
param_hint="BLACK_NUM_WORKERS",
)
else:
workers = os.cpu_count() or 1
if sys.platform == "win32":
# Work around https://bugs.python.org/issue26903
workers = min(workers, 60)
Expand Down
20 changes: 20 additions & 0 deletions tests/test_black.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,26 @@ def test_works_in_mono_process_only_environment(self) -> None:
f.write_text('print("hello")\n', encoding="utf-8")
self.invokeBlack([str(workspace)])

@event_loop()
def test_invalid_black_num_workers(self) -> None:
for workers in ["abc", "0", "-1"]:
with (
cache_dir() as workspace,
patch.dict(os.environ, {"BLACK_NUM_WORKERS": workers}),
):
for f in [
(workspace / "one.py").resolve(),
(workspace / "two.py").resolve(),
]:
f.write_text('print("hello")\n', encoding="utf-8")

result = BlackRunner().invoke(black.main, [str(workspace)])

assert result.exit_code == 2
assert result.exception is not None
assert "BLACK_NUM_WORKERS" in result.stderr
assert "Traceback" not in result.stderr

@event_loop()
def test_check_diff_use_together(self) -> None:
with cache_dir():
Expand Down
Loading