mrkeyoor.com_
Thu 06 Aug 02:42 UTC
PyPICLI & Toolingupdated 06 Aug 2026

black

Black reformats Python files in place to one fixed style. You run black on a file or directory and it rewrites the code: line length 88, double quotes, one consistent way to break long calls and collections, blank lines normalized. It ignores how the code was formatted before and re-derives the layout from the parse tree, which is why two files that started out very differently come out looking identical. Configuration is deliberately tiny; line length and a couple of escape hatches are essentially all you get. Before writing anything it re-parses its own output and checks the AST is equivalent to the original, so a formatter bug fails loudly instead of silently changing your program. It is maintained by the Python Software Foundation and versioned by calendar date, with a new stable style promoted once a year.

Verdict

Black defined what Python code looks like now, and it is still the safest zero-argument choice for a team that wants the debate over. For a new project, compare it against ruff format first, which produces nearly the same output in a fraction of the time and replaces two other tools while it is at it.

API stability4/5The CLI flags and pyproject options have barely moved in years and the documented Stability Policy governs how style changes graduate, but the output itself changes annually: 26.1.0 promoted nine preview behaviors into the stable style at once.
Docs5/5black.readthedocs.io documents the current style and the planned future style as separate references, publishes the stability policy, explains the pragmatism exceptions, and covers editor, CI, and pre-commit integration in detail.
Maintenance5/5Owned by the Python Software Foundation with several active maintainers, pushed August 2026, releases roughly monthly through 2026, and 269 open issues (298 counting PRs) against a very large user base.
Ecosystem5/5Around 42 million weekly downloads, an official GitHub Action, a dedicated pre-commit mirror, plugins for every editor, and a style that ruff format explicitly targets for compatibility.

Use it if

  • You want formatting arguments to stop happening in code review, permanently, and are willing to accept a style nobody on the team chose
  • You are contributing to or maintaining a project that already requires it: pytest, Django, pandas, SQLAlchemy, Poetry, attrs, Home Assistant, and a long list of others gate CI on black --check
  • You want a safety guarantee, not just a pretty printer: every file is re-parsed after formatting and compared for AST equivalence, so a formatting bug cannot quietly change behavior
  • You are adopting a formatter on a legacy codebase gradually and need --line-ranges to reformat only the lines a change touches instead of exploding the diff
  • You format Jupyter notebooks and need control over which cell magics get treated as Python, which black[jupyter] exposes through --python-cell-magics
Skip it if

Setup reality

pip install black needs Python 3.10 or later and pulls click, packaging, pathspec, platformdirs, pytokens, and mypy-extensions. Notebooks need pip install black[jupyter] (ipython plus tokenize-rt), the blackd HTTP server needs black[d], and there are prebuilt standalone binaries on the GitHub releases page if you do not want a Python environment at all. Configuration goes in pyproject.toml under [tool.black], and Black finds that file by walking up from the sources, which surprises people in monorepos. Adopting it on an existing repo produces one enormous commit, so record its hash in .git-blame-ignore-revs before anyone runs git blame. Pin the version in CI and pre-commit and use --required-version, because the annual style bump will otherwise turn a green build red without a code change. For pre-commit, the psf/black-pre-commit-mirror repo is the recommended source rather than psf/black itself. If you also run isort, it needs profile = "black" or the two will fight over import block formatting forever. One more upgrade trap: 26.1.0 moved to pathspec v1 so .gitignore matching now follows git's real semantics, and files that used to be reformatted through an unignored subdirectory are now skipped.

Patterns

Reformat files in placeformat-a-project

black .
black src/ tests/
python -m black src/        # when the console script is not on PATH

# All done! 1 file reformatted, 42 files left unchanged.

Black rewrites files in place with no backup, so commit or stash first the very first time you run it. It skips anything matched by .gitignore and by its own default exclude list (.venv, build, dist and friends).

Fail the build when code is not formattedcheck-in-ci

black --check --diff .

# exit 0   nothing would change
# exit 1   some files would be reformatted
# exit 123 internal error

--check alone tells you a file is wrong; adding --diff prints the exact patch, which saves a round trip for the contributor. Pair it with --required-version so a CI runner on a different Black release fails with a clear message instead of a mysterious diff.

Set the few options that existconfigure-pyproject

# pyproject.toml
[tool.black]
line-length = 100
target-version = ["py311", "py312"]
skip-string-normalization = false
skip-magic-trailing-comma = false
required-version = "26"
preview = false

That is close to the complete list of style knobs; there is no option for quote style beyond the on/off switch, indentation, or alignment. target-version turns off syntax that older interpreters cannot parse, and Black warns if you name a version newer than the interpreter running it because the AST safety check cannot parse it.

Keep Black out of generated or vendored codeexclude-paths

[tool.black]
extend-exclude = '''
(
  ^/migrations/
  | ^/vendor/
  | _pb2\.pyi?$
)
'''
force-exclude = '^/generated/'

These are regexes matched against the path, not glob patterns, and extend-exclude adds to the built-in list while exclude replaces it. force-exclude is the one that still applies when a file is named explicitly, which is what pre-commit does, so use it for anything that must never be touched.

Protect hand-formatted code from reformattingdisable-inline

# fmt: off
MATRIX = [
    1, 0, 0,
    0, 1, 0,
    0, 0, 1,
]
# fmt: on

result = some_call(a, b, c)  # fmt: skip

fmt: off and fmt: on must sit at the same indentation level and bracket a whole statement range; fmt: skip applies to one logical line. Both are honored but are a steady source of edge-case bug reports, so keep them rare.

Force a collection to stay explodedmagic-trailing-comma

# no trailing comma: Black collapses this if it fits
COLORS = ["red", "green", "blue"]

# trailing comma present: Black keeps one item per line forever
COLORS = [
    "red",
    "green",
    "blue",
]

This is the main way you influence Black's output without a config flag: a trailing comma you typed is treated as an instruction. --skip-magic-trailing-comma turns the behavior off globally, which will collapse a lot of deliberately exploded literals in one commit.

Run Black on staged filespre-commit-hook

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black-pre-commit-mirror
    rev: 26.5.1
    hooks:
      - id: black
        # id: black-jupyter  for notebooks

Use the black-pre-commit-mirror repo rather than psf/black; it is the documented, faster source. Pin rev to an exact version and bump it deliberately, since pre-commit autoupdate crossing a January release will reformat your whole repo.

Format only the lines you changedgradual-adoption

black --line-ranges=41-83 legacy/reports.py

# roughly, only the lines in this commit:
git diff -U0 --name-only -- '*.py' | while read f; do
  black --line-ranges="$(git diff -U0 -- "$f" | grep -oP '(?<=^@@ -)\d+(,\d+)?' | head -1)-9999" "$f"
done

--line-ranges lets a large legacy repo converge one pull request at a time instead of one giant commit. Ranges are 1-indexed and inclusive, and Black still parses the whole file, so a syntax error anywhere stops it.

Hide the big reformat commit from git blamekeep-blame-clean

black .
git commit -am "style: apply black 26.5.1"
git rev-parse HEAD >> .git-blame-ignore-revs
git config blame.ignoreRevsFile .git-blame-ignore-revs

Without this, every line in the repo shows the reformat commit as its last author. GitHub reads .git-blame-ignore-revs automatically, but each developer has to set the local git config themselves for it to work in their terminal.

See next year's style before it landstry-preview-style

black --preview --diff .

# individually opt into an unstable feature:
black --preview --unstable \
      --enable-unstable-feature=string_processing src/

Preview changes become the stable style in a future January release, so running --diff against preview tells you what your next upgrade commit will look like. Do not enable preview in CI: the feature list changes between releases and unstable features carry no guarantee they will ship at all.

Format Jupyter notebooks including cell magicsformat-notebooks

pip install "black[jupyter]"
black notebooks/
black --python-cell-magics=timeit,capture notebooks/analysis.ipynb

Without the jupyter extra, .ipynb files are skipped with a warning rather than an error, so a CI job can look green while formatting nothing. Only cell magics you list are formatted as Python; the rest are left alone.

Call Black from Pythonpython-api

import black

mode = black.Mode(
    line_length=100,
    string_normalization=True,
    magic_trailing_comma=True,
    target_versions={black.TargetVersion.PY311},
)

print(black.format_str('x = {  "a":1}', mode=mode))
# x = {"a": 1}

try:
    black.format_file_contents(source, fast=False, mode=mode)
except black.NothingChanged:
    pass

format_str always returns a string; format_file_contents raises NothingChanged when the input was already formatted, which is the signal you branch on. The docs treat this API as an implementation detail rather than a stable contract, so pin the version if you build on it, or run the blackd HTTP server instead.

Alternatives

PackageRegistryPick it when
ruffPyPIYou want the same style far faster, plus linting and import sorting in one tool; this is the default recommendation for new projects.
yapfPyPIYour team genuinely needs a configurable style (column alignment, custom indentation) and is willing to own that config forever.
autopep8PyPIYou only want minimal PEP 8 fixes applied to existing code rather than a full reformat, for example on a codebase where a wholesale rewrite is politically impossible.