Skip to content

Commit c0cc19b

Browse files
committed
Delay worker count determination
os.cpu_count() can return None (sounds like a super arcane edge case though) so the type annotation for the `workers` parameter of `black.main` is wrong. This *could* technically cause a runtime TypeError since it'd trip one of mypyc's runtime type checks so we might as well fix it. Reading the documentation (and cross-checking with the source code), you are actually allowed to pass None as `max_workers` to `concurrent.futures.ProcessPoolExecutor`. If it is None, the pool initializer will simply call os.cpu_count() [^1] (defaulting to 1 if it returns None [^2]). It'll even round down the worker count to a level that's safe for Windows. ... so theoretically we don't even need to call os.cpu_count() ourselves, but our Windows limit is 60 (unlike the stdlib's 61) and I'd prefer not accidentally reintroducing a crash on machines with many, many CPU cores. [^1]: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor [^2]: https://github.com/python/cpython/blob/a372a7d65320396d44e8beb976e3a6c382963d4e/Lib/concurrent/futures/process.py#L600
1 parent afed2c0 commit c0cc19b

File tree

2 files changed

+9
-16
lines changed

2 files changed

+9
-16
lines changed

src/black/__init__.py

+3-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import io
22
import json
3-
import os
43
import platform
54
import re
65
import sys
@@ -28,11 +27,6 @@
2827
Union,
2928
)
3029

31-
if sys.version_info >= (3, 8):
32-
from typing import Final
33-
else:
34-
from typing_extensions import Final
35-
3630
import click
3731
from click.core import ParameterSource
3832
from mypy_extensions import mypyc_attr
@@ -92,7 +86,6 @@
9286
from blib2to3.pytree import Leaf, Node
9387

9488
COMPILED = Path(__file__).suffix in (".pyd", ".so")
95-
DEFAULT_WORKERS: Final = os.cpu_count()
9689

9790
# types
9891
FileContent = str
@@ -371,9 +364,8 @@ def validate_regex(
371364
"-W",
372365
"--workers",
373366
type=click.IntRange(min=1),
374-
default=DEFAULT_WORKERS,
375-
show_default=True,
376-
help="Number of parallel workers",
367+
default=None,
368+
help="Number of parallel workers [default: number of CPUs in the system]",
377369
)
378370
@click.option(
379371
"-q",
@@ -448,7 +440,7 @@ def main( # noqa: C901
448440
extend_exclude: Optional[Pattern[str]],
449441
force_exclude: Optional[Pattern[str]],
450442
stdin_filename: Optional[str],
451-
workers: int,
443+
workers: Optional[int],
452444
src: Tuple[str, ...],
453445
config: Optional[str],
454446
) -> None:

src/black/concurrency.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import asyncio
88
import logging
9+
import os
910
import signal
1011
import sys
1112
from concurrent.futures import Executor, ProcessPoolExecutor, ThreadPoolExecutor
@@ -15,7 +16,7 @@
1516

1617
from mypy_extensions import mypyc_attr
1718

18-
from black import DEFAULT_WORKERS, WriteBack, format_file_in_place
19+
from black import WriteBack, format_file_in_place
1920
from black.cache import Cache, filter_cached, read_cache, write_cache
2021
from black.mode import Mode
2122
from black.output import err
@@ -87,13 +88,13 @@ def reformat_many(
8788
maybe_install_uvloop()
8889

8990
executor: Executor
90-
worker_count = workers if workers is not None else DEFAULT_WORKERS
91+
if workers is None:
92+
workers = os.cpu_count() or 1
9193
if sys.platform == "win32":
9294
# Work around https://bugs.python.org/issue26903
93-
assert worker_count is not None
94-
worker_count = min(worker_count, 60)
95+
workers = min(workers, 60)
9596
try:
96-
executor = ProcessPoolExecutor(max_workers=worker_count)
97+
executor = ProcessPoolExecutor(max_workers=workers)
9798
except (ImportError, NotImplementedError, OSError):
9899
# we arrive here if the underlying system does not support multi-processing
99100
# like in AWS Lambda or Termux, in which case we gracefully fallback to

0 commit comments

Comments
 (0)