Skip to content

Fix unnecessary parentheses in indexed assignment RHS - #5095

Merged
cobaltt7 merged 16 commits into
psf:mainfrom
ParamChordiya:fix/unnecessary-parens-indexed-assignment
Jun 10, 2026
Merged

cobaltt7 merged 16 commits into
psf:mainfrom
ParamChordiya:fix/unnecessary-parens-indexed-assignment

Conversation

@ParamChordiya

Copy link
Copy Markdown
Contributor

Summary

Fixes #4349.

When an assignment target contains brackets (e.g. indexed access like x[key] = expr), Black would wrap the right-hand side expression in unnecessary parentheses when the line was too long.

Before:

dictionary_of_arrays["long_key_name_for_the_example"][
    very_long_index_name, index_zero
] = (10 - 5)

After:

dictionary_of_arrays["long_key_name_for_the_example"][
    very_long_index_name, index_zero
] = 10 - 5

Details

The root cause is in can_omit_invisible_parens() in src/black/lines.py. This function decides whether optional parentheses around the RHS can be safely removed. It returned False for simple expressions with operators (like 10 - 5) because they don't start or end with brackets β€” falling through to the default return False.

The fix adds a targeted check: when the RHS head ends with = and the LHS contains brackets (indicating an indexed/subscript assignment), and the body expression is short enough, allow omitting the optional parens. The downstream _prefer_split_rhs_oop_over_rhs() still makes the final decision on whether the resulting layout is better.

The line_length // 2 threshold ensures we only omit parens for genuinely short RHS expressions β€” long expressions that need their own line splitting still get wrapped as before.

Test plan

When an assignment target contains brackets (e.g. indexed access like
`x[key] = expr`), Black would wrap the right-hand side expression in
unnecessary parentheses when the line was too long. For example:

    dictionary["key"][idx] = (10 - 5)

This happened because `can_omit_invisible_parens` returned False for
simple expressions with operators (like `10 - 5`) that don't start or
end with brackets. The function was too conservative: it didn't consider
that the line could instead be split at the LHS brackets, producing:

    dictionary["key"][
        idx
    ] = 10 - 5

The fix allows omitting optional parens when the RHS body is short
enough and the LHS contains brackets that can absorb the split. The
downstream `_prefer_split_rhs_oop_over_rhs` still decides whether the
resulting layout is actually better.

Fixes psf#4349.
@github-actions

github-actions Bot commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (0ce601c) to main (e34bb1b):

--preview style: no changes

--stable style: no changes


What is this? | Workflow run | diff-shades documentation

…S.md

Move the fix for unnecessary parentheses in indexed assignment RHS
behind a Preview flag to avoid unintentional stable style changes.
Test cases moved to a preview-specific test file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread src/black/lines.py Resolved
Comment thread src/black/lines.py Resolved
ParamChordiya and others added 3 commits April 12, 2026 21:37
…ck, add docs

- Make `mode` parameter required in `can_omit_invisible_parens` to prevent
  accidental omission at call sites (fixes flake8 B008)
- Replace `str_width(str(line))` with `enumerate_with_length()` for an
  AST-only body length calculation (avoids expensive string conversion)
- Document `fix_unnecessary_parens_in_indexed_assignment` in
  `docs/the_black_code_style/future_style.md` (fixes test_feature_lists_are_up_to_date)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread src/black/lines.py Resolved
cobaltt7
cobaltt7 previously approved these changes Apr 24, 2026
@cobaltt7
cobaltt7 dismissed their stale review April 24, 2026 18:32

Didn't realize the changes to Black and in shades

@cobaltt7

cobaltt7 commented Apr 24, 2026 •

Copy link
Copy Markdown
Collaborator

Almost missed this:
There's a few side-effects here that don't look like an improvement to me
image
image
image

Black would previously prefer to wrap long lines in parentheses to get it under the line length:

# -l 80
middle.children[1].prefix = (
    middle.children[0].prefix + middle.children[1].prefix
)

With this change, Black always prefers to split lines instead of wrap them, which isn't an improvement

# -l 80
middle.children[1].prefix = middle.children[0].prefix + middle.children[
    1
].prefix

…r_rhs

When the OOP split lands mid-chain (tail starts with ] followed by more
tokens like .attr, and = is already in head), the split is inside a
subscript access on the RHS β€” not the LHS target. Guard against this
by returning False so paren-wrapping is preferred instead.

Adds regression tests for attribute-subscript chains on the RHS.
Black formats itself as part of CI (run_self). After the formatting
rule change in _prefer_split_rhs_oop_over_rhs, 4 files needed
reformatting to stay consistent with the updated formatter output.
The guard in _prefer_split_rhs_oop_over_rhs that prevents mid-chain
subscript splits on the RHS was running unconditionally, affecting
stable mode formatting across a large corpus (diff-shades failure).

Gate it behind Preview.fix_unnecessary_parens_in_indexed_assignment,
matching the flag used in can_omit_invisible_parens, so stable code
style is unchanged.
The unstable-mode self-format (eba22d0) broke test_piping which
formats __init__.py in stable mode and expects an idempotent result.
The two modes disagree on the ternary expression formatting, so the
files need to stay in the stable-mode-idempotent form from the
upstream merge (87d9925).
In linegen.py: tighten the subscript-chain guard to only suppress OOP
splitting when the token after `]` is a DOT (attribute access), not any
token. Without this, list literal assignments with magic trailing commas
were incorrectly paren-wrapped because the tail had `]` + NEWLINE (count=2).

In lines.py: fix can_omit_invisible_parens to check only visible brackets
in the LHS when deciding whether to allow the indexed-assignment OOP path.
Invisible bracket tokens (empty LPAR/RPAR from tuple assignments like
`a, b = expr`) were matching the condition, causing tuple-target assignments
to incorrectly omit optional parens and produce unstable formatting.
@ParamChordiya

ParamChordiya commented Apr 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Hey @cobaltt7 , thanks for catching that, really appreciate the thorough review. You were right, and it took a fair bit of digging to get to the bottom of it.

What was happening

The regression you spotted (middle.children[1].prefix = ... splitting mid-chain at the subscript instead of paren-wrapping the whole RHS) was happening because _prefer_split_rhs_oop_over_rhs in linegen.py was choosing the OOP (omit-optional-parens) split path even when the result was ugly. The OOP split would break at middle.children[ and put the index 1 on its own line, which is clearly wrong for a chained attribute access.
To fix it, I added a guard in _prefer_split_rhs_oop_over_rhs that says: if the OOP tail starts with ] followed by a . (meaning we'd be splitting inside a subscript access chain like obj[idx].attr), and the = is in the head (so the split is on the RHS, not the LHS), prefer paren-wrapping instead.

The first round of issues
After gating this guard behind Preview.fix_unnecessary_parens_in_indexed_assignment (to avoid affecting stable mode), the diff-shades corpus check was clean, but "Format ourselves" started failing on lint and the uvloop platforms. Turned out the guard was slightly too broad the condition len(rhs_oop.tail.leaves) > 1 was firing for list literal assignments like expected: list[Path] = [items] because the tail had ] plus a NEWLINE token (count=2, not just ] alone). That caused those list assignments to get incorrectly paren-wrapped. Fixed by tightening to rhs_oop.tail.leaves[1].type == token.DOT only suppress OOP when the character immediately after ] is actually a dot (attribute access).

The second bug, the trickier one
Even after that, "Format ourselves" was still failing because init.py kept getting reformatted. Traced it down to a separate bug in can_omit_invisible_parens in lines.py. The indexed-assignment early-return block checks whether there are brackets in the LHS: any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-2])
The problem: Python's AST wraps multi-target assignments like root, method = expr with invisible parentheses β€” these show up as empty-value LPAR/RPAR tokens in the leaf list. Those invisible tokens were matching the bracket condition and causing the indexed-assignment OOP path to fire for completely unrelated tuple assignments. That led to the outer parens being removed from the ternary in init.py, which is stable-idempotent but not unstable-idempotent, creating a formatting loop.

The fix was a one-character addition: and leaf.value, only count a bracket if it has an actual visible value, which correctly excludes the implicit/invisible tokens that the parser inserts.

How we verified

  • All 211 format tests pass, including the prefer_rhs_split regression cases added in the original PR
  • test_piping passes (reads init.py in stable mode and expects idempotency)
  • python -m black --check src/ tests/ with the project's own unstable = true config reports 57 files unchanged
  • init.py is idempotent under both stable and unstable mode now

The net change across both files is two lines, but it took some real archaeology to get there, the invisible-bracket issue in particular was subtle since nothing in the usual formatting output hinted that those tokens even existed.

@cobaltt7

Copy link
Copy Markdown
Collaborator

Please check the shades changes - it's still splitting within function calls.

@cobaltt7

cobaltt7 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Hey @ParamChordiya, are you still updating this PR?

@ParamChordiya

Copy link
Copy Markdown
Contributor Author

Hey @ParamChordiya, are you still updating this PR?

Hey @cobaltt7 sorry i got caught up with something else and lost track of this PR. let me circle back soon.

ParamChordiya and others added 3 commits June 9, 2026 19:39
…ens-indexed-assignment

# Conflicts:
#	CHANGES.md
#	docs/the_black_code_style/future_style.md
#	src/black/resources/black.schema.json
The previous heuristic in can_omit_invisible_parens was too broad and
caused 758 unwanted formatting changes across the diff-shades corpus:

- It allowed omitting optional parens whenever the RHS contained any
  bracket, even when the RHS did not fit on one line, producing
  mid-expression splits inside function calls and ternaries.
- Its LHS check matched any bracket before the '=', so it fired for
  parenthesized tuple targets like '(x,) = ...' and annotated
  assignments like 'x: list[str] = ...', which are not indexed
  assignments.

The path now requires: a visible ']' immediately before the '=' (a
genuine subscripted target), no annotation, a head too long to fit on
one line (so the subscript split is forced regardless), and an RHS that
fits on the '] = ' tail line. This preserves the intended psf#4349 fix
while leaving every other assignment shape untouched.

With the narrowed check, bad candidates no longer reach the
rhs-oop-vs-rhs comparison, so the subscript-chain guard added to
_prefer_split_rhs_oop_over_rhs is no longer needed and is removed.

Verified: all format tests pass; every regression hunk from the
diff-shades report now formats unchanged; black's own sources are
stable and idempotent under stable, preview, and unstable modes.
@ParamChordiya

Copy link
Copy Markdown
Contributor Author

Hey @cobaltt7, back on this β€” thanks for your patience. You were right that the earlier approach was still too aggressive, so I reworked the heuristic instead of patching around it.

What was causing the bad shades changes

The check in can_omit_invisible_parens had an escape hatch that allowed omitting the parens whenever the RHS contained any bracket, even when the RHS didn't fit on one line. The splitter would then break at the last bracket inside the expression β€” which is exactly the "splitting within function calls" you flagged (ternaries like kwargs["empty_label"] = kwargs.get(...) if ... else None, and method chains like _get_totp(...).generate(...).decode()). It was also matching things that aren't indexed assignments at all: parenthesized tuple targets ((self.next,) = ...) and annotated assignments (self.args: list[str] = ...), because it only looked for "any bracket before the =".

The new approach

The paren-omission path now only fires when all of these hold:

  • the target genuinely ends in a subscript β€” a visible ] immediately before the =
  • it isn't an annotated assignment (so the ] of list[str] doesn't count)
  • the LHS is too long to fit on one line, so Black is forced to split at the subscript brackets regardless
  • the RHS fits on the resulting ] = expr tail line

In other words, it only removes the parens in the exact #4349 situation: the line has to split at the LHS anyway, and the parens on the tail are pure noise. When the LHS fits, paren-wrapping the RHS stays the preferred style, untouched. With the bad candidates eliminated at the source, the subscript-chain guard I'd added to _prefer_split_rhs_oop_over_rhs became dead code, so it's removed β€” the diff is smaller than before.

How I verified

  • I reconstructed every hunk from the last diff-shades report (all 88 hunks / 758 changes across the 9 projects) and ran the new build over them: zero changes remain, so the only diff-shades delta left should be the intended Unnecessary parentheses added to expression in indexed assignmentΒ #4349 shape.
  • All format tests pass, and I added regression cases for each failure mode you surfaced (ternary RHS, method chains, tuple targets, annotated assignments, and a too-long RHS that should still wrap).
  • Black's own sources stay clean and idempotent under stable, preview, and unstable modes.
  • A generated sweep of ~1000 assignment variants (slices, augmented/chained/annotated targets, walrus, lambdas, comments, wide chars) shows no stable-vs-preview differences outside the intended improvement, and no idempotency issues.

Also merged main and resolved the conflicts, so the branch is mergeable again. Would appreciate another look when you get a chance!

@cobaltt7

Copy link
Copy Markdown
Collaborator

Thanks!

@cobaltt7
cobaltt7 merged commit 00cd1e4 into psf:main Jun 10, 2026
56 checks passed
luketainton pushed a commit to luketainton/luke_instant-msg-api that referenced this pull request Oct 5, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [black](https://github.com/psf/black) ([changelog](https://github.com/psf/black/blob/main/CHANGES.md)) | `<26.5.2,>=26.5.1` β†’ `<26.10.1,>=26.10.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/black/26.10.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/black/26.5.1/26.10.0?slim=true) |

---

### Release Notes

<details>
<summary>psf/black (black)</summary>

### [`v26.10.0`](https://github.com/psf/black/blob/HEAD/CHANGES.md#Version-26100)

[Compare Source](psf/black@26.5.1...26.10.0)

##### Stable style

- `--line-ranges` no longer inserts an empty line after a docstring when the range
  covers only the docstring itself ([#&#8203;5312](psf/black#5312))
- Fix `# fmt: skip` on a bracketed ternary turning the surrounding tuple into a call
  ([#&#8203;5464](psf/black#5464))
- Fix crash when `# fmt: skip` is placed on a one-line function or class with PEP 695
  type parameters ([#&#8203;5429](psf/black#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
  ([#&#8203;5395](psf/black#5395))
- Fix unparseable output when `# fmt: skip` is placed on a bracket of an `if`, `while`,
  `for`, or `with` header ([#&#8203;5401](psf/black#5401), [#&#8203;5405](psf/black#5405))
- Fix crash when formatting parenthesized expressions with multiple inline comments and
  `# fmt: skip` ([#&#8203;5414](psf/black#5414))
- Fix parsing Jupyter notebook assignment magics when non-ASCII characters appear
  earlier on the line ([#&#8203;5381](psf/black#5381))
- Preserve blank lines that come immediately before a `# fmt: on` comment ([#&#8203;5300](psf/black#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__`
  ([#&#8203;5321](psf/black#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) ([#&#8203;5287](psf/black#5287))
- Fix unparseable output for a t-string whose replacement field contains a quote (e.g.
  `t'\'{a["b"]}\''`) ([#&#8203;5265](psf/black#5265))
- Fix unparseable output for a triple-quoted string whose body ends in an
  already-escaped double quote (e.g. `'''\'''\"'''`) ([#&#8203;5262](psf/black#5262))
- Fix `--skip-magic-trailing-comma` dropping the trailing comma from a split
  single-element tuple used as a lambda parameter default ([#&#8203;5246](psf/black#5246))
- Fix unstable formatting when an inline comment sits on optional parentheses (e.g. a
  parenthesized assert message) ([#&#8203;5241](psf/black#5241))
- Fix `--skip-magic-trailing-comma` dropping the trailing comma of a one-element
  subscript (`a[x,]`) when the line is long enough to be split and contains a power
  operator ([#&#8203;5272](psf/black#5272))
- Fix crash when a standalone comment sits between tokens of a comprehension or lambda
  ([#&#8203;5144](psf/black#5144))
- Fix inline comments on a bracket inside a comprehension being dropped ([#&#8203;5330](psf/black#5330))
- Respect the magic trailing comma in a PEP 695 type parameter list containing a
  `*TypeVarTuple` or `**ParamSpec` ([#&#8203;5244](psf/black#5244))
- Fix crash when a comment-only `# fmt: off`/`# fmt: on` block is followed by a `with`
  statement after another standalone comment ([#&#8203;5189](psf/black#5189))
- Fix a crash when splitting `case case if ...` match patterns at very small line
  lengths ([#&#8203;5147](psf/black#5147))
- Fix multiline docstring indentation when leading tabs are used inside indented
  docstrings ([#&#8203;5148](psf/black#5148))
- Respect `# fmt: skip` on a line that opens a bracket (e.g.
  `from x import (  # fmt: skip`) when a standalone comment is among the bracket's
  contents ([#&#8203;5161](psf/black#5161))
- Fix an AST safety error when separate `# type: ignore` comments in a parenthesized
  attribute chain were merged onto one physical line ([#&#8203;5297](psf/black#5297))
- Preserve comments and blank lines outside requested ranges when formatting with
  `--line-ranges` ([#&#8203;5175](psf/black#5175))
- Fix crash when `# fmt: skip` is used on one-line `async def`, `async with`, and
  `async for` statements containing a semicolon ([#&#8203;5311](psf/black#5311))
- Stop converting form feeds or other similar characters in docstrings into newlines
  ([#&#8203;5461](psf/black#5461))
- Fix `--skip-source-first-line` turning the skipped line's CRLF ending into `\r\r\n`
  when reformatting a file with CRLF line endings ([#&#8203;5438](psf/black#5438))

##### Preview style

##### New preview features

- Add missing blank lines after classes whose last method has an ellipsis body ([#&#8203;5439](psf/black#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)`) ([#&#8203;5448](psf/black#5448))
- Keep dictionary keys containing operators together on one line when the value can be
  wrapped onto a new line instead ([#&#8203;5435](psf/black#5435))
- Remove redundant parentheses around individual variables in unpacking targets (e.g.
  `for (x), (y) in points:` becomes `for x, y in points:`) ([#&#8203;5416](psf/black#5416))
- Normalize uppercase `T` prefixes on t-strings to lowercase ([#&#8203;5425](psf/black#5425))
- Remove redundant parentheses around generator expressions ([#&#8203;5304](psf/black#5304), [#&#8203;5369](psf/black#5369))
- Preserve two blank lines before a top-level class starting inside a `# fmt: off` block
  after an import ([#&#8203;5238](psf/black#5238))
- Fix unnecessary parentheses around short right-hand expressions in indexed assignments
  (e.g. `x[key] = expr`) ([#&#8203;5095](psf/black#5095))
- Parenthesize tuple expressions in `yield` statements for consistency with function
  calls and returns ([#&#8203;5170](psf/black#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
  ([#&#8203;5135](psf/black#5135))
- In `.pyi` stub files, enforce a blank line after a function or method that has a
  docstring-only body when another comment or statement follows it ([#&#8203;5158](psf/black#5158))

##### Updates to existing preview features

- Fix crash in stub files when `# fmt: skip` is placed on a function in a group of
  same-name decorated functions (e.g. `@overload`s or a property setter) ([#&#8203;5430](psf/black#5430))
- Keep the parentheses around a lambda used as the iterable of a comprehension (e.g.
  `[x for x in (lambda: 0) if x]`) ([#&#8203;5176](psf/black#5176), [#&#8203;5200](psf/black#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 ([#&#8203;5466](psf/black#5466))
- Fix duplicated inline comment when stripping the parentheses around a string or
  merging a backslash-continued string on the same line ([#&#8203;5449](psf/black#5449))
- Split long stringified return annotations even when the function has parameters
  ([#&#8203;5427](psf/black#5427))
- Don't hug brackets when doing so would join two `# type: ignore` comments onto one
  line ([#&#8203;5271](psf/black#5271))
- Fix a crash when `# type: ignore` is lost during formatting of a long parenthesized
  string ([#&#8203;5329](psf/black#5329))
- Fix only the first part of an implicitly concatenated unmergeable string (e.g.
  `r"..." r"..."`) being wrapped in parentheses ([#&#8203;5434](psf/black#5434))

##### Configuration

- Add support for `NO_COLOR` environment variable to disable ANSI output ([#&#8203;5129](psf/black#5129))
- Remove spurious target version warning when runtime version is included in a
  `--target-version` flag ([#&#8203;5167](psf/black#5167))
- Fix `--force-exclude` not excluding files whose path contains `..` (e.g.
  `black ../generated/file.py` run from a subdirectory) ([#&#8203;5471](psf/black#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 ([#&#8203;5386](psf/black#5386))
- Fix loading a stale cached `pyproject.toml` path when `--code` is used from different
  working directories in the same process ([#&#8203;5152](psf/black#5152))
- Add validation for `--line-ranges` values ([#&#8203;5107](psf/black#5107))
- Ignore empty cache files instead of raising an `EOFError` ([#&#8203;5192](psf/black#5192))
- Reject non-string `include` and `force-exclude` values in `pyproject.toml` ([#&#8203;5193](psf/black#5193))
- Validate `BLACK_NUM_WORKERS` values and report invalid values as usage errors instead
  of crashing ([#&#8203;5211](psf/black#5211))
- Ignore permission errors when reading cache ([#&#8203;5258](psf/black#5258))

##### Packaging

- Reduce the size of Linux standalone binaries by stripping debug symbols during the
  PyInstaller release build ([#&#8203;5223](psf/black#5223))
- Black is now released using GitHub
  [Immutable Releases](https://docs.github.com/code-security/concepts/supply-chain-security/immutable-releases)
  ([#&#8203;5296](psf/black#5296))

##### Performance

- Fix superlinear runtime growth with the number of input files ([#&#8203;5450](psf/black#5450))
- Improve performance on strings containing many consecutive backslashes ([#&#8203;5163](psf/black#5163))
- Improve performance on files with many `# fmt: skip`/`# fmt: off`/`# fmt: on` comments
  ([#&#8203;5169](psf/black#5169), [#&#8203;5190](psf/black#5190), [#&#8203;5232](psf/black#5232))
- Improve performance on long calls and collections ([#&#8203;5177](psf/black#5177))
- Improve performance on multiline strings inside large collections (e.g. a dict literal
  with triple-quoted strings as values) ([#&#8203;5188](psf/black#5188))
- Improve performance on files with many soft keywords (e.g. `match`/`case` blocks)
  ([#&#8203;5186](psf/black#5186))
- Improve performance on long semicolon-separated statements (in the stable style) and
  large dict literals (in `--preview`) ([#&#8203;5184](psf/black#5184))
- Improve performance of `--line-ranges` on files with many sibling blocks (a long
  `if`/`elif` chain, a `match` with many cases, or many top-level definitions) ([#&#8203;5213](psf/black#5213))
- Improve performance on deeply nested bracketed expressions ([#&#8203;5171](psf/black#5171), [#&#8203;5242](psf/black#5242))
- Improve performance on lists and subscripts holding one long expression without
  operators (e.g. a long run of implicitly concatenated strings inside `[]`) ([#&#8203;5239](psf/black#5239))
- Improve performance on deeply chained operations (e.g. a long `a ** b ** c ** ...`
  chain) ([#&#8203;5235](psf/black#5235))
- Improve performance on long `if`/`elif` chains and other compound statements with many
  clauses ([#&#8203;5322](psf/black#5322))
- Improve performance of `string_processing`:
  - when merging implicitly concatenated f-strings containing long string literals
    ([#&#8203;5165](psf/black#5165))
  - when merging long implicitly concatenated strings ([#&#8203;5173](psf/black#5173), [#&#8203;5194](psf/black#5194))
  - when rewriting large nodes (e.g. `"%s ..." % (a, b, c, ...)`) ([#&#8203;5178](psf/black#5178), [#&#8203;5199](psf/black#5199), [#&#8203;5220](psf/black#5220))
  - when splitting long string literals ([#&#8203;5183](psf/black#5183))

##### Output

- Report parser failures using editor-friendly `path:line:column` locations ([#&#8203;5237](psf/black#5237))
- Fix crash when writing formatted code or diffs to a `sys.stdout` that has no `buffer`
  attribute (e.g. in Jupyter notebooks) ([#&#8203;5411](psf/black#5411))
- Report parse failures on Black's own output as internal errors ([#&#8203;5383](psf/black#5383))

##### *Blackd*

- Return HTTP 400 instead of 500 when the `X-Python-Variant` header is empty or has an
  empty entry (e.g. a trailing comma) ([#&#8203;5428](psf/black#5428))
- Allow optional whitespace around comma-separated versions and `pyi` in the
  `X-Python-Variant` header ([#&#8203;5441](psf/black#5441))

##### Integrations

- Remove unused `migrate-black` script ([#&#8203;5319](psf/black#5319))
- Support PEP 440 version specifiers in `tool.black.required-version` for the GitHub
  Action ([#&#8203;5399](psf/black#5399))
- Add outputs (`is_formatted`, `change_count`, `same_count`, `failure_count`) to GitHub
  Action runs ([#&#8203;5408](psf/black#5408))

##### Documentation

- Document `vim-python-pep8-indent`, which provides an `indentexpr` for Black-style
  insert-mode indentation ([#&#8203;5288](psf/black#5288))

</details>

---

### Configuration

πŸ“… **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

β™» **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

πŸ”• **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMzIuNSIsInVwZGF0ZWRJblZlciI6IjQ0LjEzMi41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL2RlcGVuZGVuY2llcyJdfQ==-->

Reviewed-on: https://git.tainton.uk/luke/instant-msg-api/pulls/290
Co-authored-by: renovate[bot] <renovate-bot@git.tainton.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unnecessary parentheses added to expression in indexed assignment

2 participants