Repository navigation
Releases: psf/black
Release list
26.10.1
Highlights
This release fixes a security issue in Black's bundled GitHub Action. Repositories that
use the action are encouraged to update to the latest version of Black immediately. This
update is received automatically when using psf/black@stable, and is independent of
the version of Black installed by the action.
When the GitHub Action reads version specifiers from tool.black.required-version, it
now only accepts released versions of Black, as it already did for black requirements
in dependency lists. Other values, such as URL references, are now rejected. This issue
has been registered as GHSA-cg8m-r9f2-5wm2; a CVE identifier is pending.
Stable style
- Fix a long Jupyter notebook assignment magic (for example
x = !ls -la) being wrapped
in parentheses, which IPython can no longer run; such cells are now left unchanged
(#5481) - Keep repeated lines outside the selected
--line-rangesunchanged (#5436) - Fix
--line-rangesformatting lines that follow a statement when formatting joins or
re-indents that statement (#5484) - Fix
--line-rangescrashing with an internal error when an unselected statement has a
# fmt: skipcomment on the last line before a closing bracket (#5477) - Prevent moving an encoding declaration (for example
# -*- coding: latin-1 -*-) onto
the first two lines of a file (#5487) - Fix adding an extra trailing blank line inside a multi-line module docstring when its
last line is near the line length limit (#5494)
Preview style
- Do not split short expressions on delimiters (such as binary operators, comparisons,
or comprehensions) inside brackets when preceded by a standalone comment (#5455) - Fix the unstable
string_processingfeature removing backslash-newline sequences from
raw strings, which changed their value (#5482) - Avoid adding unnecessary parentheses around unbreakable right-hand side expressions in
assignments (such as annotated assignments or subscript targets) (#5473) - Parenthesize expressions with parameter comments when they exceed the line length
under--preview(#5442) - Fix
wrap_long_dict_values_in_parensdropping the parentheses around a right-hand
side that contains a dictionary, leaving a line over the length limit (#5492) - Fix the unstable
string_processingfeature adding a space after*or**in a PEP
695 type parameter (* Ts = ...) when it splits a string in the parameter's default
(#5504) - Avoid unnecessary line splits of trailers (such as indexing/subscripts) when an
earlier bracket split exceeds the line length only due to a trailing comment (#5498)
Configuration
- Add
--cache-dirto configure the cache directory from the command line (#5433) - Don't cache a file as formatted after formatting or checking it with
--line-ranges,
which made a later fullblack --checkpass andblackskip the unformatted lines
(#5476) - Respect a
.gitignorethat starts with a UTF-8 byte order mark. The mark became part
of the first pattern, so that pattern never matched and Black formatted files that git
ignores (#5497)
Performance
- Avoid quadratic runtime when splitting very long lines with many trailing bracket
pairs (such as consecutive subscripts) (#5475)
Integrations
- Fix the GitHub Action crashing on dependency-group includes when
use_pyprojectis
enabled (#5479) - When the GitHub Action reads version specifiers from
tool.black.required-version, it
now only accepts released versions of Black, as it already did forblack
requirements in dependency lists. Other values, such as URL references, are now
rejected. (#5510)
26.10.0
Stable style
--line-rangesno longer inserts an empty line after a docstring when the range
covers only the docstring itself (#5312)- Fix
# fmt: skipon a bracketed ternary turning the surrounding tuple into a call
(#5464) - Fix crash when
# fmt: skipis placed on a one-line function or class with PEP 695
type parameters (#5429) - Fix an inline comment after the closing bracket of optional parentheses being moved
inside the parentheses when the parenthesized expression contains own-line comments
(#5395) - Fix unparseable output when
# fmt: skipis placed on a bracket of anif,while,
for, orwithheader (#5401, #5405) - Fix crash when formatting parenthesized expressions with multiple inline comments and
# fmt: skip(#5414) - Fix parsing Jupyter notebook assignment magics when non-ASCII characters appear
earlier on the line (#5381) - Preserve blank lines that come immediately before a
# fmt: oncomment (#5300) - Keep the parentheses around the target of an annotated assignment (e.g.
(x): int = 5), which prevent CPython from including the name in__annotations__
(#5321) - Stop treating a t-string in docstring position as a docstring (e.g.
t" spam "as
the first statement of a module, class or function) (#5287) - Fix unparseable output for a t-string whose replacement field contains a quote (e.g.
t'\'{a["b"]}\'') (#5265) - Fix unparseable output for a triple-quoted string whose body ends in an
already-escaped double quote (e.g.'''\'''\"''') (#5262) - Fix
--skip-magic-trailing-commadropping the trailing comma from a split
single-element tuple used as a lambda parameter default (#5246) - Fix unstable formatting when an inline comment sits on optional parentheses (e.g. a
parenthesized assert message) (#5241) - Fix
--skip-magic-trailing-commadropping the trailing comma of a one-element
subscript (a[x,]) when the line is long enough to be split and contains a power
operator (#5272) - Fix crash when a standalone comment sits between tokens of a comprehension or lambda
(#5144) - Fix inline comments on a bracket inside a comprehension being dropped (#5330)
- Respect the magic trailing comma in a PEP 695 type parameter list containing a
*TypeVarTupleor**ParamSpec(#5244) - Fix crash when a comment-only
# fmt: off/# fmt: onblock is followed by awith
statement after another standalone comment (#5189) - Fix a crash when splitting
case case if ...match patterns at very small line
lengths (#5147) - Fix multiline docstring indentation when leading tabs are used inside indented
docstrings (#5148) - Respect
# fmt: skipon a line that opens a bracket (e.g.
from x import ( # fmt: skip) when a standalone comment is among the bracket's
contents (#5161) - Fix an AST safety error when separate
# type: ignorecomments in a parenthesized
attribute chain were merged onto one physical line (#5297) - Preserve comments and blank lines outside requested ranges when formatting with
--line-ranges(#5175) - Fix crash when
# fmt: skipis used on one-lineasync def,async with, and
async forstatements containing a semicolon (#5311) - Stop converting form feeds or other similar characters in docstrings into newlines
(#5461) - Fix
--skip-source-first-lineturning the skipped line's CRLF ending into\r\r\n
when reformatting a file with CRLF line endings (#5438)
Preview style
New preview features
- Add missing blank lines after classes whose last method has an ellipsis body (#5439)
- Split only the brackets holding a magic trailing comma when more trailers follow them
(e.g. stop splitting inside the[2]of[1,][2](3)) (#5448) - Keep dictionary keys containing operators together on one line when the value can be
wrapped onto a new line instead (#5435) - Remove redundant parentheses around individual variables in unpacking targets (e.g.
for (x), (y) in points:becomesfor x, y in points:) (#5416) - Normalize uppercase
Tprefixes on t-strings to lowercase (#5425) - Remove redundant parentheses around generator expressions (#5304, #5369)
- Preserve two blank lines before a top-level class starting inside a
# fmt: offblock
after an import (#5238) - Fix unnecessary parentheses around short right-hand expressions in indexed assignments
(e.g.x[key] = expr) (#5095) - Parenthesize tuple expressions in
yieldstatements for consistency with function
calls and returns (#5170) - Stop splitting between a variable and its operator (
not in,==,is, ...) when
the right-hand side is a bracketed expression, and instead split inside the brackets
(#5135) - In
.pyistub files, enforce a blank line after a function or method that has a
docstring-only body when another comment or statement follows it (#5158)
Updates to existing preview features
- Fix crash in stub files when
# fmt: skipis placed on a function in a group of
same-name decorated functions (e.g.@overloads or a property setter) (#5430) - Keep the parentheses around a lambda used as the iterable of a comprehension (e.g.
[x for x in (lambda: 0) if x]) (#5176, #5200)
Updates to existing unstable features
- Do not treat multi-line expressions with merged strings and trailing
# type: ignore
comments as single-line unsplittable expressions (#5466) - Fix duplicated inline comment when stripping the parentheses around a string or
merging a backslash-continued string on the same line (#5449) - Split long stringified return annotations even when the function has parameters
(#5427) - Don't hug brackets when doing so would join two
# type: ignorecomments onto one
line (#5271) - Fix a crash when
# type: ignoreis lost during formatting of a long parenthesized
string (#5329) - Fix only the first part of an implicitly concatenated unmergeable string (e.g.
r"..." r"...") being wrapped in parentheses (#5434)
Configuration
- Add support for
NO_COLORenvironment variable to disable ANSI output (#5129) - Remove spurious target version warning when runtime version is included in a
--target-versionflag (#5167) - Fix
--force-excludenot excluding files whose path contains..(e.g.
black ../generated/file.pyrun from a subdirectory) (#5471) - Fall back to the default configuration, with a warning, when the given sources share
no common project root (e.g. they are on different drives on Windows) instead of
crashing (#5386) - Fix loading a stale cached
pyproject.tomlpath when--codeis used from different
working directories in the same process (#5152) - Add validation for
--line-rangesvalues (#5107) - Ignore empty cache files instead of raising an
EOFError(#5192) - Reject non-string
includeandforce-excludevalues inpyproject.toml(#5193) - Validate
BLACK_NUM_WORKERSvalues and report invalid values as usage errors instead
of crashing (#5211) - Ignore permission errors when reading cache (#5258)
Packaging
- Reduce the size of Linux standalone binaries by stripping debug symbols during the
PyInstaller release build (#5223) - Black is now released using GitHub
Immutable Releases
(#5296)
Performance
- Fix superlinear runtime growth with the number of input files (#5450)
- Improve performance on strings containing many consecutive backslashes (#5163)
- Improve performance on files with many
# fmt: skip/# fmt: off/# fmt: oncomments
(#5169, #5190, #5232) - Improve performance on long calls and collections (#5177)
- Improve performance on multiline strings inside large collections (e.g. a dict literal
with triple-quoted strings as values) (#5188) - Improve performance on files with many soft keywords (e.g.
match/caseblocks)
(#5186) - Improve performance on long semicolon-separated statements (in the stable style) and
large dict literals (in--preview) (#5184) - Improve performance of
--line-rangeson files with many sibling blocks (a long
if/elifchain, amatchwith many cases, or many top-level definitions) (#5213) - Improve performance on deeply nested bracketed expressions (#5171, #5242)
- Improve performance on lists and subscripts holding one long expression without
operators (e.g. a long run of implicitly concatenated strings inside[]) (#5239) - Improve performance on deeply chained operations (e.g. a long
a ** b ** c ** ...
chain) (#5235) - Improve performance on long
if/elifchains and other compound statements with many
clauses (#5322) - Improve performance of
string_processing:
Output
- Report parser failures using editor-friendly
path:line:columnlocations (#5237) - Fix crash when writing formatted code or diffs to a
sys.stdoutthat has nobuffer
attribute (e.g. in Jupyter notebooks) (#5411) - Report parse failures on Black's own output as internal errors (#5383)
Blackd
- Return HTTP 400 instead of 500 when the
X-Python-Variantheader is empty or has an
empty entry (e.g. a trailing comma) (#5428) - Allow optional whitespace around comma-separated versions and
pyiin the
X-Python-Variantheader (#5441)
Integrations
- Remove unused
migrate-blackscript (#5319) - Support PEP 440 version specifiers in
tool.black.required-versionfor the GitHub
Action (#5399) - Add outputs (
is_formatted,change_count,same_count,failure_count) to GitHub
Action runs (#5408)
Documentation
- Document
vim-python-pep8-indent, which provides anindentexprfor Black-style
insert-mode indentation (#5288)
26.5.1
Stable style
- Fix unstable formatting of annotated assignments whose subscript annotation contains
an inline comment (e.g.x: list[ # pyright: ignore[...]) (#5130) - Preserve inline comments (including
# type: ignore) immediately before a
# fmt: skipline, avoiding AST equivalence failures (#5139)
Packaging
- Correct the version in the published executables (#5137)
Documentation
- Add Neovim integration guide covering conform.nvim, ALE, and simple command approaches
(#5124)
26.5.0
Highlights
- Add support for unpacking in comprehensions (PEP 798) and for lazy imports (PEP 810),
both new syntactic features in Python 3.15 (#5048) - Python 3.15 is now supported. Compiled wheels are not yet provided for Python 3.15, so
performance may be slower than on existing Python versions. Wheels will be provided
once Python 3.15 is later in its release cycle. (#5127)
Stable style
- Fix
# fmt: skipbeing ignored in nestedifexpressions with parenthesizedin
clauses (#4903) - Add syntactic support for Python 3.15 (#5048)
- Fix crash when an f-string follows a
# fmt: offcomment inside brackets (#5097) - Preserve multiline compound statement headers when
# fmt: skipis placed on the
colon line (#5117)
Preview style
- Improve heuristics around whether blank lines should appear before, within and after
groups of same-name decorated functions (such as@overloadgroups) in.pyistub
files (#5021) - Fix blank lines being removed between a function and a decorated class in
.pyistub
files (#5092) - Prevent string merger from creating unsplittable long lines when a pragma comment
(e.g.# type: ignore) follows the closing bracket (#5096)
Packaging
- Run CI on 3.15 (#5127)
Output
- Improve parse error readability by showing multi-line output with an error pointer.
(#5068) - Add
SourceASTParseErrorto distinguish source parse failures from internal safety
errors, improving error reporting when Black's lenient parser accepts input that
ast.parse()rejects (#5080)
Blackd
- Return HTTP 400 (Bad Request) for source parse failures instead of HTTP 500, keeping
HTTP 500 only for genuine internal safety errors (#5080)
Integrations
- Added documentation for doctest formatting tools and updated the integrations index to
match (#4916)
Documentation
26.3.1
Stable style
- Prevent Jupyter notebook magic masking collisions from corrupting cells by using
exact-length placeholders for short magics and aborting if a placeholder can no longer
be unmasked safely (#5038)
Configuration
- Always hash cache filename components derived from
--python-cell-magicsso custom
magic names cannot affect cache paths (#5038)
Blackd
- Disable browser-originated requests by default, add configurable origin allowlisting
and request body limits, and bound executor submissions to improve backpressure
(#5039)
26.3.0
Stable style
- Don't double-decode input, causing non-UTF-8 files to be corrupted (#4964)
- Fix crash on standalone comment in lambda default arguments (#4993)
- Preserve parentheses when
# type: ignorecomments would be merged with other
comments on the same line, preventing AST equivalence failures (#4888)
Preview style
- Fix bug where
ifguards incaseblocks were incorrectly split when the pattern had
a trailing comma (#4884) - Fix
string_processingcrashing on unassigned long string literals with trailing
commas (one-item tuples) (#4929) - Simplify implementation of the power operator "hugging" logic (#4918)
Packaging
- Fix shutdown errors in PyInstaller builds on macOS by disabling multiprocessing in
frozen environments (#4930)
Performance
- Introduce winloop for windows as an alternative to uvloop (#4996)
- Remove deprecated function
uvloop.install()in favor ofuvloop.new_event_loop()
(#4996) - Rename
maybe_install_uvloopfunction tomaybe_use_uvloopto simplify loop
installation and creation of either a uvloop/winloop evenloop or default eventloop
(#4996)
Output
- Emit a clear warning when the target Python version is newer than the running Python
version, since AST safety checks cannot parse newer syntax. Also replace the
misleading "INTERNAL ERROR" message with an actionable error explaining the version
mismatch (#4983)
Blackd
- Introduce winloop to be used when windows in use which enables blackd to run faster on
windows when winloop is installed. (#4996)
Integrations
- Remove unused gallery script (#5030)
- Harden parsing of
blackrequirements in the GitHub Action whenuse_pyprojectis
enabled so that only version specifiers are accepted and direct references such as
black @ https://...are rejected. Users should upgrade to the latest version of the
action as soon as possible. This update is received automatically when using
psf/black@stable, and is independent of the version of Black installed by the
action. (#5031)
Documentation
26.1.0
Highlights
Introduces the 2026 stable style (#4892), stabilizing the following changes:
always_one_newline_after_import: Always force one blank line after import
statements, except when the line after the import is a comment or an import statement
(#4489)fix_fmt_skip_in_one_liners: Fix# fmt: skipbehavior on one-liner declarations,
such asdef foo(): return "mock" # fmt: skip, where previously the declaration would
have been incorrectly collapsed (#4800)fix_module_docstring_detection: Fix module docstrings being treated as normal
strings if preceded by comments (#4764)fix_type_expansion_split: Fix type expansions split in generic functions (#4777)multiline_string_handling: Make expressions involving multiline strings more compact
(#1879)normalize_cr_newlines: Add\rstyle newlines to the potential newlines to
normalize file newlines both from and to (#4710)remove_parens_around_except_types: Remove parentheses around multiple exception
types inexceptandexcept*withoutas(#4720)remove_parens_from_assignment_lhs: Remove unnecessary parentheses from the left-hand
side of assignments while preserving magic trailing commas and intentional multiline
formatting (#4865)standardize_type_comments: Format type comments which have zero or more spaces
between#andtype:or betweentype:and value to# type: (value)(#4645)
The following change was not in any previous stable release:
- Regenerated the
_width_table.pyand added tests for the Khmer language (#4253)
This release alo bumps pathspec to v1 and fixes inconsistencies with Git's
.gitignore logic (#4958). Now, files will be ignored if a pattern matches them, even
if the parent directory is directly unignored. For example, Black would previously
format exclude/not_this/foo.py with this .gitignore:
exclude/
!exclude/not_this/
Now, exclude/not_this/foo.py will remain ignored. To ensure exclude/not_this/ and
all of it's children are included in formatting (and in Git), use this .gitignore:
*/exclude/*
!*/exclude/not_this/
This new behavior matches Git. The leading */ are only necessary if you wish to ignore
matching subdirectories (like the previous behavior did), and not just matching root
directories.
Output
- Explicitly shutdown the multiprocessing manager when run in diff mode too (#4952)
Integrations
- Upgraded PyPI upload workflow to use Trusted Publishing (#4611)
26.1a1
This is a preview of the Black stable style for 2026 (#4892). Please test it and
share your feedback!
The following formatting changes are included:
always_one_newline_after_import: Always force one blank line after import
statements, except when the line after the import is a comment or an import statement
(#4489)fix_fmt_skip_in_one_liners: Fix# fmt: skipbehavior on one-liner declarations,
such asdef foo(): return "mock" # fmt: skip, where previously the declaration would
have been incorrectly collapsed (#4800)fix_module_docstring_detection: Fix module docstrings being treated as normal
strings if preceded by comments (#4764)fix_type_expansion_split: Fix type expansions split in generic functions (#4777)multiline_string_handling: Make expressions involving multiline strings more compact
(#1879)normalize_cr_newlines: Add\rstyle newlines to the potential newlines to
normalize file newlines both from and to (#4710)remove_parens_around_except_types: Remove parentheses around multiple exception
types inexceptandexcept*withoutas. See PEP 758 for details (#4720)remove_parens_from_assignment_lhs: Remove unnecessary parentheses from the left-hand
side of assignments while preserving magic trailing commas and intentional multiline
formatting (#4865)standardize_type_comments: Format type comments which have zero or more spaces
between#andtype:or betweentype:and value to# type: (value)(#4645)- Regenerated the
_width_table.pyand added tests for the Khmer language (#4253)
25.12.0
Please test out the draft 2026 style in version 26.1a1! This style will be finalized in
the January release (26.1.0). Most of the changes in --preview will be in the 2026
stable style, but not all.
Please share your feedback!
This release (25.12.0) will still produce the 2025 style.
Highlights
- Black no longer supports running with Python 3.9 (#4842)
Stable style
- Fix bug where comments preceding
# fmt: off/# fmt: onblocks were incorrectly
removed, particularly affecting Jupytext's# %% [markdown]comments (#4845) - Fix crash when multiple
# fmt: skipcomments are used in a multi-part if-clause, on
string literals, or on dictionary entries with long lines (#4872) - Fix possible crash when
fmt:directives aren't on the top level (#4856)
Preview style
- Fix
fmt: skipskipping the line after instead of the line it's on (#4855) - Remove unnecessary parentheses from the left-hand side of assignments while preserving
magic trailing commas and intentional multiline formatting (#4865) - Fix
fix_fmt_skip_in_one_linerscrashing onwithstatements (#4853) - Fix
fix_fmt_skip_in_one_linerscrashing on annotated parameters (#4854) - Fix new lines being added after imports with
# fmt: skipon them (#4894)
Packaging
- Releases now include arm64 Windows binaries and wheels (#4814)
Integrations
- Add
output-fileinput to GitHub Actionpsf/blackto write formatter output to a
file for artifact capture and log cleanliness (#4824)
25.11.0
Highlights
- Enable base 3.14 support (#4804)
- Add support for the new Python 3.14 t-string syntax introduced by PEP 750 (#4805)
Stable style
- Fix bug where comments between
# fmt: offand# fmt: onwere reformatted (#4811) - Comments containing fmt directives now preserve their exact formatting instead of
being normalized (#4811)
Preview style
- Move
multiline_string_handlingfrom--unstableto--preview(#4760) - Fix bug where module docstrings would be treated as normal strings if preceded by
comments (#4764) - Fix bug where python 3.12 generics syntax split line happens weirdly (#4777)
- Standardize type comments to form
# type: <value>(#4645) - Fix
fix_fmt_skip_in_one_linerspreview feature to respect# fmt: skipfor compound
statements with semicolon-separated bodies (#4800)
Configuration
- Add
no_cacheoption to control caching behavior. (#4803)
Packaging
- Releases now include arm64 Linux binaries (#4773)
Output
- Write unchanged content to stdout when excluding formatting from stdin using pipes
(#4610)
Blackd
- Implemented BlackDClient. This simple python client allows to easily send formatting
requests to blackd (#4774)
Integrations
- Enable 3.14 base CI (#4804)
- Enhance GitHub Action
psf/blackto support therequired-versionmajor-version-only
"stability" format when using pyproject.toml (#4770) - Improve error message for vim plugin users. It now handles independently vim version
- Vim: Warn on unsupported Vim and Python versions independently (#4772)
- Vim: Print the import paths when importing black fails (#4675)
- Vim: Fix handling of virtualenvs that have a different Python version (#4675)